一:AOP基本使用

AOP指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;

步骤一:导入aop模块;Spring AOP:(spring-aspects)

        <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>

步骤二:定义一个业务逻辑类;在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,环绕)

public class MathCalculator {
public int div(int i,int j){
System.out.println("MathCalculator...method...");
return i/j;
}
}

步骤三:定义一个日志切面类:切面类里面的方法需要动态感知业务逻辑类中需要切入的方法运行到哪里然后执行;

通知方法:
前置通知(@Before):logStart:在目标方法运行之前运行
后置通知(@After):logEnd:在目标方法运行结束之后运行(无论方法正常结束还是异常结束)
返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
@Aspect //步骤六
public class LogAspects { //抽取公共的切入点表达式
//1、本类引用
//2、其他的切面引用
@Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
public void pointCut(){}; //步骤4:@Before @After @AfterReturning @AfterThrowing //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
} @After("com.atguigu.aop.LogAspects.pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
} //JoinPoint一定要出现在参数表的第一位
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
} @AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
} }

步骤四:给切面类的目标方法标注何时何地运行(通知注解);

步骤五:将切面类和业务逻辑类(目标方法所在类)都加入到容器中;

@EnableAspectJAutoProxy  //步骤七
@Configuration
public class MainConfigOfAOP { //业务逻辑类加入容器中
@Bean
public MathCalculator calculator(){
return new MathCalculator();
} //切面类加入到容器中
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}

步骤六:必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)

步骤七:给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】==>等价于

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

二:通过注解解析AOP原理

1. @EnableAspectJAutoProxy是什么?

该注解通过@Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar;利用AspectJAutoProxyRegistrar自定义给容器中注册bean;返回BeanDefinetion

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);

因此internalAutoProxyCreator即为AnnotationAwareAspectJAutoProxyCreator;所以给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;

2. AnnotationAwareAspectJAutoProxyCreator的UML示例:
AnnotationAwareAspectJAutoProxyCreator
->AspectJAwareAdvisorAutoProxyCreator
->AbstractAdvisorAutoProxyCreator
->AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory

3. AOP的处理流程

1)传入配置类,创建ioc容器

2)注册配置类,调用refresh()刷新容器;

public AnnotationConfigApplicationContext(Class... annotatedClasses) {
this();
this.register(annotatedClasses);
this.refresh();
}

3)registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;

3.1)先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor;

3.2)给容器中加别的BeanPostProcessor

3.3)优先注册实现了PriorityOrdered接口的BeanPostProcessor;

3.4)再给容器中注册实现了Ordered接口的BeanPostProcessor;

3.5)注册没实现优先级接口的BeanPostProcessor;

3.6)注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;

创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】

3.6.1)创建Bean的实例

3.6.2)populateBean;给bean的各种属性赋值

3.6.3)initializeBean:初始化bean;

3.6.3.1)invokeAwareMethods():处理Aware接口的方法回调;

3.6.3.2)applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()

3.6.3.3)invokeInitMethods();执行自定义的初始化方法

3.6.3.4)applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();

3.6.4)BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;-->aspectJAdvisorsBuilder

3.7)把BeanPostProcessor注册到BeanFactory中;beanFactory.addBeanPostProcessor(postProcessor);

以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程

4)finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean

4.1)遍历获取容器中所有的Bean,依次创建对象getBean(beanName);

getBean->doGetBean()->getSingleton()->

4.2)创建bean

【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】

4.2.1)先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建; 只要创建好的Bean都会被缓存起来

4.2.2)createBean();创建bean; AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例

【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】 ;

【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】

4.2.2.1)resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续

4.2.2.1.1)、后置处理器先尝试返回对象;

bean = applyBeanPostProcessorsBeforeInstantiation():

拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;就执行postProcessBeforeInstantiation--->下面的作用部分

   	if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}

4.2.2.2)doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;

3.1 AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用:

1)每一个bean创建之前,调用postProcessBeforeInstantiation();关心MathCalculator和LogAspect的创建

1.1)判断当前bean是否在advisedBeans中(保存了所有需要增强bean)

1.2)判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
或者是否是切面(@Aspect) 1.3)是否需要跳过 1.3.1)获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true 1.3.2)永远返回false

2)创建对象

postProcessAfterInitialization;
return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
2.1)获取当前bean的所有增强器(通知方法)  Object[]  specificInterceptors

	2.1.1)找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)

	2.1.2)获取到能在当前bean使用的增强器。

	2.1.3)给增强器排序

2.2)保存当前bean在advisedBeans中;

*2.3)如果当前bean需要增强,创建当前bean的代理对象;

	2.3.1)获取所有增强器(通知方法)

	2.3.2)保存到proxyFactory

	*2.3.3)创建代理对象:Spring自动决定
JdkDynamicAopProxy(config);jdk动态代理;
ObjenesisCglibAopProxy(config);cglib的动态代理; 2.4)给容器中返回当前组件使用cglib增强了的代理对象; 2.5)以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;

3)目标方法执行;容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);

3.1)CglibAopProxy.intercept();拦截目标方法的执行

3.2)根据ProxyFactory对象获取将要执行的目标方法拦截器链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 3.2.1)List<Object> interceptorList保存所有拦截器 ,5个:
一个默认的ExposeInvocationInterceptor 和 4个增强器; 3.2.2)遍历所有的增强器,将其转为Interceptor;
registry.getInterceptors(advisor); 3.2.3)将增强器转为List<MethodInterceptor>;
如果是MethodInterceptor,直接加入到集合中
如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
转换完成返回MethodInterceptor数组; 3.3)如果没有拦截器链,直接执行目标方法;
拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制) 3.4)如果有拦截器链,把需要执行的目标对象,目标方法,
拦截器链等信息传入创建一个 CglibMethodInvocation 对象,并调用 Object retVal = mi.proceed(); 3.5)拦截器链的触发过程; 3.5.1)如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法; 3.5.2)链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;拦截器链的机制,保证通知方法与目标方法的执行顺序;

链接器链调用流程:

总结:

1) @EnableAspectJAutoProxy 开启AOP功能

2) @EnableAspectJAutoProxy会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator

3)AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;

4)、容器的创建流程:

4.1)registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
4.2)finishBeanFactoryInitialization()初始化剩下的单实例bean
4.2.1)创建业务逻辑组件和切面组件
4.2.2)AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
4.2.3)组件创建完之后,判断组件是否需要增强
是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);

5)执行目标方法:

5.1)代理对象执行目标方法
5.2)CglibAopProxy.intercept();
5.2.1)得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
5.2.2)利用拦截器的链式机制,依次进入每一个拦截器进行执行;
5.2.3)效果:
正常执行:前置通知-》目标方法-》后置通知-》返回通知
出现异常:前置通知-》目标方法-》后置通知-》异常通知

spring注解开发-AOP(含原理)的更多相关文章

  1. 浅尝Spring注解开发_AOP原理及完整过程分析(源码)

    浅尝Spring注解开发_AOP原理及完整过程分析(源码) 浅尝Spring注解开发,基于Spring 4.3.12 分析AOP执行过程及源码,包含AOP注解使用.AOP原理.分析Annotation ...

  2. Spring注解开发系列Ⅵ --- AOP&事务

    注解开发 --- AOP AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,横向重复,纵向抽取.详细的AO ...

  3. Spring注解开发_Spring容器创建概述

    浅尝Spring注解开发_Spring容器创建概述 浅尝Spring注解开发,基于Spring 4.3.12 概述Spring容器创建的过程,包括12个方法的执行 浅尝Spring注解开发_自定义注册 ...

  4. Annotation(三)——Spring注解开发

    Spring框架的核心功能IoC(Inversion of Control),也就是通过Spring容器进行对象的管理,以及对象之间组合关系的映射.通常情况下我们会在xml配置文件中进行action, ...

  5. spring注解开发中常用注解以及简单配置

    一.spring注解开发中常用注解以及简单配置 1.为什么要用注解开发:spring的核心是Ioc容器和Aop,对于传统的Ioc编程来说我们需要在spring的配置文件中邪大量的bean来向sprin ...

  6. Spring注解开发-全面解析常用注解使用方法之生命周期

    本文github位置:https://github.com/WillVi/Spring-Annotation/ 往期文章:Spring注解开发-全面解析常用注解使用方法之组件注册 bean生命周期 ​ ...

  7. Spring注解开发系列专栏

    这个系列主要是讲Spring注解的使用,可以为后面SpringBoot的学习带来一定的帮助.我觉得从Spring直接过度到SpringBoot还是有点快,还是得需要一个演变的过程.从Spring开发, ...

  8. 浅尝Spring注解开发_Bean生命周期及执行过程

    Spring注解开发 浅尝Spring注解开发,基于Spring 4.3.12 包含Bean生命周期.自定义初始化方法.Debug BeanPostProcessor执行过程及在Spring底层中的应 ...

  9. 浅尝Spring注解开发_Servlet3.0与SpringMVC

    浅尝Spring注解开发_Servlet 3.0 与 SpringMVC 浅尝Spring注解开发,基于Spring 4.3.12 Servlet3.0新增了注解支持.异步处理,可以省去web.xml ...

随机推荐

  1. Django View类的解析

    class View(object): """ Intentionally simple parent class for all views. Only impleme ...

  2. IT兄弟连 JavaWeb教程 Servlet 状态管理 会话跟踪

    HTTP协议是无状态的,我们的客户端与服务器的每一次请求与响应,我们服务器都没有记忆能力将客户端与服务器的多次交互数据进行存储与管理共有两种技术实现: ●  基于客户端实现:Cookie,将状态保存在 ...

  3. tyvj 1391 走廊泼水节【最小生成树】By cellur925

    题目传送门 题意简化:给你一棵树,要求你加边使它成为完全图(任意两点间均有一边相连) ,满足原来的树是这个图的最小生成树.求加边的价值最小是多少. 考虑Kruskal的过程,我们每次找一条最短的,两边 ...

  4. 跟我一起玩Win32开发(22):抓取屏幕

    关于如何拷贝屏幕并保存,这里已经有现成的例子,我也不必去Copy人家了,我一向不喜欢Copy.这里有一个完整的例子,可以看看. http://msdn.microsoft.com/EN-US/libr ...

  5. 跟我一起玩Win32开发(16):ListView的多个视图

    在上一个例子中,我们只用到了ListView的Report视图,也就是详细视图.本文我们再把上一篇文章中所用的例子进行一下扩展,例子源码可以到俺的资源区下载. 我们为ListView中显示的数据加上图 ...

  6. Hdu 5459 Jesus Is Here (2015 ACM/ICPC Asia Regional Shenyang Online) 递推

    题目链接: Hdu 5459 Jesus Is Here 题目描述: s1 = 'c', s2 = 'ff', s3 = s1 + s2; 问sn里面所有的字符c的距离是多少? 解题思路: 直觉告诉我 ...

  7. C# HashSet 用法[转]

    原文链接 .NET 3.5在System.Collections.Generic命名空间中包含一个新的集合类:HashSet<T>.这个集合类包含不重复项的无序列表.这种集合称为“集(se ...

  8. Reference for shell scripting

    ${var} 和 $var的区别 http://stackoverflow.com/questions/8748831/when-do-we-need-curly-braces-in-variable ...

  9. 关于MyBatis的两种写法

    刚接触MyBatis是在Jike的视频中学习的,但是之后又发现和项目中的MyBatis的用法不太一致.上网找了好多资料,发现网上的教程分为两种写法: 第一种,是jike视频中的写法,写好map.xml ...

  10. 为localhost添加https

    1.按照https://stackoverflow.com/a/7184031/4619958来做 其中,CommonName填写localhost 2.在ssl.conf里头加上 <Direc ...