Spring Aop深度解析
关于spring Aop也接触了好多知识了,在此,做一个总结,以便学习。
Spring Aop是什么?
AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。
基本术语:
(1)切面(Aspect)
切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
@Component
@Aspect
public class LogAspect {
}
可以简单地认为, 使用 @Aspect 注解的类就是切面
(2) 目标对象(Target)
目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。
(3) 连接点(JoinPoint)
程序执行过程中明确的点,如方法的调用或特定的异常被抛出。连接点由两个信息确定:
简单来说,连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。
@Before("pointcut()")
public void log(JoinPoint joinPoint) { //这个JoinPoint参数就是连接点
}
(4) 切入点(PointCut)
切入点是对连接点进行拦截的条件定义。切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。
一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配连接点,给满足规则的连接点添加通知。
@Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))")
public void pointcut() {
}
上边切入点的匹配规则是com.remcarpediem.test.aop.service包下的所有类的所有函数。
(5) 通知(Advice)
通知是指拦截到连接点之后要执行的代码,包括了“around”、“before”和“after”等不同类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以连接点为中心的拦截器链。
// @Before说明这是一个前置通知,log函数中是要前置执行的代码,JoinPoint是连接点,
@Before("pointcut()")
public void log(JoinPoint joinPoint) {
}
通知方法:
前置通知:在我们执行目标方法之前运行(@Before)
后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)
返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)
异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)
环绕通知:动态代理, 需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)
代码展示:
切面类:
package Aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
/**
* @author 孙一鸣 on 2020/2/22
*/
@Aspect
public class LogAspects {
@Pointcut("execution(public int Aop.Calculator.*(..))")
public void pointCut(){};
//@before代表在目标方法执行前切入, 并指定在哪个方法前切入
@Before("pointCut()")
public void logStart(){
System.out.println("除法运行....参数列表是:{}");
}
@After("pointCut()")
public void logEnd(){
System.out.println("除法结束......");
}
@AfterReturning("pointCut()")
public void logReturn(){
System.out.println("除法正常返回......运行结果是:{}");
}
@AfterThrowing("pointCut()")
public void logException(){
System.out.println("运行异常......异常信息是:{}");
}
@Around("pointCut()")
public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("@Arount:执行目标方法之前...");
Object obj = proceedingJoinPoint.proceed();//相当于开始调div地
System.out.println("@Arount:执行目标方法之后...");
return obj;
}
}
业务逻辑方法:
package Aop;
/**
* @author 孙一鸣 on 2020/2/22
*/
public class Calculator {
//业务逻辑方法
public int div(int i, int j) {
System.out.println("----业务方法:----");
return i / j;
}
}
配置bean:
package Aop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @author 孙一鸣 on 2020/2/22
*/
@Configuration
@EnableAspectJAutoProxy //开启注解版Aop功能
public class Config1 {
@Bean
public Calculator calculator(){
return new Calculator();
}
@Bean
public LogAspects aspects(){
return new LogAspects();
}
}
测试类:
package Aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author 孙一鸣 on 2020/2/22
*/
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config1.class);
Calculator calculator = (Calculator) context.getBean("calculator");
calculator.div(12,2);
}
}
结果截图:
Aop原理研究:
对于Aop的原理,我们应该从Aop的注解 @EnableAspectJAutoProxy来研究。
1.@EnableAspectJAutoProxy是什么?
我们进入这个注解来看:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
我们可以看到注解中要导入一个名为:AspectJAutoProxyRegistrar的组件。
进入这个组件:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
AspectJAutoProxyRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
ImportBeanDefinitionRegistrar 这个接口,在以前的学习中使用过,ImportBeanDefinitionRegistrar手动注册bean到容器中,通过@import(HelloBeanRegister.class)使用
到这里,我们大致梳理一下步骤
- 使用注解@EnableAspectJAutoProxy
- 注解中import导入组件 AspectJAutoProxyRegistrar
- 组件中要注册Bean
- 注册了一个什么样的对象呢? AnnotationAwareAspectJAutoProxyCreator.class
- 研究这个注解给容器中注册的这个组件的功能?
我们仔细看一下组件AspectJAutoProxyRegistrar是怎样注册Bean?
组件中实现了接口ImportBeanDefinitionRegistrar,定义了注册方法:registerBeanDefinitions(){
}
进入registerBeanDefinitions方法:
第一句:
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
首先调用了Aop配置工具类里的方法
方法名解释:如果需要的话注册切面注解自动代理创建器
进入AopConfigUtils类
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null);
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
//给容器中注册了一个注解 装配模式下切面自动代理创建器
}
进入registerOrEscalateApcAsRequired方法:
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator"))
//首先判断容器中是否含有internalAutoProxyCreator 这个Bean的定义信息
{
BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
//如果有得到internalAutoProxyCreator这个Bean的信息
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
} else {//如果容器中没有internalAutoProxyCreator 这个Bean的定义信息
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", -2147483648);
beanDefinition.setRole(2);
//将这个internalAutoProxyCreator Bean注册进容器
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
return beanDefinition;
}
}
研究这个组件的功能?AnnotationAwareAspectJAutoProxyCreator.class
进入AnnotationAwareAspectJAutoProxyCreator类
继承了AspectJAwareAdvisorAutoProxyCreator
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
类图详细分析:
到这里,我们大致梳理一下步骤
这是前面的步骤:
- 使用注解@EnableAspectJAutoProxy
- 注解中import导入组件 AspectJAutoProxyRegistrar
- 组件中要注册Bean
- 注册了一个什么样的对象呢? AnnotationAwareAspectJAutoProxyCreator.class
- 研究这个注解给容器中注册的这个组件的功能?
===========================================
新发现: - 通过类图知道,这个组件具有BeanPostProcessor的特点,也有BeanFactoryAware的特点,说明这个Bean组件AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器,也是一个Aware接口的实现类
分析AnnotationAwareAspectJAutoProxyCreator的关于后置处理器的方法
首先来看AnnotationAwareAspectJAutoProxyCreator 继承的最顶层父类:1.AbstractAutoProxyCreator
2.AbstractAdvisorAutoProxyCreator
3.父类:AspectJAwareAdvisorAutoProxyCreator
4.自己:AnnotationAwareAspectJAutoProxyCreator
Debug模式学习源码:
创建和注册AnnotationAwareAspectJAutoProxyCreator的过程流程:
1)、传入配置类,创建ioc容器
2)、注册配置类,调用refresh()刷新容器;
3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;
- 1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
2)、给容器中加别的BeanPostProcessor
3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
5)、注册没实现优先级接口的BeanPostProcessor;
6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】-
1)、创建Bean的实例 2)、populateBean;给bean的各种属性赋值 3)、initializeBean:初始化bean; 1)、invokeAwareMethods():处理Aware接口的方法回调 2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization() 3)、invokeInitMethods();执行自定义的初始化方法 4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization(); 4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder
beanFactory.addBeanPostProcessor(postProcessor); -
AnnotationAwareAspectJAutoProxyCreator => 后置处理器类型InstantiationAwareBeanPostProcessor
4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
- 1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
getBean->doGetBean()->getSingleton()->
2)、创建bean
【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】
2.1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;
只要创建好的Bean都会被缓存起来
2.2)、createBean();创建bean;
AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例
【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】
1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
1)、后置处理器先尝试返回对象;
bean = applyBeanPostProcessorsBeforeInstantiation():
拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
就执行postProcessBeforeInstantiation
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
关心MathCalculator和LogAspect的创建
-
1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
-
2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
-
3)、是否需要跳过 1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】 每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor; 判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true 2)、永远返回false
2)、创建对象
-
postProcessAfterInitialization;
3)、目标方法执行 ;
容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
1)、CglibAopProxy.intercept();拦截目标方法的执行
2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;- List< Object> chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
1)、List< Object> interceptorList保存所有拦截器 5
一个默认的ExposeInvocationInterceptor 和 4个增强器;
2)、遍历所有的增强器,将其转为Interceptor;
registry.getInterceptors(advisor);
3)、将增强器转为List;
如果是MethodInterceptor,直接加入到集合中
如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
转换完成返回MethodInterceptor数组;
3)、如果没有拦截器链,直接执行目标方法;
拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
4)、如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个 CglibMethodInvocation 对象, 并调用 Object retVal = mi.proceed();
5)、拦截器链的触发过程;
- 1)、如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
- 2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
拦截器链的机制,保证通知方法与目标方法的执行顺序;
- List< Object> chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
总结:
1)@EnableAspectJAutoProxy 开启AOP功能
2) @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
3)AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
4)容器的创建流程:
-
1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
2)、finishBeanFactoryInitialization()初始化剩下的单实例bean1)、创建业务逻辑组件和切面组件 2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程 3)、组件创建完之后,判断组件是否需要增强 是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
5)执行目标方法:</font>
-
1)、代理对象执行目标方法
2)、CglibAopProxy.intercept();1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor) 2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行; 3)、效果: 正常执行:前置通知-》目标方法-》后置通知-》返回通知 出现异常:前置通知-》目标方法-》后置通知-》异常通知