手写 Spring 10 - AOP 和 Spring 容器的整合
我们先大致了解正常在 Spring 容器中使用 AOP 的基本步骤:
- 定义切面类:创建一个切面类,该类包含了切面逻辑(Advice)和切入点(Pointcut)。切面逻辑定义了要在切入点处执行的逻辑,而切入点定义了在哪些方法或位置应用该切面逻辑。
- 配置切面类:在 Spring 配置文件(如 XML 配置文件)中,声明切面类作为一个 bean,并配置其相关属性,例如切面逻辑和切入点。
- 使用 AOP 功能:在应用程序中,可以直接使用被代理的 bean,并在其方法上应用切面逻辑。当方法被调用时,AOP 将拦截相应的切入点,并执行切面逻辑中定义的逻辑。
而 AOP 和 Spring 容器的整合,如上就是我们最终封装之后,对外暴露的流程。
# 解析
主要就是对上一篇 AOP 的进一步封装。与 Spring 整合 AbstractAutowireCapableBeanFactory.createBean 在创建 Bean 时,在实例化 Bean 之前判断是否需要代理,通过 InstantiationAwareBeanPostProcessor 查找所有的需要被代理的类和当前类比较,如果当前类需要被代理则创建代理类返回(不走正常的实例化逻辑了);
# 进一步封装 AOP
# 定义一些职责和能力
Advisor
一个 aop 操作的访问者,持有要执行的具体操作。
public interface Advisor {
/**
* 获取一个准备好的 aop 执行器
*
* @return {@link Advice}
*/
Advice getAdvice();
}
2
3
4
5
6
7
8
9
10
BeforeAdvice,MethodBeforeAdvice
MethodBeforeAdvice 用于在目标方法执行之前执行一些额外的逻辑操作(在 Spring 框架中,Advice 都是通过方法拦截器 MethodInterceptor 实现的。环绕 Advice 类似一个拦截器的链路,Before Advice、After advice等。这里简单只做一个MethodBeforeAdvice前置拦截)。
通过实现 MethodBeforeAdvice 接口,开发人员可以在目标方法执行之前插入自定义的行为,比如日志记录、权限验证、事务管理等。
public interface BeforeAdvice extends Advice {
}
2
3
public interface MethodBeforeAdvice extends BeforeAdvice {
/**
* aop 方法前置操作
*
* @param method 目标方法
* @param args 目标方法参数
* @param target 目标对象,可能是空
* @throws Throwable throwable
*/
void before(Method method, Object[] args, Object target) throws Throwable;
}
2
3
4
5
6
7
8
9
10
11
12
13
PointcutAdvisor
切入点访问者,PointcutAdvisor 承担了 Pointcut 和 Advice *的组合,*Pointcut 用于获取 JoinPoint,而 Advice 决定于 JoinPoint 执行什么操作。
public interface PointcutAdvisor extends Advisor {
/**
* 获取一个切入点
*
* @return {@link Pointcut}
*/
Pointcut getPointcut();
}
2
3
4
5
6
7
8
9
10
# 具体的实现
AspectJExpressionPointcutAdvisor 切面拦截器
将切面表达式,切面,匹配后的执行操作进行封装实现,后续就可以配置为一个 bean 加载到容器中方便使用了。
public class AspectJExpressionPointcutAdvisor implements PointcutAdvisor {
private Advice advice;
private String expression;
private AspectJExpressionPointcut aspectJExpressionPointcut;
public void setAdvice(Advice advice) {
this.advice = advice;
}
public void setExpression(String expression) {
this.expression = expression;
}
@Override
public Advice getAdvice() {
return advice;
}
@Override
public Pointcut getPointcut() {
if (aspectJExpressionPointcut == null) {
aspectJExpressionPointcut = new AspectJExpressionPointcut(expression);
}
return aspectJExpressionPointcut;
}
}
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
MethodBeforeAdviceInterceptor 方法前置拦截器
对方法拦截器的适配,将拦截到的方法,执行前后交由 spring 处理。
这里处理了执行前交由 MethodBeforeAdvice 处理。
public class MethodBeforeAdviceInterceptor implements MethodInterceptor {
private MethodBeforeAdvice methodBeforeAdvice;
public MethodBeforeAdviceInterceptor() {
}
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice methodBeforeAdvice) {
this.methodBeforeAdvice = methodBeforeAdvice;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
methodBeforeAdvice.before(invocation.getMethod(), invocation.getArguments(), invocation.getThis());
return invocation.proceed();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ProxyFactory 代理工厂
选择 JDK 还是 Cglib 代理。
public class ProxyFactory {
private final AdvisedSupport advisedSupport;
public ProxyFactory(AdvisedSupport advisedSupport) {
this.advisedSupport = advisedSupport;
}
public Object getProxy() {
return createAopProxy().getProxy();
}
private AopProxy createAopProxy() {
if (advisedSupport.isProxyTargetClass()) {
return new Cglib2AopProxy(advisedSupport);
}
return new JdkDynamicAopProxy(advisedSupport);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 将实现加入原有的逻辑中
- 原有类修改,AdvisedSupport(主要是用于把代理、拦截、匹配的各项属性包装到一个类中,方便在 AopProxy 实现类进行使用。),添加通过什么方式代理。
/**
* 是否通过 类 代理对象(cglib代理?true:false)
*/
private boolean proxyTargetClass = false;
public boolean isProxyTargetClass() {
return proxyTargetClass;
}
public void setProxyTargetClass(boolean proxyTargetClass) {
this.proxyTargetClass = proxyTargetClass;
}
2
3
4
5
6
7
8
9
10
11
12
# 测试
- 测试类
无
- 测试结果
无
# 与 Spring 整合
# 定义一些职责和能力
InstantiationAwareBeanPostProcessor
扩展自 BeanPostProcessor,接口的主要作用在于:目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置。
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
/**
* 在目标对象实例化之前调用,该方法的返回值类型是Object,我们可以返回任何类型的值。
* 由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。
* 如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessAfterInitialization方法会调用,其它方法不再调用;否则按照正常的流程走。
*/
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
}
2
3
4
5
6
7
8
9
10
# 具体的实现
DefaultAdvisorAutoProxyCreator
DefaultAdvisorAutoProxyCreator
是 Spring Framework 中的一个类,负责根据配置的 Advisors 自动为 Bean 创建代理。它会扫描 bean,查找任何配置的 Advisors,并在必要时为这些 bean 创建动态代理。在 Spring Framework 中,Advisors 是定义切面逻辑(advice)的对象,例如拦截方法调用以应用额外的行为。
它的工作原理如下:
DefaultAdvisorAutoProxyCreator
在初始化阶段检查应用上下文中的所有 bean。- 它查找配置了 Advisors 的 bean。一个 Advisor 通常由切面逻辑(advice)和切入点(pointcut)组成,切入点用于指定切面逻辑应该应用的位置。
- 对于每个配置了 Advisors 的 bean,
DefaultAdvisorAutoProxyCreator
创建一个代理对象,该代理对象包装了原始的 bean。 - 代理对象拦截对 bean 的方法调用,并应用配置的 Advisors 定义的切面逻辑。
- 当应用上下文中的其他 bean 请求原始 bean 时,它们将接收代理对象而不是原始对象。
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (isInfrastructureClass(beanClass)) {
return null;
}
Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
for (AspectJExpressionPointcutAdvisor advisor : advisors) {
// 不匹配当前类,过滤
ClassFilter classFilter = advisor.getPointcut().getClassFilter();
if (classFilter != null && !classFilter.matches(beanClass)) {
continue;
}
// 转换为代理对象返回
TargetSource targetSource = null;
try {
targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
}
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTargetSource(targetSource);
advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
// 是否使用 cglib 代理,这里先写死为 true,后续根据目标类动态改造
advisedSupport.setProxyTargetClass(true);
return new ProxyFactory(advisedSupport).getProxy();
}
return null;
}
private boolean isInfrastructureClass(Class<?> beanClass) {
return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
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
# 将实现加入原有的逻辑中
- 修改 AbstractAutowireCapableBeanFactory,在创建 bean 时,与 spring 整合。
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
// 这里加入逻辑:判断是否返回 代理 Bean 对象
bean = resolveBeforeInstantiation(beanName, beanDefinition);
if (bean != null) {
return bean;
}
// 实例化
bean = createBeanInstance(beanName, beanDefinition, args);
// 属性填充
applyPropertyValues(beanName, bean, beanDefinition);
// 添加 Bean 的初始化扩展
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Failed to bean:[" + beanName + "] instance", e);
}
// 添加单例 Bean 缓存
if (beanDefinition.isSingleton()) {
// 注册实现了 DisposableBean 接口的 单例Bean 对象,留待容器停止的时候调用。
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
addSingleton(beanName, bean);
}
return bean;
}
protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
// 前置方法,尝试获取一个代理对象
Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
if (bean != null) {
// 如果生成了目标代理对象,后续只有 postProcessAfterInitialization 方法会调用,其它方法不再调用
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
return bean;
}
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
Object result = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessBeforeInstantiation(beanClass, beanName);
if (null != result) {
return result;
}
}
}
return null;
}
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
# 测试
- xml 准备,spring-advice.xml。
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<!-- 目标类 -->
<bean id="tiger" class="com.snail.springframework.beans.factory.bean.Tiger">
<property name="name" value="name-xml-init-wow!"></property>
</bean>
<bean class="com.snail.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<!-- 切面逻辑 -->
<bean id="tigerAdvice" class="com.snail.springframework.aop.aspectj.TigerAdvice"/>
<bean id="methodInterceptor" class="com.snail.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
<property name="methodBeforeAdvice" ref="tigerAdvice"/>
</bean>
<!-- 切入点 -->
<bean class="com.snail.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="advice" ref="methodInterceptor"></property>
<!-- cglib 代理 -->
<property name="expression" value="execution(* com.snail.springframework.beans.factory.bean.Tiger.*(..))"></property>
</bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
提前准备
TigerAdvice
public class TigerAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("TigerAdvice method before() -> method:" + method.getName() + "(), args:" + JSONUtil.toJsonStr(args));
System.out.println("TigerAdvice method before() -> 前置aop, 进行一些操作, sleep(2000)");
Thread.sleep(2000);
}
}
2
3
4
5
6
7
8
9
10
- 测试类
/**
* aop 和 spring 整合测试
*/
@Test
public void aopSpringAdviceTest() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-advice.xml");
Tiger proxy = (Tiger) context.getBean("tiger");
System.out.println("------------------" + DateUtil.now());
proxy.setName("wow^^ hu~ Proxy");
System.out.println("------------------" + DateUtil.now());
// debug 查看是是否代理类:Tiger$$EnhancerByCGLIB$$ced6ff89@1860
System.out.println("proxy tiger name:" + proxy.getAnimalName());
System.out.println("------------------" + DateUtil.now());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- 测试结果
------------------2023-07-17 17:35:40
TigerAdvice method before() -> method:setName(), args:["wow^^ hu~ Proxy"]
TigerAdvice method before() -> 前置aop, 进行一些操作, sleep(2000)
------------------2023-07-17 17:35:42
TigerAdvice method before() -> method:getAnimalName(), args:[]
TigerAdvice method before() -> 前置aop, 进行一些操作, sleep(2000)
proxy tiger name:wow^^ hu~ Proxy
------------------2023-07-17 17:35:44
2
3
4
5
6
7
8
# 总结
AOP(面向切面编程)和 Spring 容器的整合是 Spring Framework 提供的一项重要功能。通过将 AOP 和 Spring 容器结合使用,可以实现横切关注点的处理,提高代码的模块化和复用性,以及增强应用程序的可维护性和可扩展性。
在 AOP 和 Spring 容器的整合过程中,关键点包括:
- 切面定义:通过定义切面类,其中包含切面逻辑(Advice)和切入点(Pointcut),来描述切面。
- 切点解析:切入点是一个表达式,用于确定在哪些方法或位置应用切面逻辑。
- 代理模式:Spring 使用代理模式来实现切面逻辑的织入。它通过动态创建代理对象并将切面逻辑织入到目标对象中。
- 代理创建和织入:对于实现接口的目标对象,Spring 使用 JDK 动态代理;对于没有实现接口的目标对象,Spring 使用 CGLIB 代理。
- AOP 配置:通过注解、XML 配置或基于 Java 的配置,开发者可以定义切面类、建议、切入点和切点表达式,以及指定目标对象和要织入的切面逻辑。
打赏一下
「真诚赞赏,手留余香」
# 打赏记录
打赏者 | 打助金额 (元) | 支付方式 | 时间 | 备注 |
---|---|---|---|---|
John | 12 | 微信 | 2020-06-09 | tip of you |
艾斯 | 32 | 支付宝 | 2020-07-11 | 火拳赞赏 |
HickSalmon | 15 | 微信 | 2020-09-21 | 有赏交流 |