>>分享流行的Java框架以及开源软件,对孙卫琴的《精通Spring:Java Web开发技术详解》提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 22043 个阅读者 刷新本主题
 * 贴子主题:  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面向对象编程-->异常处理
  JavaWeb开发-->Servlet技术详解(Ⅰ)
  JSP与Hibernate开发-->映射组成关系
  Java网络编程-->安全网络通信
  精通Spring-->绑定表单
  Vue3开发-->Vue CLI脚手架工具
  几种常见的MAVEN仓库地址
  微服务的拆分方式
  Spring MVC和前后端分离的RESTFul框架
  Spring Boot JPA @OneToOne
  重新理解响应式编程
  POJO与JavaBean与SpringBean的概念与区别
  国内 Java 开发者必备的两个神器:Maven国内镜像和Spring国内...
  如何实现Git服务间同步
  另一种缓存,Spring Boot 整合 Ehcache
  Kafka笔记整理
  带你逆袭kafka之路
  阿里面试官问我:如何用Redis设计秒杀系统?我的回答让他比起...
  Spring @Transactional注解失效解决方案
  Spring Framework 组件注册 之 @Import
  springboot —— 多数据源
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


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