切点表达式
Joinpoint,织入点,指需要执行代理操作的某个类的某个方法(仅支持方法级别的JoinPoint);
Pointcut是JoinPoint的表述方式,能捕获JoinPoint。
最常用的切点表达式是AspectJ的切点表达式。需要匹配类,定义ClassFilter接口;匹配方法,定义MethodMatcher接口。PointCut需要同时匹配类和方法,包含ClassFilter和MethodMatcher,AspectJExpressionPointcut是支持AspectJ切点表达式的PointCut实现,简单实现仅支持execution函数。
测试:
1 2 3 4 5 6
| public class HelloService { public String sayHello() { System.out.println("hello"); return "hello"; } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| 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的动态代理
AopProxy是获取代理对象的抽象接口,JdkDynamicAopProxy的基于JDK动态代理的具体实现。TargetSource,被代理对象的封装。MethodInterceptor,方法拦截器,是AOP Alliance的”公民”,顾名思义,可以拦截方法,可在被代理执行的方法前后增加代理行为。
测试;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class DynamicProxyTest {
@Test public void testJdkDynamicProxy() throws Exception { WorldService worldService = new WorldServiceImpl();
AdvisedSupport 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文件,为类生成子类,因此被代理类不需要继承自任何接口。
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class DynamicProxyTest {
private AdvisedSupport advisedSupport;
@Before public void setup() { 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); }
@Test public void testCglibDynamicProxy() throws Exception { WorldService proxy = (WorldService) new CglibAopProxy(advisedSupport).getProxy(); proxy.explode(); } }
|
AOP代理工厂ProxyFactory
增加AOP代理工厂ProxyFactory,由AdvisedSupport#proxyTargetClass属性决定使用JDK动态代理还是CGLIB动态代理。
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class DynamicProxyTest {
private AdvisedSupport advisedSupport;
@Before public void setup() { 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); }
@Test public void testProxyFactory() throws Exception { // 使用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(); } }
|
几种常用的Advice:BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice…
Spring将AOP联盟中的Advice细化出各种类型的Advice,常用的有BeforeAdvice/AfterAdvice/AfterReturningAdvice/ThrowsAdvice,我们可以通过扩展MethodInterceptor来实现。
只简单实现BeforeAdvice,有兴趣的同学可以帮忙实现另外几种Advice。定义MethodBeforeAdviceInterceptor拦截器,在执行被代理方法之前,先执行BeforeAdvice的方法。
- BeforeAdvice
- AfterAdvice
- AfterReturningAdvice
- ThrowsAdvice
测试:
1 2 3 4 5 6 7
| public class WorldServiceBeforeAdvice implements MethodBeforeAdvice {
@Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("BeforeAdvice: do something before the earth explodes"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class DynamicProxyTest {
private AdvisedSupport advisedSupport;
@Before public void setup() { WorldService worldService = new WorldServiceImpl();
advisedSupport = new AdvisedSupport(); TargetSource targetSource = new TargetSource(worldService); MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* org.springframework.test.service.WorldService.explode(..))").getMethodMatcher(); advisedSupport.setTargetSource(targetSource); advisedSupport.setMethodMatcher(methodMatcher); }
@Test public void testBeforeAdvice() throws Exception { //设置BeforeAdvice WorldServiceBeforeAdvice beforeAdvice = new WorldServiceBeforeAdvice(); MethodBeforeAdviceInterceptor methodInterceptor = new MethodBeforeAdviceInterceptor(beforeAdvice); advisedSupport.setMethodInterceptor(methodInterceptor);
WorldService proxy = (WorldService) new ProxyFactory(advisedSupport).getProxy(); proxy.explode(); } }
|
PointcutAdvisor:Pointcut和Advice的组合
Advisor是包含一个Pointcut和一个Advice的组合,Pointcut用于捕获JoinPoint,Advice决定在JoinPoint执行某种操作。实现了一个支持aspectj表达式的AspectJExpressionPointcutAdvisor。
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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的流程,具体实现查看AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation。
DefaultAdvisorAutoProxyCreator是处理横切逻辑的织入返回代理对象的InstantiationAwareBeanPostProcessor实现类,当对象实例化时,生成代理对象并返回。
至此,bean的生命周期如下:
测试: auto-proxy.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="worldService" class="org.springframework.test.service.WorldServiceImpl"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean id="pointcutAdvisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor"> <property name="expression" value="execution(* org.springframework.test.service.WorldService.explode(..))"/> <property name="advice" ref="methodInterceptor"/> </bean>
<bean id="methodInterceptor" class="org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor"> <property name="advice" ref="beforeAdvice"/> </bean>
<bean id="beforeAdvice" class="org.springframework.test.common.WorldServiceBeforeAdvice"/>
</beans>
|
1 2 3 4 5 6 7 8 9 10 11
| public class AutoProxyTest {
@Test public void testAutoProxy() throws Exception { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:auto-proxy.xml");
//获取代理对象 WorldService worldService = applicationContext.getBean("worldService", WorldService.class); worldService.explode(); } }
|