#yyds干货盘点# Spring AOP详解

Posted 公众号JavaEdge

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点# Spring AOP详解相关的知识,希望对你有一定的参考价值。

1 AOP 应用场景

AOP,Aspect Oriented Programming,面向切面编程。通过预编译和运行期动态代理,实现在不修改源代码情况下,给程序动态地统一添加功能。
AOP设计追求调用者和被调用者之间的解耦,一些非业务,如:日志、事务、安全等都会写在业务代码中(也即是说,这些非业务类横切于业务类),但这些代码往往是重复,复制粘贴式的代码,会给程序维护带来不便,AOP 就实现了把这些业务需求与系统需求分开来做。这种解决的方式也称代理机制。

2 AOP 概念

1 切面(Aspect)

官方定义“ 一个关注点的模块化,这个关注点可能会横切多个对象” 。
“ 切面”可在ApplicationContext 中aop:aspect来配置(古老的 xml 配置方式)

连接点(Joinpoint)

程序执行过程中的某一行为,例如,xxxService .get 的调用或
xxxService .delete 抛出异常等行为。

通知(Advice)

“切面”对于某个“连接点”所产生的动作。其中,一个“切面”可以包含多个“Advice”。

切入点(Pointcut)

匹配连接点的断言,在AOP 中通知和一个切入点表达式关联。切面中的所有通知所关注的连接点,都由切入点表达式来决定。

目标对象(Target Object)

被一个或者多个切面所通知的对象。例如,AServcieImpl 和BServiceImpl,当然在实际运行时,Spring AOP 采用代理实现,实际AOP 操作的是TargetObject 的代理对象。

AOP 代理(AOP Proxy)

在Spring AOP 中有两种代理方式:

  • JDK 动态代理
  • CGLib 代理

默认,TargetObject 实现了接口时,则采用JDK 动态代理;反之,采用CGLib 代理。
强制使用CGLib 代理需要将aop:config的proxy-target-class 属性设为true。
通知(Advice)类型:
6、前置通知(Before Advice)
在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext
中在aop:aspect里面使用aop:before元素进行声明。例如,TestAspect 中的doBefore 方法。
7、后置通知(After Advice)
当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext 中在
aop:aspect里面使用aop:after元素进行声明。例如,ServiceAspect 中的returnAfter 方法,所
以Teser 中调用UserService.delete 抛出异常时,returnAfter 方法仍然执行。
8、返回后通知(After Return Advice)
在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext 中在aop:aspect
里面使用元素进行声明。
9、环绕通知(Around Advice)
包围一个连接点的通知,类似Web 中Servlet 规范中的Filter 的doFilter 方法。可以在方法的调用前
后完成自定义的行为, 也可以选择不执行。ApplicationContext 中在aop:aspect 里面使用
aop:around元素进行声明。例如,ServiceAspect 中的around 方法。

异常通知(After Throwing Advice)

在方法抛出异常退出时执行的通知。ApplicationContext 中在aop:aspect 里面使用
aop:after-throwing元素进行声明。
可将多个通知应用到一个目标对象。使用Spring AOP 可以基于两种方式:

  • 方便和强大的注解方式
  • 古老的xml配置方式

3 配置方式

注解

在xml 文件中声明激活自动扫描组件功能,同时激活自动代理功能:

<?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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.gupaoedu"/>
<context:annotation-config />
</beans>

或者基于注解的:@SpringBootApplication(scanBasePackages = "xxx")
第二步,为Aspect 切面类添加注解。

4 Spring AOP 源码分析

入口

AOP 通过接入BeanPostProcessor开始的,BeanPostProcessor是个监听器,可监听容器触发的Bean生命周期事件。BeanPostProcessor向容器注册以后,容器中管理的Bean就具备了接收IOC容器事件回调的能力。
BeanPostProcessor使用非常简单,只需要提供一个实现接口BeanPostProcessor 的实现类,然后在Bean的配置文件中设置即可。

2 AbstractAu对容器生成的Bean添加后置处理器

BeanPostProcessor调用发生在IoC容器完成对Bean实例的创建和属性的依赖注入完成后。
当应用程序第一次调用getBean()(lazy-init预实例化除外)向IoC容器索取指定Bean时,触发Spring IOC 容器创建Bean实例并进行依赖注入的过程, 真正实现创建Bean并依赖注入的方法是AbstractAutowireCapableBeanFactory#doCreateBean():

// 真正创建Bean的方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException

// 实例化bean.
// 封装被创建的Bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton())
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);

if (instanceWrapper == null)
instanceWrapper = createBeanInstance(beanName, mbd, args);

final Object bean = instanceWrapper.getWrappedInstance();
// 实例化对象的类型
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class)
mbd.resolvedTargetType = beanType;


// 调用PostProcessor
synchronized (mbd.postProcessingLock)
if (!mbd.postProcessed)
try
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

mbd.postProcessed = true;


// 向Eagerly cache singletons容器中缓存单例Bean,以防循环依赖
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure)
// 匿名内部类,为了防止循环引用,持有Eagerly cache singletons容器中对象的引用
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));


// bean实例的初始化,依赖注入在此触发
// 这个exposedObject 在初始化完成之后返回,作为依赖注入完成后的Bean
Object exposedObject = bean;
try
//将Bean实例对象封装,并且Bean 定义中配置的属性值赋值给实例对象
populateBean(beanName, mbd, instanceWrapper);
// 初始化Bean,为Bean实例应用BeanPostProcessor
exposedObject = initializeBean(beanName, exposedObject, mbd);


if (earlySingletonExposure)
//获取指定名称的已注册的单例模式Bean 对象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null)
//根据名称获取的已注册的Bean 和正在实例化的Bean 是同一个
if (exposedObject == bean)
//当前实例化的Bean 初始化完成
exposedObject = earlySingletonReference;

//当前Bean 依赖其他Bean,并且当发生循环引用时不允许新创建实例对象
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName))
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
//获取当前Bean 所依赖的其他Bean
for (String dependentBean : dependentBeans)
//对依赖Bean 进行类型检查
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean))
actualDependentBeans.add(dependentBean);






// Register bean as disposable.
// 注册完成依赖注入的Bean
try
registerDisposableBeanIfNecessary(beanName, bean, mbd);

return exposedObject;

为Bean实例添加BeanPostProcessor的入口是initializeBean()

3、initializeBean()

为容器创建的Bean实例添加BeanPostProcessor :

// 初始容器创建的Bean实例,为其添加BeanPostProcessor后置处理器
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
if (System.getSecurityManager() != null)
...

else
// 为Bean实例包装相关属性,如名称,类加载器,所属容器等信息
invokeAwareMethods(beanName, bean);


Object wrappedBean = bean;
// 对BeanPostProcessor的postProcessBeforeInitialization
// 回调方法的调用,为Bean实例初始化前做一些处理
if (mbd == null || !mbd.isSynthetic())
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

// 调用Bean实例初始化的方法,这个初始化方法是在Spring Bean 定义配置
// 文件中通过init-Method属性指定
try
invokeInitMethods(beanName, wrappedBean, mbd);

catch (Throwable ex)
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);


//对BeanPostProcessor 后置处理器的postProcessAfterInitialization
//回调方法的调用,为Bean 实例初始化之后做一些处理
if (mbd == null || !mbd.isSynthetic())
wrappedBean = applyBeanPostProcessorsAfterInitialization(#yyds干货盘点# Spring核心之面向切面编程(AOP)

#yyds干货盘点# 老王读Spring AOP-5@Transactional产生AOP代理的原理

#yyds干货盘点# 老王读Spring AOP-5@Transactional产生AOP代理的原理

#yyds干货盘点# 老王读Spring AOP-4Spring AOP 与Spring IoC 结合的过程 && ProxyFactory 解析

Spring | Spring5学习笔记 #yyds干货盘点#

#yyds干货盘点#Spring源码三千问Spring AOP 中 TargetSource 的作用及原理分析