#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 文件中声明激活自动扫描组件功能,同时激活自动代理功能:
<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 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, 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 解析