zoukankan      html  css  js  c++  java
  • Java动态代理

    代理模式是指给某个对象提供一个代理对象,用户不直接访问原对象而是通过代理对象间接访问。

    我们可以使用代理模式实现面向切面编程(AOP), 由动态代理将切面功能织入目标方法而不侵入调用方的业务代码。

    或者使用代理模式实现远程过程调用(RPC), 调用方像调用本地方法一样调用代理方法,而不必关心代理调用远程方法细节。

    JDK提供了基于反射机制的动态代理实现,而被广泛使用的第三方库CGLIB则基于字节码操作框架ASM实现动态代理。

    本文将简单介绍两种动态代理使用方法,做抛砖引玉之用。

    content:

    代理模式

    实现静态代理模式是非常简单的, 首先我们定义一个接口:

    public interface MyInterface {
    
        void foo();
    
    }
    

    编写被代理的对象:

    public class MyService implements MyInterface {
    
        @Override
        public void foo() {
            System.out.println("foo");
        }
    }
    

    在代理模式中被代理的类通常被称作委托类。

    编写静态代理:

    public class MyProxy implements MyInterface {
    
        private MyInterface subject;
    
        MyProxy(MyInterface subject) {
            this.subject = subject;
        }
    
        @Override
        public void foo() {
            long start = System.System.currentTimeMillis();
            subject.foo();
            long elapseTime = System.currentTimeMillis() - start;
            System.out.println("elapse time: " + elapseTime);
        }
    }
    

    静态代理是指代理类在编译时生成,与之相对动态代理则是运行时生成代理类的字节码并加载到JVM中。

    静态代理的问题在于编写代理类时必须了解接口细节, 即编写MyProxy时必须了解MyInterface的定义。

    以AOP框架为例,框架无法预先了解接口信息只能在运行时根据Class对象创建代理对象,因此编写此类框架必须要有动态代理机制的支持。

    java动态代理

    Java的反射机制可以在运行时创建类,Java标准库中提供了基于反射的代理机制。

    代理类应该实现java.lang.reflect.InvocationHandler接口,该接口只有一个方法: invoke

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
    

    当我们通过代理访问委托类方法时,会调用代理类的invoke方法。我们可以通过invoke方法的参数获得调用信息,并用反射机制调用为委托类的方法。

    invoke方法的三个参数为:

    • Object porxy: 被调用方法的动态代理实例
    • Method method: 被调用的方法
    • Object[] args: 调用时传入的参数

    java.lang.reflect.Proxy.newProxyInstance方法用于创建代理对象,它使用反射机制在运行时创建了代理类并加载到JVM中。该方法有三个参数:

    • ClassLoader loader: 加载代理类的加载器
    • Class<?>[] interfaces: 代理类要实现的方法
    • InvocationHandler h: 在调用代理类方法时,嵌入调用过程的InvocationHandler实例

    直接描述较难理解,我们来看代码:

    public class MyProxy implements InvocationHandler {
    
        private Object subject;
    
        public MyProxy(Object subject) {
            this.subject = subject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1
            return method.invoke(subject, args); 
        }
    
        @SuppressWarnings("unchecked")
        public static <T> T createProxy(T subject) {
            return (T)Proxy.newProxyInstance(subject.getClass().getClassLoader(), // 2
                    subject.getClass().getInterfaces(), new MyProxy(subject));
        }
    
        public static void main(String[] args) {
            MyInterface obj = createProxy(new MyService());  // 3
            obj.foo();
        }
    
    }
    

    MyInterfaceMyService和上文中完全相同:

    public interface MyInterface {
    
        void foo();
    
    }
    
    public class MyService implements MyInterface {
    
        @Override
        public void foo() {
            System.out.println("foo");
        }
    }
    

    MyProxy类实现了InvocationHandler接口,并提供了createProxy静态方法。

    main方法中我们为MyService对象创建了一个代理,并通过代理调用了foo方法。

    在代码中有几处细节值得了解:

    1. main方法中通过obj.getClass().getName()获得代理类的类名为:com.sun.proxy.$Proxy0。这个类名表示这个类是动态生成的Proxy类,0表示它是当前JVM中第一个动态代理类。

    2. 在注释2处我们通过newProxyInstance创建了代理类即上文中的$Proxy0类:

      1. loader为被代理类的加载器,也可以使用MyInterface.class.getClassLoader(),它们都是AppClassLoader实例。
      2. interfaces参数为subject.getClass().getInterfaces()表示动态代理类$Proxy0实现了subject的所有接口,但$Proxy0不是Subject类的子类。
        因此在main方法中使用MyInterface obj = createProxy(new MyService());而不能使用MyService obj = createProxy(new MyService());
      3. 我们使用MyProxy实例作为InvocationHandler拦截动态代理类所有方法调用。
    3. 在注释1处我们实现了invoke方法拦截动态代理对象所有方法调用:

      1. 传入的实参Object proxy是动态代理类的实例,即main方法中的obj, 它是$Proxy0类的实例。注意InvocationHandler实例不是动态代理实例,handler是编译生成的静态类。
      2. 传入的实参Method method是接口类的Method对象,即MyInterface.class.getMethod("foo")而非委托类MyService的Method对象
      3. method.invoke(subject, args)用反射的方式调用了被代理实例的方法,我们可以在invoke方法中添加其它代码以增强委托类的功能

    cglib动态代理

    CGLIB是一个强大的动态代理库,SpringAOP和dynaop框架使用CGLIB进行方法拦截和增强,Hibernate使用CGLIB进行代理关联,此外JMock也使用CGLIB提供Mock对象。

    在实现上,CGLIB使用高性能轻量级字节码操作框架ASM来动态生成代理类。值得一提的是,CGLIB比JDK动态代理还要快。

    CGLIB将动态代理类实现为委托类的子类,因此可以代理没有实现接口或对接口进行了扩展的类。因为子类无法覆盖final方法,因此只能代理非final方法。

    首先使用maven导入cglib依赖:

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.6</version>
    </dependency>
    

    编写CGLIB动态代理:

    public class CglibProxy {
    
        @SuppressWarnings("unchecked")
        public static <T> T createProxy(T subject) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(subject.getClass());
            enhancer.setCallback(new MyInterceptor());
            return (T) enhancer.create();
        }
    
        private static class MyInterceptor implements MethodInterceptor {
    
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                return proxy.invokeSuper(obj, args);
            }
        }
    
        public static void main(String[] args) {
            MyService obj = createProxy(new MyService());
            obj.foo();
        }
    
    }
    

    Enhancer用于对委托类进行增强,它可以拦截方法调用以实现代理机制。

    MethodInterceptor.intercept方法用于拦截方法调用,它的几个参数为:

    • Object obj: 动态代理对象
    • Method method: 被拦截的方法,本例main方法中被拦截的方法是: MyService.class.getMethod("foo")
    • Object[] args: 调用实参
    • MethodProxy proxy: 用来调用委托类方法的快捷代理,不是动态代理类。

    示例中定义的动态代理没有添加任何额外功能,这种特殊的Callback可以使用net.sf.cglib.proxy.NoOp代替:

        public static <T> T createProxy(T subject) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(subject.getClass());
            enhancer.setCallback(NoOp.INSTANCE);
            return (T) enhancer.create();
        }
    

    Enhancer可以使用CallbackFilter为每个方法调用配置过滤器:

    class MyService {
    
        void foo0() {
            System.out.println("0");
        }
    
        void foo1() {
            System.out.println("1");
        }
    
    }
    
    public class CglibProxy {
    
        @SuppressWarnings("unchecked")
        public static <T> T createProxy(T subject) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(subject.getClass());
    
            Callback callback0 = NoOp.INSTANCE;
            Callback callback1 = NoOp.INSTANCE;
            enhancer.setCallbacks(new Callback[]{callback0, callback1});
    
            enhancer.setCallbackFilter(new MyCallbackFilter());
            return (T) enhancer.create();
        }
    
        private static class MyCallbackFilter implements CallbackFilter {
    
            @Override
            public int accept(Method method) {
                if ("foo0".equals(method.getName())) {
                    return 0;
                } else {
                    return 1;
                }
            }
        }
    
        public static void main(String[] args) {
            MyService obj = createProxy(new MyService());
            obj.foo0();
            obj.foo1();
        }
    
    }
    

    当Enhancer设置了CallbackFilter之后,在拦截方法前会先调用CallbackFilter.accept()方法判断使用哪个Callback实例进行拦截。

    CallbackFilter.accept()返回值即为拦截器的下标,即若accept方法返回0,则调用enhancer.callbacks[0]进行拦截。

  • 相关阅读:
    228. Summary Ranges
    227. Basic Calculator II
    224. Basic Calculator
    222. Count Complete Tree Nodes
    223. Rectangle Area
    221. Maximal Square
    220. Contains Duplicate III
    219. Contains Duplicate II
    217. Contains Duplicate
    Java编程思想 4th 第4章 控制执行流程
  • 原文地址:https://www.cnblogs.com/Finley/p/8733984.html
Copyright © 2011-2022 走看看