携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
代理是一种介于继承和组合之间的实现“类的复用”的方法。继承是通用类功能的扩展与特化,新类隐式引用现有类,和现有类是 is-a 关系;组合是新类引用一个或多个现有类来实现新的功能,新类将显式引用现有类,用户只看得到新类提供的方法。而代理是用代理类将目标类进行包装和扩展,可以对目标类的方法进行扩展和特化、也可以对外屏蔽某些方法,其UML类图如下所示。
classDiagram
class Subject
<<abstract>> Subject
Subject : +request()
class RealSubject
RealSubject : +request()
class Proxy {
+preRequest()
+request()
+afterRequest()
}
RealSubject --|> Subject : 泛化
Proxy --|> Subject : 泛化
Client ..> Proxy : 依赖
JDK动态代理
动态代理是将对目标对象的所有调用都重定向到代理类中,主要有JDK动态代理和cglib动态代理两种常见的方式。JDK动态代理是Java原生支持的基于接口的代理方式,通过如下方式获得 ReadSubject
的代理类
@Slf4j
public class JdkProxySubject implements InvocationHandler {
private RealSubject realSubject;
public JdkProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
log.info("【jdk动态代理】方法执行前");
try {
result = method.invoke(realSubject, args);
} catch (Exception e) {
log.info("【jdk动态代理】方法抛出异常,{}", e.getMessage());
throw e;
} finally {
log.info("【jdk动态代理】方法执行结束");
}
log.info("【jdk动态代理】方法正常执行");
return result;
}
}
要在客户端中使用动态代理对象,需要引入 java.lang.reflect.Proxy 类,用 Proxy.newProxyInstance方法来得到代理对象
Subject o = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), new JdkProxySubject(new RealSubject()));
System.out.println(o.request());
对 jdk动态代理的源码进行跟踪,在 newProxyInstance 方法中发现,该方法调用 getProxyClass0方法来寻找或生成指定的代理类。查看该方法源码,首先通过 proxyClassCache.get()方法在缓存中获取或生成放在缓存中。proxyClassCache是通过 ProxyClassFactory生成的缓存对象,很明显这是一个工厂,该工厂生成代理对象字节码。我们可以在客户端代码中添加参数保存生成的字节码。