一. AOP 概述
- 什么是 AOP : 指定程序在运行期间动态的将某段代码功能切入到指定的位置运行,底层是通过动态代理来实现的
- AOP 优点 : 降低代码的耦合,提高代码复用性,提高系统的扩展性
- AOP 常见使用场景 : 例如日志记录, 性能统计, 权限控制, 事物处理等
AOP 的实现步骤
- Spring 引入 AOP 需要的依赖
- 设置 Spring 开启 AOP 功能 (xml 方式开启,与注解方式开启)
- 创建存有需要通过 AOP 增强监管的业务代码,并注入到 Spring 容器中
- 创建 AOP 切面类,将切面类注入到 Spring 容器中,
- 告诉 Spring 容器,我是一个切面类 @Aspect
- 切面类中创建通知方法 :
- 前置通知 @Before 目标方法执行前运行
- 后置通知 @After 目标方法执行完毕,或抛出异常时运行
- 返回通知 @AfterReturning 目标方法正常返回之后运行
- 异常通知 @AfterThrowing 目标方法发生异常时运行
- 环绕通知 @Around 该通知中可以获取到被监管的所有数据,可以获取到被监管执行的方法,在环绕通知方法中代理执行,所以可以自定义在被监管的方法前后添加需要执行的其他功能
- 设置通知方法在指定位置标注位置运行,切入点 (需要进行增强的业务代码位置, xml方式, 注解方式 @Pointcut(“execution()”)
Spring 支持 AOP :需要添加依赖
示例
- Spring 中引入 AOP 依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
- 注解方式配置开启 AOP ,创建配置类,配置类使用 @EnableAspectJAutoProxy 注解修饰
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.*;
//通过配置类,使用注解的方式开启 Spring 的 aop 功能
@EnableAspectJAutoProxy
@Configuration
public class MyConfiguration {
}
- xml 方式配置开启AOP, resources 文件夹中创建.xml配置文件,配置文件中添加 < aop:aspectj-autoproxy/ >,注意添加命名空间
<?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:aop=http://www.springframework.org/schema/aop
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans"
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<!--开启aop-->
<aop:aspectj-autoproxy/>
<!--向Spring 容器中注入bean 的包扫描,原因是一般情况下
AOP 切面类与,controller,server,dao,不在一个包下,在使用注解
向spring中注入时,没有扫描,无法注入,如果在一个包下,或其它位置已经扫描过了,则可以不定义-->
<context:component-scan base-package="com.aop.自定义切面类所在的包" />
</beans>
- 创建业务类,编写业务代码,假设在业务代码执行前后,或发生异常时通过 AOP 增加其它功能
@Controller
public class TestController2 {
@RequestMapping(value = "/aoptest", method = RequestMethod.GET)
@ResponseBody
public void aoptest(){
System.out.println("我是被监管的业务代码......");
}
}
- 创建切面类,将切面类注入到 Spring 容器中, 告诉 Spring 我是一个切面类,设置切入点,注入切入点扫描的目标方法所在路径是否正确,定义通知方法
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //1.代表该类时切面类
@Component //2.将切面类注入到 Spring 容器中
public class LogCut {
//3.定义切入点方法,方法上添加@Pointcut()注解,设置当前切面类监管的目标方法位置
//为了方便可以单独修饰一个空方法,然后通知注解上引用这个方法,也可以直接修饰通知方法
/*解释:execution代表是个表达式
* 扫描 com.ssm.controller 包下的所有类的所有方法
* 第一个星的位置:代表这个方法的权限修饰符,星为任何权限都可以
* com.com.controller 代表哪个包下的
* controller 后的点有两个:一个代表该包下的根目录下的直接类中方法不包括子包
* 两个点代表包括该包下的子包中类的方法
* 点后的第一个星号位置代表包下的类,此处为星号代表任意类
* 第二个星号位置代表方法,此处为星号代表任意方法
* 括号中的两个点,为方法传入的参数类型,此处为两个点代表任意参数*/
@Pointcut("execution(* com.ssm.controller..*.*(..))")
public void cut() {
}
//4.定义通知方法,方法上添加对应的注解,
//前置通知,设置前置通知监管的目标方法
//如果被监管的目标方法有参数需要获取,可以在通知
//方法中定义一个 JoinPoint 的形参,通过这个参数
//可以拿到目标方法的相关数据,注意点 JoinPoint
//这个参数一定要放在形参的第一个位置,否则报错
@Before("cut()")
public void before(JoinPoint joinPoint) {
//获取目标方法签名
Signature signature = joinPoint.getSignature();
//获取目标方法签名获取方法参数
Object [] args = joinPoint.getArgs();
//通过目标方法签名获取目标方法名
String name = signature.getName();
System.out.println("前置通知方法执行,监管了"+name);
}
//返回通知,目标方法如果出现异常没有执行完毕,该通知方法不会执行
//@AfterReturning("cut()")
//如果被监管的方法有返回值,需要获取方法执行后的返回值,可以返回通知注解中
//设置 returning 属性,属性值是返回的数据名,然后在方法形参中定义与该值同名的接收对象
@AfterReturning(value = "cut()", returning = "result")
public void afterReturn(JoinPoint joinPoint, Object result) {
System.out.println("返回通知,正常完毕会执行该方法");
}
//后置通知,不管目标类方法是否发生异常,该通知方法都会执行
@After("cut()")
public void after() {
System.out.println("后置通知,不管是否执行完毕,都会执行");
}
//异常通知目标类方法执行时发生异常执行,
//throwings 代表目标方法抛出的异常,注解中 throwing的值要与方法中形参相同
@AfterThrowing(value = "cut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
System.out.println("异常通知,发生异常时执行该终止" + e);
}
//环绕通知,目标方法执行前后,都可以做出相应的处理
//环绕通知方法中需要传入一个ProceedingJoinPoint对象,该对象可以看为被拦截的方法对象
//在该通知方法中,通过 ProceedingJoinPoint 对象,手动的调用目标方法,
//这样就可以在目标方法前后添加其它操作了
//注意环绕通知与异常通知配合使用时,环绕通知中的逻辑处理如果异常,方法执行后的代码会失效
@Around("cut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知的前置执行");
//通过环绕通知中传入的ProceedingJoinPoint对象可以获取目标类对象中的所有数据
//pjp.getTarget()方法获取到的是目标类对象的内存信息(可以理解为可以获取到目标对象)
//pjp.getSignature()方法可以获取到被代理类的方法
System.out.println(pjp.getTarget() + "----" + pjp.getSignature());
//只需目标方法,被拦截方法执行后执行?(为什么注释了该方法还是会执行后面的)
Object result = pjp.proceed();
System.out.println("环绕通知的后置执行");
return result;
}
}
- 此时如果 TestController2 类中的 aoptest() 方法,被切入点扫描,如果该方法执行,对应的通知方法也会执行
二. 通过 @EnableAspectJAutoProxy 了解 AOP 原理
- @EnableAspectJAutoProxy 注解方式,开启 Spring 的 AOP 功能,查看该注解的源码
1. 分析 @EnableAspectJAutoProxy
- 在@EnableAspectJAutoProxy中使用 @Import()向容器中注入了 AspectJAutoProxyRegistrar 类 ,查看 AspectJAutoProxyRegistrar 类,通过该类中的 registerBeanDefinitions() 方法,使用 @Import() 向容器中注入一个 AnnotationAwareAspectJAutoProxyCreator 这是一个注解模式装配切面的自动代理创建器(注入过程了解@Import注入的几种方式)
- 分析 AnnotationAwareAspectJAutoProxyCreator 类的继承关系,该类间接实现了SmartInstantiationAwareBeanPostProcessor 与 BeanFactoryAware接口
- 注意点 SmartInstantiationAwareBeanPostProcessor 继承了InstantiationAwareBeanPostProcessor 继承了BeanPostProcessor,InstantiationAwareBeanPostProcessor 与BeanPostProcessor不同的是增加了一个 postProcessBeforeInstantiation(),抽象方法,这个方法是在被监管的bean创建前执行的,这些后置处理器相关的方法在 AnnotationAwareAspectJAutoProxyCreator 的父类 AbstractAutoProxyCreator 中进行了实现,并且 bean 初始化前执行的方法 postProcessBeforeInitialization() 没做任何操作,直接进行了返回
- 类间接实现 BeanFactoryAware 接口,在AbstractAutoProxyCreator父类中重写 了setBeanFactory()方法,获取容器中的 BeanFactory
AnnotationAwareAspectJAutoProxyCreator
---->|AnnotationAwareAspectJAutoProxyCreator
--------->|AbstractAdvisorAutoProxyCreator
--------------->|AbstractAutoProxyCreator
-------------------------->| ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
(其中 SmartInstantiationAwareBeanPostProcessor 又继承了 InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor又继承了BeanPostProcessor)
2.复习 Spring 容器启动后 bean 的注入执行过程,了解 AnnotationAwareAspectJAutoProxyCreator 的注入
- 复习Spring注入bean过程:
- Spring启动容器时会调用refresh()方法刷新容器, 先创建BeanFactory,BeanPostProcessor,最终创建其它单实例bean注入到容器中
- 在创建bean以前先调用getBean(),getSingleton()方法,通过beanName在容器中获取,如果获取不到,执行createBean()创建
- 在createBean()方法内首先会执行 resolveBeforeInstantiation(beanName, mbdToUse)方法,尝试创建当前bean的代理类并返回,如果不能则继续向下执行(AOP重点关注的步骤)
- 执行doCreateBean()方法创建当前bean实例, 创建完成后会执行populateBean()方法,对当前创建的bean的属性赋值,
- populateBean()执行完成后,执行initializeBean()方法,获取所有后置处理器,执行初始化bean先后需要执行的方法
- initializeBean()中有四个方法需要关注 (AOP重点关注的步骤)
- invokeAwareMethods() ,通过该方法,判断要初始化的bean 是否是某个Aware类型,通过执行接口中的setXX()方法(Aware,通常用来获取 Spring 提供的组件,通过重写的抽象方法赋值给当前初始化的 bean AOP重点关注步骤)
- applyBeanPostProcessorsBeforeInitialization() 通过该方法获取容器中的BeanPostProcessor,执行初始化bean前需要执行的方法postProcessBeforeInitialization()
- invokeInitMethods() 方法,通过该方法执行初始化方法(例如调用InitializingBean的afterPropertiesSet,调用自定义的init-method 等)
- applyBeanPostProcessorsAfterInitialization() 通过该方法获取容器中的BeanPostProcessor,执行初始化bean后需要执行的方法postProcessAfterInitialization() (AOP重点关注步骤)
- Spring 默认提供了一个实现BeanPostProcessor 接口的ApplicationContextAwareProcessor 后置处理器,每个bean的创建都会被该后置处理器监控到,执行初始化bean之前要执行invokeAwareInterfaces() 方法,判断当前要创建的 bean 是否是 Aware 类型,如果是,调用这个 bean 重写的Aware中的抽象方法,用来获取 Spring 的组件赋值给当前要创建的bean对象
- 使用@EnableAspectJAutoProxy开启AOP又向容器中注入了一个 AnnotationAwareAspectJAutoProxyCreator 间接继承了 BeanFactoryAware与BeanPostProcessor接口,在创建这个bean时,被invokeAwareInterfaces() 判断到,方法中调用重写BeanFactoryAware 接口中的 setBeanFactory() 方法获取 Spring 提供的 BeanFactory,自此 AnnotationAwareAspectJAutoProxyCreator 持有 BeanFactory 先一步注入到了 Spring容器中
3.通过 AnnotationAwareAspectJAutoProxyCreator 生成代理对象
- 此时 AnnotationAwareAspectJAutoProxyCreator 已经创建注入到了容器中,开始创建其它 bean
- 了解Spring注入bean过程后,在执行createBean()方法,创建bean示例前首先会执行一个resolveBeforeInstantiation()尝试创建代理类
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
Class<?> resolvedClass = this.resolveBeanClass(mbd, beanName, new Class[0]);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
try {
mbdToUse.prepareMethodOverrides();
} catch (BeanDefinitionValidationException var7) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", var7);
}
Object beanInstance;
try {
//======通过后置处理器,创建 当前需要创建的 bean 的代理对象并返回================
beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse);
//==========================================================================
if (beanInstance != null) {
return beanInstance;
}
} catch (Throwable var8) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", var8);
}
//=============创建 bean ===============
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
//======================================================
if (this.logger.isDebugEnabled()) {
this.logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = this.determineTargetType(beanName, mbd);
if (targetType != null) {
bean = this.applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = this.applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = bean != null;
}
return bean;
}
- resolveBeforeInstantiation() 方法中调用了: applyBeanPostProcessorsBeforeInstantiation()
- applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);该方法会获取容器中所有后置处理器,遍历判断获取到的后置处理器是否有 InstantiationAwareBeanPostProcessor 类型的,如果有执行该后置处理器的 postProcessBeforeInstantiation(beanClass, beanName),通过该方法创建出当前要创建的 bean 的代理类对象并返回
- AOP 中 通过 @EnableAspectJAutoProxy 向容器中注入的 AnnotationAwareAspectJAutoProxyCreator 间接继承了 InstantiationAwareBeanPostProcessor 接口,判断通过,执行 postProcessBeforeInstantiation() 方法
postProcessBeforeInstantiation() 后置处理器中,创建 bean 对象前自动执行的方法
- Spring注入bean 执行 resolveBeforeInstantiation() 方法,该方法中调用了AnnotationAwareAspectJAutoProxyCreator 实现的postProcessBeforeInstantiation(),查看该方法
- 判断要创建的bean 是否在 advisedBeans 中, advisedBeans中保存了所有要增强的bean
- 判断当前要创建的bean 是否是基础类型的,是否是切面类(被切入点拦截的则需要代理增强)
- 判断是否需要跳过,shouldSkip() 调用的是AspectJAwareAdvisorAutoProxyCreator 中的,什么时候需要跳过,例如创建的bean是通知方法包装的增强器时…
-
自己理解该方法中判断当前创建的bean是否需要增强,不需要则将创建的bean存入advisedBeans集合中,并标识为false,后续创建这个bean时,就不会对这个bean进行代理处理
- 该方法执行完毕后,没有创建bean,createBean() 方法继续向下执行调用doCreateBean() 方法,调用目标类对象构造器,创建bean,
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = this.getCacheKey(beanClass, beanName);
if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
//adviseBeans中存储是否需要为某个Bean生成代理,如果里面存在cacheKey,
//说明之前已经初始化过Bean的代理类了,此时不需要再次初始化
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
//标识不代理bean,
//不代理的条件一:不是 Advice、Pointcut、Advisor、AopInfrastructureBean类的子类(切面类)
//不代理的条件二:shouldSkip返回 true,默认返回 false
//不代理的条件三:AspectJAwareAdvisorAutoProxyCreator:应该跳过所有 AspectJPointcutAdvisor指定的增强 bean
if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
if (beanName != null) {
TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.add(beanName);
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
}
return null;
}
postProcessAfterInitialization()后置处理器后置方法
- 上方Spring通过执行resolveBeforeInstantiation()并没有创建出bean的代理类,而是判断当前bean是否可以代理增强,将不可以被代理增强的bean存入advisedBeans中并标识为false,然后createBea()方法继续向下执行,调用doCreateBean()方法,调用构造器创建bean
- Spring创建bean成功后,执行populateBean()方法,对当前创建的bean的属性赋值,然后执行initializeBean(),获取BeanPostProcessor,执行初始化bean前后要执行的方法postProcessBeforeInitialization()与postProcessAfterInitialization()
- 查看 AnnotationAwareAspectJAutoProxyCreator重写的postProcessBeforeInitialization()方法,直接进行了返回没有其它操作
- 查看AnnotationAwareAspectJAutoProxyCreator重写的postProcessAfterInitialization()
- 该方法中通过advisedBeans获取当前创建的bean是否需要代理增强,不需要则跳过
- 需要代理增强的获取增强器(也就是需要切入到当前bean的通知方法)
- 修改代理增强标识为已增强
- 调用createProxy()方法创建当前bean的代理类
- 返回被代理bean
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
//判断是否已经代理过了
if (!this.earlyProxyReferences.contains(cacheKey)) {
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
//判断是否是已经在advisedBeans中集合中,该集合中存放了被标识为不用增强的和已经增强过的bean
} else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
//判断是否是切面类
} else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
//获取当前bean可以使用的增强器也就是通知方法,并根据前置通知,后置通知...对增强器进行排序(通过切入点匹配监管当前bean的通知方法)
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
if (specificInterceptors != DO_NOT_PROXY) {
//记录当前bean 已被增强处理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建当前 bean 的代理对象
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
createProxy() 代理类的创建
- createProxy()逻辑概述
- 首先会创建一个代理工厂ProxyFactory
- 增强器(通知方法),设置到ProxyFactory
- 执行createAopProxy()创建代理对象,有接口使用JDK动态代理,没有接口使用Cglib 动态代理 ObjenesisCglibAopProxy(config)
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);
}
//创建一个代理创建工厂
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (this.shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
} else {
this.evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//获取监管当前bean的所有增强器通知方法
Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
Advisor[] var7 = advisors;
int var8 = advisors.length;
for(int var9 = 0; var9 < var8; ++var9) {
Advisor advisor = var7[var9];
//将增强器保存到代理工厂中
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
this.customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (this.advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//获取代理对象
return proxyFactory.getProxy(this.getProxyClassLoader());
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
//JDK 动态代理
return new JdkDynamicAopProxy(config);
} else {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
} else {
//Cglib 动态代理 ObjenesisCglibAopProxy(config)
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
}
}
}
4. 目标方法的执行
- 在执行目标方法时,获取目标方法的所有增强器通知方法,将增强器包装为 MethodInterceptor 拦截器链,也就是获取所有拦截器,执行时判断目标方法否有拦截器拦截,如果没有拦截器,或执行到了目标方法位置,通过反射,执行目标方法,如果有拦截器,执行拦截器
- 实际根据JDK动态代理,Cgilb动态代理,不同的代理方式,底层执行的逻辑不太一样,但是两个代理的实现方式都差不多,创建方法调用链,不同的是jdk的动态代理创建的是ReflectiveMethodInvocation调用链,而cglib创建的是Cglib MethodInvocation。
三. AOP 总结
AOP 的搭建步骤
- Spring 中引入 AOP 相关依赖
- Spring 设置开启 AOP
- 创建 AOP 切面类,将界面类注入到容器中
- 切面类中编写通知方法
- 设置切入点,将通知方法织组目标方法
AOP 原理
- 通过 @EnableAspectJAutoProxy 注解设置 Spring 开启 AOP 了解到,在使用 AOP 时,会向 Spring 容器中注入一个AnnotationAwareAspectJAutoProxyCreator 后置处理器, AnnotationAwareAspectJAutoProxyCreator 间接实现了 InstantiationAwareBeanPostProcessor 与 BeanFactoryAware 接口
InstantiationAwareBeanPostProcessor 继承了 BeanPostProcessor 接口,与 BeanPostProcessor 不同的是,该接口中增加了一个 postProcessBeforeInstantiation() 抽象方法, 该方法是监控在创建bean以前执行的
- 分析 AnnotationAwareAspectJAutoProxyCreator 间接实现 BeanFactoryAware 接口, 是为了拿到 Spring 提供的BeanFactory ,因为 Spring 在提供容器时默认提供了一个 ApplicationContextAwareProcessor 后置处理器,注入到了容器中,在后续初始化 bean 时,都会被这个后置处理器监控到执行初始化的方法,判断当前要创建的 bean 是否是InstantiationAwareBeanPostProcessor 类型,如果是执行invoke() 方法,判断当前要创建的 bean 是否实现了某个 Aware 接口,如实现了,调用重写的该接口中的抽象方法获取, 获取Spring 提供的组件,此处获取的是 BeanFactory
- 分析 AnnotationAwareAspectJAutoProxyCreator 间接继承 InstantiationAwareBeanPostProcessor 接口,当前这个类就就是一个后置处理器,在该类继承的 AbstractAutoProxyCreator 抽象父类中实现了后置处理器监控 bean 的创建前执行的方法,初始化前执行的方法与初始化后执行的方法
- Spring 容器在创建时调用 refresh() 方法刷新容器,方法中创建获取创建 bean 时,首先调用registerBeanPostProcessors() 方法,将直接或间接继承了 BeanPostProcessor 接口的 bean 创建注入到容器中,也就是 IOC 容器在创建注入 bean 时会首先创建注入后置处理器,然后调用 finishBeanFactoryInitialization() 方法创建其他所有单例对象,自此 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器,就先一步获取到 BeanFactory 创建注入到了容器中
- 后续再创建其它 bean 时,在creatBean方法中(),首先会调用 resolveBeforeInstantiation(beanName, mbdToUse) 方法,获取所有后置处理器,循环判断是否存在InstantiationAwareBeanPostProcessor 这个类型的后置处理器 AnnotationAwareAspectJAutoProxyCreator 是该类型的实现子类,如果是调用执行创建 bean 以前的方法 postProcessBeforeInstantiation()
- postProcessBeforeInstantiation() 创建 bean 以前执行的方法 : 该方法在 AOP 相关的处理中并没有太多实际的动作,只是判断当前 bean 是否是切面类,是否是增强器(也就是切面类中的通知方法,在Spring 容器启动时,会将切面中的通知方法封装成一个个的增强器),是否允许被增强,如果不允许增强,将当前类放入一个集合中 advisedBeans,中并标识为false,不允许增强
- postProcessBeforeInitialization() 初始化 bean 以前执行的方法,该方法在此处没有任何操作,直接进行了返回
- postProcessAfterInitialization() 初始化 bean 后执行的方法,该方法中调用判断当前要创建的 bean 是否允许增强处理,是否已经被增强处理,创建ProxyFactory代理类工厂,通过切入点匹配获取监管当前 bean 的所有增强器(通知方法)将获取到的通知方法存入代理工厂中,通过 JDK 动态代理,或 Gglib 动态代理生成代理对象
- 执行 在执行目标方法时,获取目标方法的所有增强器通知方法,将增强器包装为 MethodInterceptor 拦截器链,如果有拦截器执行拦截器,如果没有通过反射执行目标方法?
AOP问题点
SpringBoot AOP的动态代理
- Spring中AOP会判断是否有接口,有接口使用JDK动态代理否则使用Cglib动态代理
- 查看SpringBoot AOP配置类为AopAutoConfiguration,该配置类中通过@ConditionalOnProxperty注解指定如果spring.aopproxy-target-class不指定,默认值为true,使用Cglib动态代理,如果指定spring.aopproxy-target-class=false才会判断是否有接口,有接口使用JDK动态代理否则使用Cglib动态代理
AOP 底层使用的什么机制
- 动态代理
AOP 常见的概念名词
- 切面: 指切面类,管理切入点与通知方法
- 连接点: 需要增强的方法就是连接点
- 切入点: 由切入点来哪些方法需要增强,哪些方法不需要增强,@Pointcut指定切点表达式
- 通知: 就是需要加入到业务方法中的公共代码,其中包含
- 前置通知 @Before 目标方法执行前运行
- 后置通知 @After 目标方法执行完毕,或抛出异常时运行
- 返回通知 @AfterReturning 目标方法正常返回之后运行
- 异常通知 @AfterThrowing 目标方法发生异常时运行
- 环绕通知 @Around
- 目标对象: 增强的对象,需要增强的方法所在类
- 织入: AspectJ独有的,Spring使用的织入方式是动态代理,也就是为目标对象创建动态代理的过程
- 顾问: 是通知的一种包装体现,是切入点与通知的一个结合,用来管理切入点与通知,应用中无需关心
- 引入: 可以将其它接口和实现动态引入到targetClass中
Spring AOP与AspectJ AOP有什么区别
Spring中要使用AOP时需要引入AspectJ AOP相关依赖,SpringAOP提供了AspectJ AOP的支持,但是只用到了AspectJ的切入点解析和匹配,AOP基于代理模式实现,分为动态代理与今天代理,静态代理表示AspectJ,动态代理表示SpringAOP,是主要区别:
- SpringAOP基于动态代理实现,如果目标类使用的接口则通过JDK动态代理实现,如果没有使用Cglib实现
- AspectJ是基于静态代理,在编译阶段生成AOP代理类,因此又被称为编译时增强
JDK动态代理与Cglib动态代理的区别
- 首先解释一下Spring怎么实现动态代理: 当Spring中的Bean有接口时,Spring则使用JDK动态代理,当被代理Bean没有实现接口时,Spring使用Cglib动态代理,也可以强制使用Cglib代理,在Spring配置文件中添加
< aop:aspectj-autoproxy proxy-target-cass=“true”/>
- jdk动态代理: 实现 InvocationHandler 接口,重写invoke方法,通过Proxy.newProxyInstance()获取代理对象
- cglib动态代理: 需要引入依赖 ,实现MethodInterceptor 接口,通过 重写intercept()方法实现代理执行目标类方法
- JDK动态代理
- JDK动态代是基于反射实现的,被代理类需要有接口,被代理执行的方法要在接口中,并且可以被重写,否则不能动态代理,运行时JDK动态的为目标接口创建一个$proxy*.class,调用时先执行处理类进行增强,再通过反射调用目标方法
- 动态代理中的重点主要是通过Proxy.newProxyInstance()方法在内存中动态生成代理对象,通过InvocationHandler接口中的invoke()方法,通过反射,执行被代理的方法,根据两种不同方式的调用执行,区分实现InvocationHandler接口,与通过接口的匿名实现方式设计JDK动态代理的不同
- Cglib动态代理
- 底层通过ASM字节码,在运行时动态的生产目标类的子类,该方式是针对类的动态代理,通过子类覆盖父类,由于是子类继承父类的方式该类中的方法不可以使用final,static修饰,Cglib动态代理时除了会生成代理类以外还会生成一个FastClass类,通过这个FastClass解决了JDK动态代理本类方法调用增强失效的问题(Spring中不管是JDK动态代理,还是Cglib,本类调用代理方法增强都会失效)
- 那么JDK与Gglib动态代理有什么优缺点: JDK生成速度快调用慢,Cglib生成速度慢后续调用快,JDK好像进行了优化,JDK78版本性能比CGLIB好20%左右
如何强制使用Cglib动态代理
- xml方式: < aop:aspectj-autoproxy proxy-target-cass=“true”/>
- 注解方式@EnableAspectJAutoProxy时指定proxyTargetClass=true
AOP什么情况下代理失效,如何解决
- 常见失效原因: 方法是private权限,目标类没有配置为bean交给Spring管理,切入点配置问题,重点关注本类方法调用失效
- 本类中调用会失效,两种解决方式
- 自己注入自己的接口, 通过自己注入的发起调用
- 设置@EnableAspectJAutoProxy中的exposeProxy属性为true,表示在线程中暴露代理对象,底层会将动态代理对象存到ThreadLocal中,然后在方法中通过AopContext.currentProxy()方法获取当前线程中的代理对象,通过这个代理对象调用目标方法
Spring AOP是在哪里创建的动态代理
- 正常情况下动态代理的执行
- 首先在resolveBeforeInstantiation()方法中判断创建的bean是否需要代理增强,存到advisedBeans中
- doCreate(),populateBean实例化,属性赋值后,执行initializeBean()初始化bean获取BeanPostProcessor,执行postProcessAfterInitialization()时进行的动态代理
- postProcessAfterInitialization中会调用一个wrapIfNecessary(),根据切入点表达式进行匹配,匹配成功创建ProxyFactory,执行createAopProxy()创建代理对象
- 在属性注入时创建动态代理,在属性注入时如果发生循环依赖,当调用getEarlyBeanRefrence()通过缓存获取对象时,该方法中也会调用wrapIfNecessary()尝试创建代理对象,并且在wrapIfNecessary()方法中也会执行earlyProxyReferences.remove()方法,将属性注入发生循环依赖,已经动态代理过的对象删除掉,防止后续初始化时重复创建代理对象