代理模式是指使用一个代理对象替代目标对象,用以在执行目标方法的外围完成功能的增强
代理模式的角色分为目标对象和代理对象
代理模式有静态代理、动态代理两种
静态代理:显式声明一类对象的代理
动态代理:动态生成一类对象的代理
示例:房东要出租房屋,由中介代理出租
出租人接口、房东类
// 出租人接口
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 动态代理类
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 动态代理流程
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动态代理流程
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)