AOP:
面向切面编程。封装多个类的公共行为,讲与业务无关却被共同调用的逻辑封装起来,减少系统重复代码,降低模块间的耦合度。同时解决一些系统层面的问题,例如日志、事务、权限等。
切点表达式
Joinpoint,织入点,指需要执行代理操作的某个类的某个方法(仅支持方法级别的JoinPoint);Pointcut是JoinPoint的表述方式,能捕获JoinPoint。
最常用的切点表达式是AspectJ的切点表达式。需要匹配类,定义ClassFilter接口;匹配方法,定义MethodMatcher接口。PointCut需要同时匹配类和方法,包含ClassFilter和MethodMatcher,AspectJExpressionPointcut是支持AspectJ切点表达式的PointCut实现,简单实现仅支持execution函数。
public class PointcutExpressionTest {
@Test
public void testPointcutExpression() throws Exception {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* org.springframework.test.service.HelloService.*(..))");
Class<HelloService> clazz = HelloService.class;
Method method = clazz.getDeclaredMethod("sayHello");
assertThat(pointcut.matches(clazz)).isTrue();
assertThat(pointcut.matches(method, clazz)).isTrue();
}
}
基于JDK的动态代理
在JDK中,有一个Proxy类(名词,代理人)。Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态的生成实现类,这个类提供的有一个静态方法:newProxyInstance()方法。这个方法的目的就是给我们的目标对象(委托对象)返回一个代理对象。
newProxyInstance()方法需要有三个参数:
- 类加载器(ClassLoader对象)
- 接口集合(一个Interface对象的数组,就是需要代理对象代理那些共同行为,也是委托对象继承的共同行为接口)
- 一个InvocationHandler接口对象(当然可以是它的一个实现类对象)。这个接口中有一个invoke()方法。invoke()方法起到的作用很大,当代理对象调用共同行为方法的时候,invoke()方法就会被自动调用执行。
WorldService worldService = new WorldServiceImpl();
advisedSupport = new AdvisedSupport();
TargetSource targetSource = new TargetSource(worldService);
WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();
MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(*
org.springframework.test.service.WorldService.explode(..))").getMethodMatcher();
advisedSupport.setTargetSource(targetSource);
advisedSupport.setMethodInterceptor(methodInterceptor);
advisedSupport.setMethodMatcher(methodMatcher);
WorldService proxy = (WorldService) new JdkDynamicAopProxy(advisedSupport).getProxy();
proxy.explode();
基于CGLIB的动态代理
基于CGLIB的动态代理实现逻辑也比较简单,查看CglibAopProxy。与基于JDK的动态代理在运行期间为接口生成对象的代理对象不同,基于CGLIB的动态代理能在运行期间动态构建字节码的class文件,为类生成子类,因此被代理类不需要继承自任何接口。
CGLB动态代理是一个开源的第三方工具库,其原理是继承,去生成目标类的子类对象,这样对子类的功能进行增强。但是要求:目标类不能用final修饰,目标类中的方法也不能被final修饰。
CGLB动态代理的效率要大于JDK动态代理的效率。
WorldService worldService = new WorldServiceImpl();
advisedSupport = new AdvisedSupport();
TargetSource targetSource = new TargetSource(worldService);
WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();
MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(*
org.springframework.test.service.WorldService.explode(..))").getMethodMatcher();
advisedSupport.setTargetSource(targetSource);
advisedSupport.setMethodInterceptor(methodInterceptor);
advisedSupport.setMethodMatcher(methodMatcher);
WorldService proxy = (WorldService) new CglibAopProxy(advisedSupport).getProxy();
proxy.explode();
AOP代理工厂
AOP代理工厂ProxyFactory,由AdvisedSupport#proxyTargetClass属性决定采用JDK动态代理还是CGLIB动态代理。
WorldService worldService = new WorldServiceImpl();
advisedSupport = new AdvisedSupport();
TargetSource targetSource = new TargetSource(worldService);
WorldServiceInterceptor methodInterceptor = new WorldServiceInterceptor();
MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(*
org.springframework.test.service.WorldService.explode(..))").getMethodMatcher();
advisedSupport.setTargetSource(targetSource);
advisedSupport.setMethodInterceptor(methodInterceptor);
advisedSupport.setMethodMatcher(methodMatcher);
// 使用JDK动态代理
advisedSupport.setProxyTargetClass(false);
WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
proxy.explode();
// 使用CGLIB动态代理
advisedSupport.setProxyTargetClass(true);
proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
proxy.explode();
PointcutAdvisor:Pointcut和Advice的组合
PointcutAdvisor是包含一个Pointcut和一个Advice的组合,Pointcut用于捕获JoinPoint,Advice决定在JoinPoint执行某种操作。实现了一个支持aspectj表达式的AspectJExpressionPointcutAdvisor。
public class DynamicProxyTest {
@Test
public void testAdvisor() throws Exception {
WorldService worldService = new WorldServiceImpl();
//Advisor是Pointcut和Advice的组合
String expression = "execution(* org.springframework.test.service.WorldService.explode(..))";
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
advisor.setExpression(expression);
MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(new WorldServiceBeforeAdvice());
advisor.setAdvice(methodInterceptor);
ClassFilter classFilter = advisor.getPointcut().getClassFilter();
if (classFilter.matches(worldService.getClass())) {
AdvisedSupport advisedSupport = new AdvisedSupport();
TargetSource targetSource = new TargetSource(worldService);
advisedSupport.setTargetSource(targetSource);
advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
// advisedSupport.setProxyTargetClass(true); //JDK or CGLIB
WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy();
proxy.explode();
}
}
}
动态代理融入bean生命周期
结合前面讲解的bean的生命周期,BeanPostProcessor处理阶段可以修改和替换bean,正好可以在此阶段返回代理对象替换原对象。不过我们引入一种特殊的BeanPostProcessor——InstantiationAwareBeanPostProcessor,如果InstantiationAwareBeanPostProcessor处理阶段返回代理对象,会导致短路,不会继续走原来的创建bean的流程。
具体流程如下: