设计模式之代理模式
概述
代理模式通过引入一个代理对象来控制对原始对象的访问。代理可以在原始对象的方法调用前后添加额外的处理逻辑,例如权限控制、日志记录、事务管理等,而无需修改原始对象的代码。这是一种实现了 AOP (面向切面编程) 的轻量级方式。
代理模式与 Spring 的 AOP (面向切面编程) 思想非常相似,都可以在不修改原有代码和业务流程的情况下,动态地切入新代码,增加新功能。
应用场景
代理模式常用于以下场景:
- Spring AOP:实现横切关注点的统一处理。
- 日志打印:在方法调用前后记录日志。
- 异常处理:统一处理方法调用中的异常。
- 事务控制:在方法调用前后开启和关闭事务。
- 权限控制:控制对敏感资源的访问。
代理的分类
代理模式主要分为以下三种类型:
- 静态代理(Static Proxy): 程序员手动创建或通过工具生成代理类的源代码,并在程序运行前完成编译。代理类和委托类的关系在编译前就已确定。
- 动态代理(Dynamic Proxy): 在运行时动态生成代理类,通常使用反射机制实现。JDK 动态代理要求委托类必须实现接口。
- CGLIB 动态代理(CGLIB Dynamic Proxy): 使用 CGLIB 字节码操作库在运行时动态生成代理类,可以代理没有实现接口的类。但 CGLIB 不能代理
final 类。
代码示例
1. 静态代理
概念
静态代理是指在程序运行前,代理类的源代码已经存在,并且代理类和委托类的关系在编译前就已确定。
优点
缺点
- 每个需要代理的对象都需要编写相应的代理类,代码冗余。
示例
需求: 在不修改 UserDao 类的情况下,为其 save 方法添加事务控制。
委托类
1 2 3 4 5
| public class UserDao { public void save() { System.out.println("保存数据方法"); } }
|
代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class UserDaoProxy extends UserDao { private UserDao userDao;
public UserDaoProxy(UserDao userDao) { this.userDao = userDao; }
@Override public void save() { System.out.println("开启事物..."); userDao.save(); System.out.println("关闭事物..."); } }
|
测试类
1 2 3 4 5 6 7
| public class Test { public static void main(String[] args) { UserDao userDao = new UserDao(); UserDaoProxy userDaoProxy = new UserDaoProxy(userDao); userDaoProxy.save(); } }
|
2. 动态代理(JDK 动态代理)
概念
动态代理是指在程序运行时动态生成代理类,代理类不是预先存在的。JDK 动态代理使用反射机制实现,要求委托类必须实现接口。
优点
- 无需手动编写代理类,可以在运行时动态指定代理对象,减少代码冗余。
缺点
示例
接口
1 2 3
| public interface UserDao { void save(); }
|
接口实现类
1 2 3 4 5 6
| public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("保存数据方法"); } }
|
InvocationHandler 实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class InvocationHandlerImpl implements InvocationHandler { private Object target;
public InvocationHandlerImpl(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用开始处理"); Object result = method.invoke(target, args); System.out.println("调用结束处理"); return result; } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.lang.reflect.Proxy;
public class Test { public static void main(String[] args) { UserDao userDaoImpl = new UserDaoImpl(); InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDaoImpl); ClassLoader loader = userDaoImpl.getClass().getClassLoader(); Class<?>[] interfaces = userDaoImpl.getClass().getInterfaces(); UserDao newProxyInstance = (UserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl); newProxyInstance.save(); } }
|
3. CGLIB 动态代理
概念
CGLIB (Code Generation Library) 动态代理利用 ASM 开源包,对代理对象类的 Class 文件加载进来,通过修改其字节码生成子类来处理。
优点
缺点
- 不能代理
final 类。
- 需要导入 CGLIB 相关的 jar 包。
示例
接口
1 2 3
| public interface UserDao { void save(); }
|
接口实现类
1 2 3 4 5 6
| public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("保存数据方法"); } }
|
CGLIB 动态代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor { private Object targetObject;
public Object getInstance(Object target) { this.targetObject = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); }
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("开启事物"); Object result = proxy.invoke(targetObject, args); System.out.println("关闭事物"); return result; } }
|
测试类
1 2 3 4 5 6 7
| public class Test { public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDaoImpl()); userDao.save(); } }
|