ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## **模式的定义** 代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。 ## **静态代理** 静态代理就是需要手动编写代理类,再编译成源码,在代码里是真实存在的片段。 代码模拟简单的事物提交 1. 创建dao接口以及实现dao ``` public interface UserDao { void add(); } public class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("add"); } } ``` 2. 创建代理方法 ``` public class UserDaoProxy implements UserDao { private UserDao userDao; public UserDaoProxy(UserDao target) { this.userDao = target; } @Override public void add() { System.out.println("开始事物"); userDao.add(); System.out.println("提交事物"); } } ``` 3. 客户端条用 ``` public static void main(String[] args) { UserDao userDao = new UserDaoImpl(); UserDaoProxy proxy = new UserDaoProxy(userDao); proxy.add(); } ``` 很简单的实现了静态代理处理事物的功能,但是假设我们100个dao就意味着需要创建100个proxy,很显然代码非常的冗余,JDK给我们提供了一种更好的技术,叫做JDK动态代理。 ## **JDK动态代理** 动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类接口过多的问题。动态代理是实现方式,是通过反射来实现的,借助Java自带的`java.lang.reflect.Proxy`,通过固定的规则生成 其步骤如下: 1. 编写一个委托类的接口,即静态代理的(UserDao接口) 2. 实现一个真正的委托类,即静态代理的(UserDaoImpl类) 3. 创建一个动态代理类,实现`InvocationHandler`接口,并重写该`invoke`方法 4. 在测试类中,生成动态代理的对象。 步骤1和2和静态代理的写法一致 步骤3创建动态代理类: ``` public class TransHandler implements InvocationHandler { private Object target; public TransHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始事物"); Object invoke = method.invoke(target, args); System.out.println("提交事务"); return invoke; } } ``` 步骤4生成代理对象: ``` public static void main(String[] args) { // 代理对象 UserDao userDao = new UserDaoImpl(); TransHandler transHandler = new TransHandler(userDao); // 获取代理对象的类加载器 ClassLoader loader = userDao.getClass().getClassLoader(); // 获取代理对象的实现接口 Class<?>[] interfaces = userDao.getClass().getInterfaces(); UserDao userDaoImpl = (UserDao) Proxy.newProxyInstance(loader, interfaces, transHandler); userDaoImpl.add(); } ``` ## **CGLIB动态代理** cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。 使用CGLIB动态代理实现事物功能 1. maven中添加CGLIB依赖 ``` <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> ``` 2. 创建动态代理类 ``` public class CglibProxy implements MethodInterceptor { // 代理的对象 private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); // 指定cglib生成的子类继承的父类 enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) { System.out.println("开启事物"); Object invoke = proxy.invoke(target, args); System.out.println("提交事物"); return invoke; } } ``` 3. 客户端生成创建代理对象 ``` public static void main(String[] args) { UserDao userDao = new UserDaoImpl(); CglibProxy cglibProxy = new CglibProxy(); UserDao userDaoImpl = (UserDao) cglibProxy.getInstance(userDao); userDaoImpl.add(); } ``` cglib使用底层字节码技术,动态创建代理对象(UserDao)的子类,并重写父类的方法。 ## **JDK动态代理和CGLIB动态代理的区别** | JDK动态代理 | CGLIB动态代理 | | --- | --- | | 利用反射机制生成一个实现代理接口的匿名类 | 通过动态字节码技术创建虚拟子类实现 | | 代理对象类需要实现接口 | 无需实现接口 | Spring AOP采用哪种动态代理模式呢? 1. 代理对象实现了接口,默认情况下会采用JDK动态代理来实现AOP,例如Service层,Dao层等 2. 代理对象没实现接口,会采用cglib动态代理模式实现AOP,例如Controller层