代理模式介绍
代理的作用就是想增强一个目标类的方法,但是又不修改目标类,这样就可以用到代理。即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,达到扩展目标类方法的目的。
Java代理有三种方式:静态代理,动态代理,cglib代理。
静态代理
这种代理方式需要目标列和代理类实现一样的接口。
UserDao (接口)
1 | public interface UserDao { |
UserDaoImpl (目标类)
1 | public class UserDaoImpl implements UserDao { |
UserDaoProxy (代理类)
1 | public class UserDaoProxy implements UserDao { |
可以看见目标类和代理都实现了同一个接口.
最后执行的效果是:
动态代理
- 静态代理:是在程序编译以前就在代码中已经定义好的代理方式。也就就说Java编译以后的代理文件是一个class的文件
- 动态代理:代理是在程序运行是生成的。也就是Java编译完成后没有实际的class文件,而是在运行是动态生成的类字节码,并加载到JVM中。
Java实现动态代理的大致步骤如下:
1.定义一个目标类和公共接口
2.定义一个类,实现 InvocationHandler 接口。代理类调用任何方法都会经过这个调用处理器类。
3.生成代理对象,为代理对象指定(目标对象,目标对象实现的接口,调用处理器的实例)。由此看出一个代理对象对应一个目标对象,对应一个调用处理器实例。
Java实现动态代理涉及以下几个类
- Java.lang.reflect.Proxy:这是生成代理对象所要使用的类,通过Proxy类生成的代理类都继承了Proxy类。Proxy类主要的方法为:
1 | //创建代理对象 |
这个静态函数的第一个参数是类加载器对象,即哪个类加载器来加载这个代理类到JVM方法区(目标类的类加载器)。第二个参数是这个代理类所要实现的接口(一般为目标类实现的接口)。第三个参数是调用处理器的实例,用于指定代理类中具体实现了什么。
- Java.lang.reflect.InvocationHandler:称他为“调用处理器”,是一个接口,我们动态生成的代理类需要自己定义一个类实现InvocationHandler接口。
InvocationHandler接口中是方法:
1 | invoke (Object proxy, Method method, Object[] args); |
这个方法是在代理对象调用任何方法都会调用的。第一个参数是代理对象(标识哪个代理对象调用了方法)。第二个参数是Method(表示哪个方法被调用了)。第三个参数是调用方法的参数。
ProxyHandler(调用处理器)
1 | public class ProxyHandler implements InvocationHandler { |
运行结果:
由打印结果可以看出来,Proxy.newProxyInstance返回的是一个代理对象。该对象有以下几个特点:
- 继承Proxy类。
- public final
- 命名方式是$ProxyN,其中 N 会慢慢增加。
cglib代理
先查看cglib源码的目录结构:
图中圈出的 Enhancer 和 MethodInterceptor 这两个两个类是cglib的核心。cglib生成代理类的思想就是生成一个新的类(Proxy),生成的代理类继承目标类,并对目标类方法实现一些前后操作。代理类直接继承自目标类,目标类就不用实现接口。
ProxyFactory (生成代理的工厂类)实现 MethodInterceptor接口,也可以使用匿名内部类方法
1 | public class ProxyFactory implements MethodInterceptor { |
执行结果:
可以看到执行了两次目标类的方法。
总结
- 静态代理:就是代理类和目标类实现同一个接口,在代理类中调用目标类的方法,同时增强目标类的方法。这种实现代理的方式有很强的局限性。如果目标类的方法增加了,同样代理类中也要增加相应的方法来增强。
动态代理:相比于静态代理,动态代理解决了如果目标类增加新方法时,代理类也需要做出相应修改的问题。同时可以做到一个代理类可以代理不同的目标类。但是使用动态代理要求目标类需要实现接口,同时生成的代理类是继承自 Proxy。动态代理是不需要第三方库的支持。
cglib 代理:和动态代理最终实现的效果相似。但是生成的代理类是继承自目标类,同时不需要目标类实现接口。而 cglib 依赖于 cglib 类库。同时在 cglib 实现接口方法中提供了一个额外的参数, MethodProxy 代表着当前执行方法的代理,但是一般不会使用。