模式简介
-
代理模式是指使用一个代理对象替代目标对象,用以在执行目标方法的外围完成功能的增强
-
代理模式的角色分为目标对象和代理对象
-
代理模式有静态代理、动态代理两种
静态代理:显式声明一类对象的代理
动态代理:动态生成一类对象的代理
模式实现
示例:房东要出租房屋,由中介代理出租
静态代理
-
出租人接口、房东类
// 出租人接口 public interface RentOuter { void rentOut(); } // 房东类 public class Lessor implements RentOuter { @Override public void rentOut() { System.out.println("房东出租房屋"); } }
-
静态代理类
public class StaticProxyClient implements RentOuter { private final RentOuter rentOuter; public StaticProxyClient(RentOuter rentOuter) { this.rentOuter = rentOuter; } @Override public void rentOut() { seeHorse(); signContract(); takeMoney(); rentOuter.rentOut(); } private void seeHouse() { System.out.println("中介带看房"); } private void signContract() { System.out.println("中介签合同"); } private void takeMoney() { System.out.println("中介收费用"); } }
-
测试
public class ProxyPatternTest { @Test public void testStatic() { StaticProxyClient client = new StaticProxyClient(new Lessor()); client.rentOut(); } }
输出
中介带看房 中介签合同 中介收费用 房东出租房屋
JDK 动态代理
-
JDK 动态代理类
public class DynamicProxyClient implements InvocationHandler { private final RentOuter rentOuter; public DynamicProxyClient(RentOuter rentOuter) { this.rentOuter = rentOuter; } public RentOuter getInstance() { Class<?> clazz = this.rentOuter.getClass(); return (RentOuter) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } /** * 代理方法 * * @param proxy 代理对象 * @param method 被代理对象的方法 * @param args 方法参数 * @return 方法返回值 * @throws Throwable 抛出异常 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); signContract(); takeMoney(); // 传入维护的 rentOuter(注意不是 proxy) return method.invoke(this.rentOuter, args); } // 省略方法 seeHouse 等 }
-
测试
public class ProxyPatternTest { @Test public void testDynamic() { DynamicProxyClient client = new DynamicProxyClient(new Lessor()); RentOuter instance = client.getInstance(); instance.rentOut(); } }
-
JDK 动态代理流程
- 实现 InvocationHandler 接口,invoke 方法接收 Method 参数,在调用前后加入增强逻辑
- Proxy 类 newProxyInstance 方法,接收被代理类的所有接口和 InvocationHandler
- newProxyInstance 方法会创建一个代理类,实现了被代理对象的所有接口
- 动态生成代理类的 Java 代码,根据 InvocationHandler 加入增强逻辑
CGLIB动态代理
-
CGLIB动态代理类
public class CglibProxyClient implements MethodInterceptor { public RentOuter getInstance(Class<? extends RentOuter> clazz) { // 提供方法增强功能的类 Enhancer enhancer = new Enhancer(); // 设置动态生成代理的父类 enhancer.setSuperclass(clazz); // 设置 MethodInterceptor enhancer.setCallback(this); // 创建代理对象 return (RentOuter) enhancer.create(); } /** * 代理方法 * * @param o 代理对象 * @param method 被代理对象的方法 * @param args 方法参数 * @param methodProxy 方法的代理(用于执行父类方法) * @return 方法返回值 * @throws Throwable 抛出的异常 */ @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { seeHouse(); signContract(); takeMoney(); // 调用生成代理的父类方法 return methodProxy.invokeSuper(o, args); } // 省略方法 seeHouse 等 }
-
测试
public class ProxyPatternTest { @Test public void testCglib() { CglibProxyClient client = new CglibProxyClient(); RentOuter instance = client.getInstance(Lessor.class); instance.rentOut(); } }
-
CGLIB动态代理流程
- 实现 MethodInterceptor 接口,intercept 方法接收 MethodProxy 参数,在调用前后加入增强逻辑
- 定义 Enhancer 对象,setSuperclass 方法设置父类为被代理的类
- setCallback 方法设置 MethodInterceptor 实例
- create 方法生成被代理对象,重写被代理的方法,加上 final 修饰符,同时加入增强逻辑
应用场景
-
JDK 动态代理与 CGLIB 代理的比较
JDK 动态代理对象实现了被代理对象的全部接口,CGLIB 代理是继承了被代理对象
JDK 和 CGLIB 都是在运行期生成字节码,JDK 是直接写 class 字节码,CGLIB 使用 ASM 框架写 class 字节码,生成代理对象的效率上 JDK 更高
JDK 通过反射机制调用代理方法,CGLIB 通过 FastClass 机制直接调用方法,CGLIB 被代理类执行效率更高
-
应用举例
在 Spring AOP 中,当 Bean 有实现接口时,会用 JDK 动态代理,当 Bean 没有实现接口时则选择 CGLib
可以通过配置或注解来强制使用 CGLIB
-
spring.aop.proxy-target-class=true
-
@EnableAspectJAutoProxy(proxyTargetClass = true)
-