>>分享流行的Java框架以及开源软件,对孙卫琴的《精通Spring:Java Web开发技术详解》提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 22264 个阅读者 刷新本主题
 * 贴子主题:  Spring如何实现AOP,请不要再说cglib了! 回复文章 点赞(0)  收藏  
作者:flybird    发表时间:2020-10-16 03:53:15     消息  查看  搜索  好友  邮件  复制  引用

     1. 从注解入手找到对应核心类  最近工作中我都是基于注解实现AOP功能,常用的开启AOP的注解是@EnableAspectJAutoProxy,我们就从它入手。    
  
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

上面的动图的流程的步骤就是:  @EnableAspectJAutoProxy  --> AspectJAutoProxyRegistrar  -->AopConfigUtils .registerAspectJAnnotationAutoProxyCreatorIfNecessary  -->AnnotationAwareAspectJAutoProxyCreator.class  

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

虽然找到了核心类,但是并没有找到核心方法!下面我们尝试画类图确定核心方法。  2.画核心类类图,猜测核心方法  AnnotationAwareAspectJAutoProxyCreator的部分类图。  

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

从类图看到了AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor,而AOP功能应该在创建完Bean之后执行,猜测AnnotationAwareAspectJAutoProxyCreator实现BeanPostProcessor的postProcessAfterInitialization(实例化bean后处理)是核心方法。 查看AnnotationAwareAspectJAutoProxyCreator实现的postProcessAfterInitialization方法,实际该方法在其父类AbstractAutoProxyCreator中。  
  
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

发现发现疑似方法wrapIfNecessary,查看其源码如下,发现createProxy方法。确定找对了地方。  
  
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

即AnnotationAwareAspectJAutoProxyCreator实现BeanPostProcessor的postProcessAfterInitialization方法,在该方法中由wrapIfNecessary实现了AOP的功能。 wrapIfNecessary中有2个和核心方法  getAdvicesAndAdvisorsForBean获取当前bean匹配的增强器  createProxy为当前bean创建代理  要想明白核心流程还需要分析这2个方法。  3.读重点方法,理核心流程  3.1 getAdvicesAndAdvisorsForBean获取当前bean匹配的增强器  查看源码如下,默认实现在AbstractAdvisorAutoProxyCreator中。  


点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

查阅findEligibleAdvisors方法,就干了3件事  找所有增强器,也就是所有@Aspect注解的Bean  找匹配的增强器,也就是根据@Before,@After等注解上的表达式,与当前bean进行匹配,暴露匹配上的。  对匹配的增强器进行扩展和排序,就是按照@Order或者PriorityOrdered的getOrder的数据值进行排序,越小的越靠前。  


点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

AnnotationAwareAspectJAutoProxyCreator 重写了findCandidateAdvisors,下面我们看看具体实现了什么  3.1.1findCandidateAdvisors找所有增强器,也就是所有@Aspect注解的Bean  


点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

从该方法我们可以看到处理@Aspect注解的bean的方法是:this.aspectJAdvisorsBuilder.buildAspectJAdvisors()。 这个方法如下:  public List<Advisor> buildAspectJAdvisors() {  ?  List<String> aspectNames = this.aspectBeanNames;  ?  if (aspectNames == null) {  ? ? ? synchronized (this) {  ? ? ? ?  aspectNames = this.aspectBeanNames;  ? ? ? ?  if (aspectNames == null) {  ? ? ? ? ? ? List<Advisor> advisors = new ArrayList<>();  ? ? ? ? ? ? aspectNames = new ArrayList<>();  ? ? ? ? ? ? //找到所有BeanName? ? ? ? ? ? String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(  ? ? ? ? ? ? ? ? ? this.beanFactory, Object.class, true, false);  ? ? ? ? ? ? for (String beanName : beanNames) {  ? ? ? ? ? ? ?  if (!isEligibleBean(beanName)) {  ? ? ? ? ? ? ? ? ? continue;  ? ? ? ? ? ? ?  }  ? ? ? ? ? ? ?  // 必须注意,bean会提前暴露,并被Spring容器缓存,但是这时还不能织入。? ? ? ? ? ? ?  Class<?> beanType = this.beanFactory.getType(beanName);  ? ? ? ? ? ? ?  if (beanType == null) {  ? ? ? ? ? ? ? ? ? continue;  ? ? ? ? ? ? ?  }  ? ? ? ? ? ? ?  if (this.advisorFactory.isAspect(beanType)) {  ? ? ? ? ? ? ? ? ? //找到所有被@Aspect注解的类? ? ? ? ? ? ? ? ? aspectNames.add(beanName);  ? ? ? ? ? ? ? ? ? AspectMetadata amd = new AspectMetadata(beanType, beanName);  ? ? ? ? ? ? ? ? ? if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {  ? ? ? ? ? ? ? ? ? ?  MetadataAwareAspectInstanceFactory factory =  ? ? ? ? ? ? ? ? ? ? ? ? ?  new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);  ? ? ? ? ? ? ? ? ? ?  //解析封装为Advisor返回? ? ? ? ? ? ? ? ? ?  List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);  ? ? ? ? ? ? ? ? ? ?  if (this.beanFactory.isSingleton(beanName)) {  ? ? ? ? ? ? ? ? ? ? ? ? this.advisorsCache.put(beanName, classAdvisors);  ? ? ? ? ? ? ? ? ? ?  }  ? ? ? ? ? ? ? ? ? ?  else {  ? ? ? ? ? ? ? ? ? ? ? ? this.aspectFactoryCache.put(beanName, factory);  ? ? ? ? ? ? ? ? ? ?  }  ? ? ? ? ? ? ? ? ? ?  advisors.addAll(classAdvisors);  ? ? ? ? ? ? ? ? ? }  ? ? ? ? ? ? ? ? ? else {  ? ? ? ? ? ? ? ? ? ?  // Per target or per this.? ? ? ? ? ? ? ? ? ?  if (this.beanFactory.isSingleton(beanName)) {  ? ? ? ? ? ? ? ? ? ? ? ? throw new IllegalArgumentException("Bean with name '" + beanName +  ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "' is a singleton, but aspect instantiation model is not singleton");  ? ? ? ? ? ? ? ? ? ?  }  ? ? ? ? ? ? ? ? ? ?  MetadataAwareAspectInstanceFactory factory =  ? ? ? ? ? ? ? ? ? ? ? ? ?  new PrototypeAspectInstanceFactory(this.beanFactory, beanName);  ? ? ? ? ? ? ? ? ? ?  this.aspectFactoryCache.put(beanName, factory);  ? ? ? ? ? ? ? ? ? ?  advisors.addAll(this.advisorFactory.getAdvisors(factory));  ? ? ? ? ? ? ? ? ? }  ? ? ? ? ? ? ?  }  ? ? ? ? ? ? }  ? ? ? ? ? ? this.aspectBeanNames = aspectNames;  ? ? ? ? ? ? return advisors;  ? ? ? ?  }  ? ? ? }  ?  }  ?  if (aspectNames.isEmpty()) {  ? ? ? return Collections.emptyList();  ?  }  ?  List<Advisor> advisors = new ArrayList<>();  ?  for (String aspectName : aspectNames) {  ? ? ? List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);  ? ? ? if (cachedAdvisors != null) {  ? ? ? ?  advisors.addAll(cachedAdvisors);  ? ? ? }  ? ? ? else {  ? ? ? ?  MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);  ? ? ? ?  advisors.addAll(this.advisorFactory.getAdvisors(factory));  ? ? ? }  ?  }  ?  return advisors;  }  这个方法可以概括为:  找到所有BeanName  根据BeanName筛选出被@Aspect注解的类  针对类中被Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class注解的方法,先按上边的注解顺序排序后按方法名称排序,每一个方法对应一个Advisor。  3.2 createProxy为当前bean创建代理。  3.2.1 创建代理的2种方式  众所周知,创建代理的常用的2种方式是:JDK创建和CGLIB,下面我们就看看这2中创建代理的例子。  3.2.1 .1 jdk创建代理的例子  
  
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

3.2.1 .2 cglib创建代理的例子  

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

3.2.1 .3 jdk创建代理与cglib创建代理的区别  

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

3.2.2 Spring如何选择的使用哪种方式  Spring的选择选择如何代理时在DefaultAopProxyFactory 中。  

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

config.isOptimize() 查看源码注释时发现,这个是配置使用cglib代理时,是否使用积极策略。这个值一般不建议使用!  config.isProxyTargetClass() 就是@EnableAspectJAutoProxy中的proxyTargetClass属性。  

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

hasNoUserSuppliedProxyInterfaces 是否存在可代理的接口  总结下Spring如何选择创建代理的方式:  如果设置了proxyTargetClass=true,一定是CGLIB代理  如果proxyTargetClass=false,目标对象实现了接口,走JDK代理  如果没有实现接口,走CGLIB代理  

4.总结  Spring如何实现AOP?,
您可以这样说:  AnnotationAwareAspectJAutoProxyCreator是AOP核心处理类
AnnotationAwareAspectJAutoProxyCreator实现了BeanProcessor,其中postProcessAfterInitialization是核心方法。  
核心实现分为2步  
getAdvicesAndAdvisorsForBean获取当前bean匹配的增强器
createProxy为当前bean创建代理  

getAdvicesAndAdvisorsForBean核心逻辑如下  
a. 找所有增强器,也就是所有@Aspect注解的Bean  
b. 找匹配的增强器,也就是根据@Before,@After等注解上的表达式,与当前bean进行匹配,暴露匹配上的。
c. 对匹配的增强器进行扩展和排序,就是按照@Order或者PriorityOrdered的getOrder的数据值进行排序,越小的越靠前。  

createProxy有2种创建方法,JDK代理或CGLIB  
a. 如果设置了proxyTargetClass=true,一定是CGLIB代理  
b. 如果proxyTargetClass=false,目标对象实现了接口,走JDK代理
c. 如果没有实现接口,走CGLIB代理    
----------------------------
原文链接:https://www.jianshu.com/p/12de4336d431



程序猿的技术大观园:www.javathinker.net
  Java面向对象编程-->Lambda表达式
  JavaWeb开发-->自定义JSP标签(Ⅱ)
  JSP与Hibernate开发-->域对象在持久化层的四种状态
  Java网络编程-->客户端协议处理框架
  精通Spring-->计算属性和数据监听
  Vue3开发-->绑定表单
  Netty初识
  深入理解spring注解之@ComponentScan注解
  谈谈响应式编程
  Spring的方法拦截器范例
  【项目实践】使用Vue.js和ElementUI快速实现后台管理系统的界...
  Spring MVC国际化
  Spring MVC关于分页的简单实现
  深入Redis客户端(redis客户端属性、redis缓冲区、关闭redis客...
  理解 RESTful 风格的 API
  SpringBoot构建Restful接口
  springboot集成通用mapper实现Echarts
  Nginx技术探秘
  SVN使用指南:查看历史信息的方法
  Axis、Axis2和CXF比较
  大话微服务」深入聊聊SpringCloud之客户端负载均衡机制
  更多...
 IPIP: 已设置保密
树形列表:   
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。