Spring Framework集成之AOP

一、前言

使用过AOP,我们经常用来切包或者方法,然后会回调,有些许意思哈。通过下面4点我们将了解简单的SpringAOP:

前面2点为概念扫盲,不仅要知其然,更要知其所以然,本文不会说明扫描机制,如有需要请看Spring Framework扫描注册

1.AOP概念
2.SpringAOP的概念
3.SpringAOP的简单使用
4.游走SpringAOP集成机制源码

二、AOP概念

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 - 来自百度百科

补充:AOP 实现分类、AOP术语

AOP 实现分类

  • 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
  • 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。

AOP 领域中的特性术语:

  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
  • 切点(PointCut): 可以插入增强处理的连接点。
  • 切面(Aspect): 切面是通知和切点的结合。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

来自SharpCJhttps://www.cnblogs.com/joy99/p/10941543.html

三、SpringAOP的概念

SpringAOP使用的是动态代理进行实现,借助aspectj包进行使用,aspectj支持字段、构造器和方法的连接点,SpringAOP只支持方法的连接点。

细节点:看了前面Spring Framework就知道,Bean对象的字段和构造都由Spring Framework上下文进行构建,对于第三方框架(aspectj)而言,方法切入点最合适。

四、SpringAOP的简单使用

关于Bean的注入在Spring Framework扫描加载有说明

准备
类结构图
avatar
类包依赖
gradle

1
2
3
4
5
6
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
// ==================== spring framework ====================
testImplementation 'org.springframework:spring-context:5.3.5'
// ==================== aspectj ====================
testImplementation 'org.aspectj:aspectjweaver:1.9.6'

maven

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
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>

类结构

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
54
55
56
57
58
59
60
61
62
63
64
package aop.bean;

import org.springframework.stereotype.Component;

@Component
public class Bean {
String name = "I am Bean";

public String getName() {
System.out.println("getName");//为了在输出感受到调用
return name;
}
}
-------------------------------------------------------------
package aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AOPBean {

//切点
@Pointcut("execution(* aop.bean.*.*(..))")
private void pointCut(){

}

//环切
@Around(value = "pointCut()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("start aop");
Object retVal = pjp.proceed();//原方法回调
System.out.println("stop aop");
return "my name is SpringAOP!";//偷天换日
}

//切点执行前调用
@Before("pointCut()")
public void doBefore() {
System.out.println("before");
}

//切点执行后调用
@After("pointCut()")
public void doAfter() {
System.out.println("after");
}
}
-------------------------------------------------------------
package aop;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan
@EnableAspectJAutoProxy//启动Aspect自动代理
public class AOPConfiguration {

}

junit代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void Test2(){
// 构建应用上下文
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AOPConfiguration.class);
// 刷新应用上下文
context.refresh();
Arrays.asList(context.getBeanDefinitionNames())
.forEach(System.out::println);
System.out.println("-------------------------");//上下文分割
//触发aop
System.out.println(context.getBean(Bean.class).getName());
}

执行输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
AOPConfiguration
AOPBean
bean
org.springframework.aop.config.internalAutoProxyCreator
-------------------------
start aop
before
getName
after
stop aop
my name is SpringAOP!

五、游走SpringAOP集成机制源码

由于AOP是可插拔的代理使用,所以需要手动注明,注解使用为@EnableAspectJAutoProxy

@EnableAspectJAutoProxy
avatar

通过@Import会将AspectJAutoProxyRegistrar.class注入到Spring Framework中,这边直接看处理

AspectJAutoProxyRegistrar
avatar
AopConfigUtils
avatar
avatar
avatar
AnnotationAwareAspectJAutoProxyCreator

类结构图

avatar

在获取Bean的时候,在initializeBean中回调BeanFactoryAware接口的setBeanFactory方法(具体看Spring Framework的Bean生成)

AbstractAdvisorAutoProxyCreator
avatar

在refresh刷新上下文中finishBeanFactoryInitialization方法完成最终代理对象生成

AbstractAutoProxyCreator

实例化之前,AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation方法中回调

avatar

实例化之后,AbstractAutowireCapableBeanFactory.initializeBean方法中回调

avatar
avatar
avatar
ProxyFactory
avatar
avatar
avatar

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

请我喝杯咖啡吧~

支付宝
微信