切点表达式

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的生命周期如下:

img测试: 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();
}
}

本站由 卡卡龙 使用 Stellar 1.29.1主题创建

本站访问量 次. 本文阅读量 次.