之前说了代理模式, 即为其他对象提供一种代理以控制对这个对象的访问, 详情见《简说设计模式 -- 代理模式》, 而代理模式常见的实现方式为静态代理及动态代理.
一, 静态代理
所谓静态代理类是指: 由程序员创建或由特定工具自动生成源代码, 再对其进行编译. 在程序运行之前, 代理类的. class 文件就已经存在了. UML 结构图如下:
这里以持久化层的数据交互类为例, IUserDao 是与数据库进行交互的接口, UserDao 为 IUserDao 接口的实现类, UserDaoProxy 为代理类, 二者皆实现了 IUserDao 接口. 说具体点就是, UserDao 类是 IUserDao 接口的具体实现者, 而 UserDaoProxy 类是通过调用 UserDao 类的相关方法来提供特定服务的.
1. IUserDao 接口
这里为抽象目标类 / 接口.
- public interface IUserDao {
- public void save();
- }
2. UserDao 接口
这里为具体目标类或被代理的对象. 实现了 IUserDao 中的接口方法.
- public class UserDao implements IUserDao {
- @Override
- public void save() {
- System.out.println("数据已保存!!!");
- }
- }
3. 静态代理类
首先在静态代理类中引入 IUserDao 接口, 通过调用 UserDao 类的相关方法来提供特定服务.
- public class UserDaoProxy implements IUserDao {
- private IUserDao iUserDao;
- public UserDaoProxy(IUserDao iUserDao) {
- this.iUserDao = iUserDao;
- }
- @Override
- public void save() {
- System.out.println("开始事务...");
- iUserDao.save(); // 执行目标对象
- System.out.println("提交事务...");
- }
- }
4. Client 客户端
- public class Client {
- public static void main(String[] args) {
- UserDao userDao = new UserDao();
- UserDaoProxy proxy = new UserDaoProxy(userDao);
- proxy.save();
- }
- }
运行结果如下:
总结一下就是, 在代理类中注入依赖, 即引入需要代理的实体类, 通过代理类来调用实体类中的方法来实现静态代理.
虽然静态代理可以很好的对目标对象进行功能扩展, 但对每一个服务都需要建立代理类, 工作量较大且不易管理, 而且如果接口发生改变的话, 代理类也得进行相应的修改, 这时动态代理的作用就显现出来了.
二, 动态代理
动态代理与静态代理的区别在于: 在程序运行时, 动态代理类是运用反射机制创建而成的. 在抽象工厂模式的最后有提到用反射来代理 switch 语句进行选择, 这里就运用到了类似的思想.
通过动态代理, 我们不再需要手动创建代理类, 只需编写一个动态处理器即可, 而真正的代理对象由 JDK 在运行时帮我们创建. 所以我们也将之称为 JDK 动态代理.
下面看一个例子, UML 结构图如下:
1. IBusiness 接口
被代理的接口.
- public interface IBusiness {
- public void doWork();
- }
2. Business 类
具体实现类 / 被代理的对象.
- public class Business implements IBusiness {
- @Override
- public void doWork() {
- System.out.println("进行业务逻辑处理");
- }
- }
3. BusinessHandler 类
BusinessHandler 类实现类 Invocation 接口, 它是方法调用接口, 声明了负责调用任意一个方法的 invoke()方法, 参数 proxy 指定动态代理类实例, 参数 method 指定被调用的方法, 参数 args 指定向被调用方法传递的参数, 而 invoke()方法的返回值表示被调用方法的返回值. 其中 method.invoke(iBusiness, args) 相当于 iBusiness.method(args) .
- public class BusinessHandler implements InvocationHandler {
- private IBusiness iBusiness;
- public BusinessHandler(IBusiness iBusiness) {
- this.iBusiness = iBusiness;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("before");
- method.invoke(iBusiness, args);
- System.out.println("after");
- return null;
- }
- }
4. Client 客户端
- public class Client {
- public static void main(String[] args) {
- Business business = new Business();
- // 生成代理类对象
- IBusiness proxy = (IBusiness) Proxy.newProxyInstance(
- business.getClass().getClassLoader(),
- business.getClass().getInterfaces(),
- new BusinessHandler(business));
- proxy.doWork();
- }
- }
此处通过 java.lang.reflect.Proxy 类的 newProxyInstance()方法来生成代理类对象, 它的完整定义如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException
参数 loader 指定动态代理类的类加载器, 参数 interfaces 指定动态代理类需要实现的所有接口, 参数 handler 指定与动态代理类相关联的 InvocationHandler 对象. 所以我们只需调用 newProxyInstance()方法就可以某一个对象的代理对象了.(有关 ClassLoader 类加载器的内容这里就不再赘述了, 它的作用是将 class 文件加载到 jvm 虚拟机中去).
运行结果如下:
相比于静态代理, 动态代理的优势还是很明显的, 不仅减少了对业务接口的依赖, 还降低了耦合度, 但它还是无法摆脱对接口的依赖. 那么对于没有接口的类应该如何实现动态代理呢?
三, CGLIB 代理
cglib 是一个强大的高性能代码生成包, 底层是通过使用一个小而快的字节码处理框架 ASM 来转换并生成新的类, 所以我们一般也称之为 cglib 字节码生成.
与 JDK 动态代理不同, cglib 是针对类来实现代理的, 所以对于没有接口的类我们可以通过 cglib 字节码生成来实现代理. 原理是对指定的业务类生成一个子类, 并覆盖其中的业务方法实现代理. 但因为采用的是继承, 所以不能对 final 修饰的类进行代理.
下面看一个使用 cglib 进行代理的实例, 需先导入相应的 jar 包(asm 及 cglib 包).
1. 被代理类
首先定义业务类, 无需实现接口.
- public class Hello {
- public void sayHello() {
- System.out.println("Hello World!");
- }
- }
2. 拦截器
定义一个拦截器, 通过实现 MethodInterceptor 接口的 intercept()方法来实现回调方法, 通过 invokeSuper()执行目标对象的方法.
- public class HelloMethodInterceptor implements MethodInterceptor {
- @Override
- public Object intercept(Object object, Method method , Object[] objects , MethodProxy methodProxy ) throws Throwable {
- System.out.println("before" + method.getName());
- methodProxy.invokeSuper(object, objects);
- System.out.println("after" + method.getName());
- return null;
- }
- }
3. Client 客户端
通过 Enhancer 加强类来创建动态代理类, 通过它的 setSuperclass()方法来指定要代理的业务类 (即为下方生成的代理类指定父类), 然后通过 create() 方法生成代理类对象.
- public class Client {
- public static void main(String[] args) {
- Enhancer enhancer = new Enhancer(); // 工具类
- enhancer.setSuperclass(Hello.class); // 继承被代理类
- enhancer.setCallback(new HelloMethodInterceptor()); // 设置回调
- Hello hello = (Hello) enhancer.create(); // 生成代理类对象
- hello.sayHello();
- }
- }
运行结果如下:
综上所述, cglib 采用的是动态创建子类的方法, 所以对 final 修饰的类不能进行代理. 以 Spring AOP 编程为例, JDK 动态代理及 cglib 代理的区别在于, 有接口的目标对象采用 JDK 代理, 无接口的目标对象采用 cglib 代理.
源码地址: https://gitee.com/adamjiangwh/GoF
来源: https://www.cnblogs.com/adamjwh/p/10907526.html