代理模式中代理类和被代理类提供同样的行为,比如实现同一个接口,代理类内部持有对被代理类的引用,在代理类中调用被代理类的方法。
访问者通过调用代理类的方法进而调用被代理类的方法。对调用者来说似乎没有差别,但是在代理类的方法里调用被代理类的方法前后可以增加额外的功能逻辑,这样就可以实现目标方法的增强效果。如下图的结构:
静态代理通过编写代理类的静态代码实现代理。测试代码如下:
//测试业务接口
public interface BizService{
void querySkuStock();
}
//测试业务接口实现类
public class BizServiceImpl implements BizService {
@Override
public void querySkuStock(){
//doSomething...
}
}
//静态代理类
public class BizServiceProxy implements BizService {
private BizServiceImpl bizServiceImpl;
public BizServiceProxy(BizServiceImpl bizServiceImpl){
this.bizService= bizService;
}
public void querySkuStock(){
log.info("querySkuStock开始...");
bizServiceImpl.querySkuStock();
log.info("querySkuStock结束...");
}
}
//测试调用
@Test
public void testBizServiceProxy(){
BizServiceProxy bizServiceProxy = new BizServiceProxy(new BizServiceImpl());
bizServiceProxy.querySkuStock();
}
这种静态代理的方式存在一个最大的问题就是对目标方法的调用逻辑写死在代理类方法里面,也就是每一个被代理类的方法都是不同的,需要创建同样个数的代理类才能实现对不同被代理类的调用。为了解决这个问题,动态代理应运而生,它的解决思路就是将对目标类的调用提取出来抽象成一个接口,在这个接口的实现类里面去实现具体的调用逻辑。结构如下图:
动态代理中用的比较多的两种:JDK动态代理、CGLIB动态代理。
JDK动态代理
使用JDK的java.lang.reflect.Proxy类的newProxyInstance方法实现的代理。用一个测试接口看下具体实现:
//测试接口
public interface JdkProxyDemoService {
void queryTrs();
}
//测试接口的实现类
@Service
public class JdkProxyDemoServiceImpl implements JdkProxyDemoService {
public void queryTrs() {
log.info("业务方法执行...");
}
}
//自定义调用处理器
public class LycJdkProxyInvocationHandler implements InvocationHandler {
private Object targetObj;
public LycJdkProxyInvocationHandler(Object targetObj) {
this.targetObj = targetObj;
}
//proxy 被代理对象 method 目标对象中的方法对象 args 方法对象的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行之前...");
Object rtnObj = method.invoke(targetObj, args);
System.out.println("执行之后...");
return rtnObj;
}
}
//测试调用代理类的方法
public class LycAspectTest {
public static void main(String[] args) {
JdkProxyDemoService targetService = new JdkProxyDemoServiceImpl();
InvocationHandler h = new LycJdkProxyInvocationHandler(targetService);
Object proxyInstance = Proxy.newProxyInstance(targetService.getClass().getClassLoader(),targetService.getClass().getInterfaces(), h);
JdkProxyDemoService jdkProxyDemoService = (JdkProxyDemoService) proxyInstance;
jdkProxyDemoService.queryTrs();
}
}
idea中通过配置VM参数可以将动态生成的代理类的字节码文件保存到本地,方便观察分析代理类的结构。
JDK8及以前版本:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
JDK8以后版本:-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
配置好后再次执行上面的测试代码,在项目目录下就会生成代理类的目录
可以看到JDK动态代理生成的代理类默认是继承Proxy,并实现了被代理类实现的接口,在实现的接口方法里面调用InvocationHandler的invoke方法,这就是前面说的将对目标方法的调用抽象到接口中,在接口的实现类里面定义具体的调用逻辑,这里就可以增加额外的功能,对目标方法进行增强。
从代理类的结构也可以看出来,为什么说JDK动态代理是对接口的代理,因为Java是单继承的,这个代理类默认已经继承了Proxy也就不能再继承其他类了。
JDK动态代理除了需要被代理类是接口类还存在一个问题,因为它是实现被代理类实现的接口方法,如果被代理类有个私有的方法,这种方式生成的代理类就无法对这个私有方法进行代理了,对于这些局限性CGLIB动态代理就能很好的解决。
CGLIB动态代理
对类进行代理。code generator library代码生成库,动态的生成字节码对象。同样通过一个测试类来看下具体实现:
//测试类
public class CglibProxyDemo {
public void queryCost() {
log.info("业务方法执行查询成本...");
}
}
//测试调用处理
public class LycCglibProxyInterceptor implements MethodInterceptor {
// proxy:代理对象 method:目标对象中的方法 args:目标对象中的方法 methodProxy:代理对象中的代理方法对象
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("执行之前....");
Object rtnObj = methodProxy.invokeSuper(proxy, args);
System.out.println("执行之后....");
return rtnObj;
}
}
//测试代理类生成及调用
public class LycAspectTest {
public static void main(String[] args) {
// 创建空的字节码对象
Enhancer enhancer = new Enhancer();
// 设置字节码对象的父类也就是目标类
enhancer.setSuperclass(CglibProxyDemo.class);
//创建回调对象
Callback callback = new LycCglibProxyInterceptor();@1
// 设置字节码对象的回调方法
enhancer.setCallback(callback);
// 得到代理对象
CglibProxyDemo cglibProxyDemo = (CglibProxyDemo) enhancer.create();
// 调用方法
cglibProxyDemo.queryCost();
}
}
同样的通过编码的方式也可以将动态生成的代理类的字节码文件保存到本地,代码如下:
//保存生成的字节码文件
byte[] generate = enhancer.getStrategy().generate(enhancer);
FileOutputStream fileOutputStream = new FileOutputStream(
new File("C:\\个人文档\\learing\\lyccglibtest.class"));
fileOutputStream.write(generate);
fileOutputStream.flush();
fileOutputStream.close();
执行上面的LycAspectTest 测试代码,本地项目目录下也会生成的字节码文件。反编译观察分析它的结构:
可以看到CGLIB生成的代理类是通过创建一个新的类继承被代理类,并重写了父类的方法,在重写的方法里面调用的接口的intercept方法,这个就是前面定义的调用目标类的目标方法的具体逻辑,在这里面可以增加额外的逻辑实现对目标方法的增强。
这种通过创建子类并重写父类方法的方式就要求被代理的方法不能被final修饰,否则子类无法重写也就无法进行代理。
动态代理一个比较重要的应用就是在Spring框架中AOP基于动态代理在程序运行期间对目标方法进行增强。
在IOC阶段通过跟踪代码可以知道,对于开启切面编程或者开启事务管理的配置的项目,在初始化Bean的前后会调用BeanPostProcessor的两个方法,在初始化Bean之前调用postProcessBeforeInitialization方法解析切面类生成通知类,在初始化Bean之后调用postProcessAfterInitialization判断匹配切面通知类以及增强器,如果有匹配到就会生成代理类,代码如下:
两种代理的核心逻辑方法:
JdkDynamicAopProxy. invoke(Object proxy, Method method, Object[] args)
// 获取目标类
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取方法的拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 如果没有拦截器直接反射执行目标方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 将拦截器链组装成MethodInvocation
**MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);**
// 调用proceed()执行拦截器
retVal = **invocation.proceed();**
}
CglibAopProxy. intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
//获取目标类
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
// 直接反射执行
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = invokeMethod(target, method, argsToUse, methodProxy);
}else {
// 调用拦截器
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
// 返回值
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
CGLIB代理里面CglibMethodInvocation.proceed()逻辑是执行super.proceed();而CglibMethodInvocation 继承ReflectiveMethodInvocation
这样看来AOP中JDK动态代理和CGLIB动态代理最终都是调用的ReflectiveMethodInvocation.proceed(),这个类的方法具体逻辑如下:
对于@Aspect切面类生成的通知类会执行if里面的代码,而@Transaction事务管理中事务增强器TransactionInterceptor会执行else里面的代理。看下事务管理如何进行事务增强的:
TransactionInterceptor类:
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
TransactionAspectSupport类:
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); @1 :开启事务
Object retVal = null;
try {
retVal = invocation.proceedWithInvocation(); @2: 调用拦截器方法执行
} catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex);@3: 异常回滚处理
throw ex;
} finally {
cleanupTransactionInfo(txInfo);@4:最终处理
}
commitTransactionAfterReturning(txInfo);@5:事务提交
return retVal;
}
}
AOP中实现增强就是生成代理类的时候定义的调用目标类的目标方法的逻辑,将通知类或者增强器封装成拦截器链,采用责任链模式首先依次调用拦截器的方法执行拦截器内部逻辑,所有拦截器都调用后最后再调用目标方法,然后再依次返回到每个拦截器最终将执行结果返回出去。