Spring 第七讲 Aspect Advisor
pom.xml 准备
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/>
</parent>
<groupId>org.laoshiren</groupId>
<artifactId>spring-bean-init</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<aspectj.version>1.9.7</aspectj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 织入的依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- 运行时需要的依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
spring 中的代理
java
@Aspect
@Component("acMyAspect")
public class MyAspect {
@Before("execution(* foo())")
public void before(){
System.out.println("before");
}
@Before("execution(* foo())")
public void before2(){
System.out.println("before2");
}
@After("execution(* foo())")
public void after(){
System.out.println("after");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
spring中有2中切面,一种是aspect,一种advisor , advisor是更细粒度的切面,只包含了一个通知(advice)和切点(pointcut) 而aspect 会将拆解成多个通知和切点。
Spring 中对切点、通知、切面的抽象如下
- 切点:接口 Pointcut,典型实现 AspectJExpressionPointcut
- 通知:典型接口为 MethodInterceptor 代表环绕通知
- 切面:Advisor,包含一个 Advice 通知,PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut
java
public class AspectMain {
public static void main(String[] args) {
IService iService = new MyService();
// 准备切点
// Pointcut
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.laoshiren.*..*.foo())");
// 准备通知
MethodInterceptor advice = (invocation)->{
System.out.println("before");
Object proceed = invocation.proceed();
System.out.println("after");
return proceed;
};
// 准备切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
// 创建代理
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(iService);
proxyFactory.addAdvisor(advisor);
IService proxy = (IService)proxyFactory.getProxy();
proxy.foo();
}
}
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
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
选择规则
查看我们代理使用的是cglib 在什么时候才会使用JDK呢
java
public class ProxyConfig implements Serializable {
private boolean proxyTargetClass = false;
}
1
2
3
4
5
2
3
4
5
- proxyTargetClass 是false 的时候,目标实现了接口,使用JDK
- proxyTargetClass 是false 的时候,目标没有实现接口,使用cglib
- proxyTargetClass 是true 的时候,总是使用cglib
上述代码为什么还是使用cglib 呢,是因为我们没有设置代理的接口 proxyFactory.setInterfaces(iService.getClass().getInterfaces())
切点匹配
判断哪些方法适合这个切点匹配
java
public class AspectMatchMain {
public static void main(String[] args) throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.laoshiren.*..*.foo())");
Method methodFoo = MyService.class.getMethod("foo");
boolean matches = pointcut.matches(methodFoo, MyService.class);
System.out.println(matches);
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
调用aspectj的匹配方法,比较关键额是它实现了MethodMatcher
接口用来执行匹配
java
public class AspectMatchMain {
public static void main(String[] args) throws NoSuchMethodException {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.laoshiren.*..*.foo())");
Method methodFoo = MyService.class.getMethod("foo");
boolean matches = pointcut.matches(methodFoo, MyService.class);
System.out.println(matches);
AspectJExpressionPointcut pointcut2 = new AspectJExpressionPointcut();
pointcut2.setExpression("@annotation(com.laoshiren.app.aspect.match.Ass)");
boolean matches2 = pointcut2.matches(methodFoo, MyService.class);
System.out.println(matches2);
AnnotationMatchingPointcut annotationPointcut = new AnnotationMatchingPointcut(Ass.class);
boolean matches1 = annotationPointcut
.getMethodMatcher()
.matches(methodFoo, MyService.class);
System.out.println(matches1);
StaticMethodMatcherPointcut staticPointcut = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 检查方法上是否加了对应注解
boolean present = MergedAnnotations
.from(method)
.isPresent(Ass.class);
// 查看类上是否加了对应注解
boolean present1 = MergedAnnotations
.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY)
.isPresent(Ass.class);
return present || present1;
}
};
System.out.println(staticPointcut.matches(methodFoo, MyService.class));
}
}
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
31
32
33
34
35
36
37
38
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
31
32
33
34
35
36
37
38
@Advisor 与 @Aspect
java
class Target1 {
public void foo(){
System.out.println("target1 foo");
}
}
class Target2 {
public void bar(){
System.out.println("target1 bar");
}
}
// 高级切面类
@Aspect
class Aspect1{
@Before("execution(* foo())")
public void before(){
System.out.println("aspect1 before...");
}
@After("execution(* foo())")
public void after(){
System.out.println("aspect1 after...");
}
}
@Configuration
class Config {
// 低级切面类
@Bean
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice3);
}
@Bean
public MethodInterceptor advice3(){
return invocation -> {
System.out.println("advice before...");
Object proceed = invocation.proceed();
System.out.println("advice after...");
return proceed;
};
}
}
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
AnnotationAwareAspectJAutoProxyCreator
的作用
- 将高级
@Aspect
切面统一为低级 Advisor 切面 - 在合适的时机创建代理
java
public class AdvisorMain {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Aspect1.class);
context.registerBean(Config.class);
// 解析@Bean
context.registerBean(ConfigurationClassPostProcessor.class);
// Bean 处理器 将Aspect 高级切面转换低级切面 ,根据切面创建代理对象 (ProxyFactory)
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
context.refresh();
// 获取这个processor
AnnotationAwareAspectJAutoProxyCreator proxyCreator
= context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
// 根据目标获取有资格的切面 看看那些advisor 可以应用于目标类(低级切面)
// 高级切面会转换成低级切面
List<Advisor> targetAdvisor = proxyCreator.findEligibleAdvisors(Target1.class, "target");
for (Advisor advisor : targetAdvisor) {
System.out.println(advisor.getClass().getName());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
输出如下
shell
## Spring 给所有代理都加的切面
org.springframework.aop.interceptor.ExposeInvocationInterceptor$1
## Config类里的定义的切面
org.springframework.aop.support.DefaultPointcutAdvisor
## 高级切面转换后的切面 @Before
org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl
## 高级切面转换后的切面 @After
org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
findEligibleAdvisors
找到资格的 Advisors
- 有资格的 Advisor 一部分是低级的, 可以由自己编写, Config类 advisor3
- 有资格的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得
调用之后返回集合不为空,则表示需要创建代理
java
public class AdvisorMain {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Aspect1.class);
context.registerBean(Config.class);
// 解析@Bean
context.registerBean(ConfigurationClassPostProcessor.class);
// Bean 处理器 将Aspect 高级切面转换低级切面 ,根据切面创建代理对象 (ProxyFactory)
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
context.refresh();
// 获取这个processor
AnnotationAwareAspectJAutoProxyCreator proxyCreator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
// 根据目标获取有资格的切面 看看那些advisor 可以应用于目标类(低级切面)
// 高级切面会转换成低级切面
List<Advisor> targetAdvisor = proxyCreator.findEligibleAdvisors(Target1.class, "target");
// 代理对象 wrapIfNecessary 会内部调用FindEligibleAdvisors 判断是否需要进行转换低级切面再创建代理
Object o = proxyCreator.wrapIfNecessary(new Target1(), "target1", "target1");
// 原始对象
Object o1 = proxyCreator.wrapIfNecessary(new Target2(), "target2", "target2");
Target1 target1 = (Target1)o;
target1.foo();
}
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
shell
advice before...
aspect1 before...
target1 foo
aspect1 after...
advice after...
1
2
3
4
5
2
3
4
5
高级切面向低级切面的转换
首先我们拿到Aspect类之后,应该遍历里面的方法,将带有@Before
,@After
等注解转换成低级的注解
java
@Aspect
class Aspect1 {
@Before("execution(* foo())")
public void before() {
System.out.println("aspect1 before...");
}
@After("execution(* foo())")
public void after() {
System.out.println("aspect1 after...");
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 获取当前类的定义方法
- 查看方法是否存在对应的注解
- 创建对应的切点
- 创建通知
- 创建切面
java
public class AspectTransAdvisorMain {
public static void main(String[] args) {
List<Advisor> list = new ArrayList<>();
// Aspect的实例工场
AspectInstanceFactory factory = new SimpleAspectInstanceFactory(Aspect1.class);
// 获取Aspect切面类定义的所有方法
Method[] declaredMethods = Aspect1.class.getDeclaredMethods();
// 遍历数组准备转换成低级切面
for (Method declaredMethod : declaredMethods) {
// 查看方法上是否标注指定注解
boolean beforeAnnotationPresent = declaredMethod.isAnnotationPresent(Before.class);
if (beforeAnnotationPresent) {
// 获取注解
Before methodBeforeAnnotation = declaredMethod.getAnnotation(Before.class);
String expression = methodBeforeAnnotation.value();
// 创建切点对象
AspectJExpressionPointcut expressionPointcut = new AspectJExpressionPointcut();
expressionPointcut.setExpression(expression);
// 前置通知底层通知类 AspectJMethodBeforeAdvice
AspectJMethodBeforeAdvice beforeAdvice = new AspectJMethodBeforeAdvice(
// 需要前置通知的方法
declaredMethod,
// 切点
expressionPointcut,
// 切面的实例工程
factory
);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(expressionPointcut, beforeAdvice);
list.add(advisor);
}
}
System.out.println(list.size());
}
}
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
31
32
33
34
35
36
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
31
32
33
34
35
36
创建通知的代码包含了如下信息
- 通知的代码从哪来 (即需要通知的代码的方法)
- 切点
- 通知对象如何创建 对应不同的Aspect类
通知 | 注解 | 通知 |
---|---|---|
前置 | @Before | AspectJMethodBeforeAdvice |
环绕通知 | @Around | AspectJAroundAdivce |
返回后通知 | @AfterReturning | AspectAfterReturingAdvice |
throw通知 | @AfterThrowing | AspectAfterThrowingAdvice |
after通知 | @After | AspectJAfterAdvice |