美文网首页程序员
Java中的两种动态代理

Java中的两种动态代理

作者: fanyank | 来源:发表于2018-02-12 22:27 被阅读28次

    前言

    Java语言本身是具有动态性的,在我们平时使用中,存在两种动态代理,分别是出自官方之手的JDK动态代理,一种是出自民间的CGLib(Code Generation Library)。

    JDK动态代理

    JDK动态代理依赖接口,所以我们首先创建一个接口类 Hello

    public interface Hello {
      public void sayHello();
    }
    

    接下来就是一个接口实现类 HelloImpl

    public class HelloImpl implements Hello {
      public void sayHello() {
        System.out.println("HelloImpl is invoking sayHello() method...");
      }
    }
    

    至此,我们准备工作已经做好了,接下来看看我们还需要做哪些工作

    我们需要创建一个类,这个类的作用是给我们动态创建出来的代理类调用的,暂且命名为 DynamicProxy 吧。该类需要继承自 InvocationHandler 接口,并实现 invoke() 方法。

    public class DynamicProxy implements InvocationHandler {
        //委托对象实例
        private Object target;
    
        public DynamicProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if(method.getName().equals("sayHello")) {
                before();
                Object result = method.invoke(target,args);
                after();
                return result;
            }
            return null;
        }
    
        public void before() {
            System.out.println("before invoke sayHello() method");
        }
    
        public void after() {
            System.out.println("after invoke sayHello() method");
        }
    }
    

    可以看到在 invoke() 方法中我们在方法执行前后添加了自定义的行为,我们称之为方法拦截。接下来就是书写 main() 方法了。

    public class ProxyTest {
        public static void main(String[] args) {
            //创建实现接口的对象
            Hello helloImpl = new HelloImpl();
            //该类的作用就是对方法进行拦截
            DynamicProxy dynamicProxy = new DynamicProxy(helloImpl);
            //在运行过程中动态创建代理类
            Hello helloProxy = (Hello) Proxy.newProxyInstance(helloImpl.getClass().getClassLoader(),
                    helloImpl.getClass().getInterfaces(),
                    dynamicProxy);
            //通过代理类调用方法
            helloProxy.sayHello();
        }
    }
    

    具体的各类之间关系我们还是来看UML图吧。

    JDK动态代理

    如上图,我们可以看到,通过JDK生成的动态代理类$HelloProxy100与委托类(实现Hello接口的HelloImpl类)是兄弟关系,当要执行动态代理类$HelloProxy100中的任一方法时,都会去调用DynamicProxy类中的invoke()方法,这样我们就对方法实现了拦截。

    JDK动态代理优化

    我们在 DynamicProxy 类中添加一个获取代理类的方法,以后每次通过这个 DynamicProxy 中间类来获取代理类。

    @SuppressWarnings("unchecked")
       public <T> T getProxy() {
           return (T) Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
                   this.target.getClass().getInterfaces(),
                   this);
       }
    

    这样我们的 main() 方法中的代码量就大大减少了。

    public class ProxyTest {
        public static void main(String[] args) {
            DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
            Hello helloProxy = dynamicProxy.getProxy();
            helloProxy.sayHello();
        }
    }
    

    CGLib动态代理

    与JDK动态代理相比,CGLib动态代理是不需要接口的,这里就创建一个普通类 Hi

    public class Hi {
        public void sayHi() {
            System.out.println("Hi,my friend");
        }
    }
    

    接着,创建出一个与 DynamicProxy 作用(给动态创建出的代理类调用)相似的类,创建一个叫做 CGLibProxy 的类,该类必须实现 MethodInterceptor 接口,并实现 interceptor() 方法。

    public class CGLibProxy implements MethodInterceptor {
    
        public <T> T getProxy(Class<?> cls) {
            return (T) Enhancer.create(cls,this);
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            before();
            //执行委托类中的原方法
            Object result = methodProxy.invokeSuper(o,objects);
            after();
            return result;
        }
    
        public void before() {
            System.out.println("before method");
        }
    
        public void after() {
            System.out.println("after method");
        }
    }
    

    最后书写 main() 方法。

    public class ProxyTest {
        public static void main(String[] args) {
            CGLibProxy cgLibProxy = new CGLibProxy();
            Hi hiProxy = cgLibProxy.getProxy(Hi.class);
            hiProxy.sayHi();
        }
    }
    
    

    各类关系我们还是使用UML来表示

    CGLib动态代理

    可以看出,生成的动态代理类 $HiProxy100 是委托类的子类,通过反编译工具查看 $HiProxy100 源代码可得知,该类重写了委托类的 sayHi() 方法,如果发现存在实现了 MethodInterceptor 接口的对象,那么就会通过这个对象调用 intercept() 方法对委托类中的每一个方法进行拦截(除了final方法,因为final方法不能被继承,所以动态代理类自然就没有这个方法)。

    相关文章

      网友评论

        本文标题:Java中的两种动态代理

        本文链接:https://www.haomeiwen.com/subject/mmvdtftx.html