当前位置:首页 > 科技  > 软件

Spring 冷知识:一个提前 AOP 的机会

来源: 责编: 时间:2023-10-30 09:06:44 194观看
导读今天再来聊一个 Spring 中的冷门知识:Bean 的处理不走正常流程,而是提前进行 AOP。1. Bean 创建流程在 Bean 创建的过程中,会先给 BeanPostProcessor 一个返回代理对象的机会:@Overrideprotected Object createBean(Strin

今天再来聊一个 Spring 中的冷门知识:Bean 的处理不走正常流程,而是提前进行 AOP。caM28资讯网——每日最新资讯28at.com

1. Bean 创建流程

在 Bean 创建的过程中,会先给 BeanPostProcessor 一个返回代理对象的机会:caM28资讯网——每日最新资讯28at.com

@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)  throws BeanCreationException { //省略。。。 try {  // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.  Object bean = resolveBeforeInstantiation(beanName, mbdToUse);  if (bean != null) {   return bean;  } } catch (Throwable ex) {  throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,    "BeanPostProcessor before instantiation of bean failed", ex); } try {  Object beanInstance = doCreateBean(beanName, mbdToUse, args);  if (logger.isTraceEnabled()) {   logger.trace("Finished creating instance of bean '" + beanName + "'");  }  return beanInstance; }    //省略。。。}

小伙伴们看,这里的 resolveBeforeInstantiation 方法就是给 BeanPostProcessor 一个返回代理对象的机会,在这个方法中,最终就会触发到 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 方法,而在 postProcessBeforeInstantiation 方法中,会先判断当前 bean 是否是 AOP 相关类等:caM28资讯网——每日最新资讯28at.com

@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {  if (this.advisedBeans.containsKey(cacheKey)) {   return null;  }  if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {   this.advisedBeans.put(cacheKey, Boolean.FALSE);   return null;  } }  TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) {  if (StringUtils.hasLength(beanName)) {   this.targetSourcedBeans.add(beanName);  }  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);  Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);  this.proxyTypes.put(cacheKey, proxy.getClass());  return proxy; } return null;}

这里主要来说说 getCustomTargetSource 中的逻辑。caM28资讯网——每日最新资讯28at.com

先来说什么情况下会走到 getCustomTargetSource 方法:当前 Bean 不是代理对象,也不是 AOP 相关的类,就是一个普普通通的常规类,那么就会走到 getCustomTargetSource 方法这里来,这里失去查找到一个 TargetSource 对象,然后根据该对象创建当前 bean 的代理对象并返回,如果返回了代理对象,那么后续的 bean 创建流程就不执行了。caM28资讯网——每日最新资讯28at.com

我们来看下这个方法的源码:caM28资讯网——每日最新资讯28at.com

@Nullableprotected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) { // We can't create fancy target sources for directly registered singletons. if (this.customTargetSourceCreators != null &&   this.beanFactory != null && this.beanFactory.containsBean(beanName)) {  for (TargetSourceCreator tsc : this.customTargetSourceCreators) {   TargetSource ts = tsc.getTargetSource(beanClass, beanName);   if (ts != null) {    return ts;   }  } } // No custom TargetSource found. return null;}

可以看到,这里就是当前类 AbstractAutoProxyCreator 中有一个 customTargetSourceCreators 变量,现在就是遍历该变量,通过这个集合中保存的 TargetSourceCreator 来创建 TargetSource 对象。caM28资讯网——每日最新资讯28at.com

TargetSourceCreator 是一个接口,这个接口只有一个抽象类 AbstractBeanFactoryBasedTargetSourceCreator,我们来看下 AbstractBeanFactoryBasedTargetSourceCreator 中的 getTargetSource 方法是怎么执行的:caM28资讯网——每日最新资讯28at.com

@Override@Nullablepublic final TargetSource getTargetSource(Class<?> beanClass, String beanName) { AbstractBeanFactoryBasedTargetSource targetSource =   createBeanFactoryBasedTargetSource(beanClass, beanName); if (targetSource == null) {  return null; } DefaultListableBeanFactory internalBeanFactory = getInternalBeanFactoryForBean(beanName); // We need to override just this bean definition, as it may reference other beans // and we're happy to take the parent's definition for those. // Always use prototype scope if demanded. BeanDefinition bd = getConfigurableBeanFactory().getMergedBeanDefinition(beanName); GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd); if (isPrototypeBased()) {  bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE); } internalBeanFactory.registerBeanDefinition(beanName, bdCopy); // Complete configuring the PrototypeTargetSource. targetSource.setTargetBeanName(beanName); targetSource.setBeanFactory(internalBeanFactory); return targetSource;}

首先,TargetSource 对象是通过 createBeanFactoryBasedTargetSource 方法来创建的,这个方法是一个抽象方法,将来在子类中被实现。caM28资讯网——每日最新资讯28at.com

接下来会调用 getInternalBeanFactoryForBean 方法创建一个新的内部容器 internalBeanFactory,本质上这个 internalBeanFactory 其实是一个子容器,现有的容器将作为这个子容器的父容器。caM28资讯网——每日最新资讯28at.com

接下来就是获取到当前 beanName 所对应的 BeanDefinition,然后进行属性配置,并注册到内部容器中,最后返回 targetSource 对象。caM28资讯网——每日最新资讯28at.com

我们来看下这里的 getInternalBeanFactoryForBean 方法:caM28资讯网——每日最新资讯28at.com

protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) { synchronized (this.internalBeanFactories) {  return this.internalBeanFactories.computeIfAbsent(beanName,    name -> buildInternalBeanFactory(getConfigurableBeanFactory())); }}protected DefaultListableBeanFactory buildInternalBeanFactory(ConfigurableBeanFactory containingFactory) { // Set parent so that references (up container hierarchies) are correctly resolved. DefaultListableBeanFactory internalBeanFactory = new DefaultListableBeanFactory(containingFactory); // Required so that all BeanPostProcessors, Scopes, etc become available. internalBeanFactory.copyConfigurationFrom(containingFactory); // Filter out BeanPostProcessors that are part of the AOP infrastructure, // since those are only meant to apply to beans defined in the original factory. internalBeanFactory.getBeanPostProcessors().removeIf(beanPostProcessor ->   beanPostProcessor instanceof AopInfrastructureBean); return internalBeanFactory;}

这个其实就是正常的容器创建,倒也没啥好说的,但是有几个需要注意的点:caM28资讯网——每日最新资讯28at.com

  1. 在调用 buildInternalBeanFactory 方法构建容器的时候,会先调用 getConfigurableBeanFactory 方法获取到当前容器作为父容器,如果当前容器不存在,那么就会抛出异常。这就意味着,当我们自己提供 TargetSourceCreator 实例的时候,一定要指定一个容器。
  2. 在创建了内部容器之后,会从内部容器中移除所有 AopInfrastructureBean 类型的 BeanPostProcessor,也就是内部容器将来创建出来的 bean,不再走 AopInfrastructureBean 类型后置处理器,因为这种类型的后置处理器主要是用来处理 AOP 的,现在,AOP 代理当场就生成了,就不再需要这些后置处理器了。

好了,这就是大致的 AOP 提前生成原理,接下来松哥写一个案例我们一起来看下。caM28资讯网——每日最新资讯28at.com

2. 实践

首先,我们先来自定义一个 TargetSource:caM28资讯网——每日最新资讯28at.com

public class UserServiceTargetSource extends AbstractBeanFactoryBasedTargetSource {    @Override    public Object getTarget() throws Exception {        return getBeanFactory().getBean(getTargetBeanName());    }    @Override    public boolean isStatic() {        return true;    }}

关于 TargetSource 本身,松哥在之前的 Spring 源码视频中已经和大家介绍过很多了,这里我就不再啰嗦了。caM28资讯网——每日最新资讯28at.com

接下来自定义 TargetSourceCreator:caM28资讯网——每日最新资讯28at.com

public class CustomTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {    @Override    protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {        if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {            if (beanClass.isAssignableFrom(UserService.class)) {                return new UserServiceTargetSource();            }        }        return null;    }}

如果要创建的 bean 是 UserService 的话,那么就给返回一个 UserServiceTargetSource 对象。caM28资讯网——每日最新资讯28at.com

最后,也是最关键的一步,根据前面的分析,TargetSourceCreator 是存在于 AnnotationAwareAspectJAutoProxyCreator 这样一个 InstantiationAwareBeanPostProcessor 类型的后置处理器中的,因此,我们要想办法把自定义的 TargetSourceCreator 设置给 AnnotationAwareAspectJAutoProxyCreator,如下:caM28资讯网——每日最新资讯28at.com

@Componentpublic class SetCustomTargetSourceCreator implements BeanPostProcessor, PriorityOrdered, BeanFactoryAware {    private BeanFactory beanFactory;    @Override    public int getOrder() {        return Integer.MIN_VALUE;    }    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        if(bean instanceof AnnotationAwareAspectJAutoProxyCreator) {            AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator = (AnnotationAwareAspectJAutoProxyCreator)bean;            CustomTargetSourceCreator customTargetSourceCreator = new CustomTargetSourceCreator();            customTargetSourceCreator.setBeanFactory(beanFactory);            annotationAwareAspectJAutoProxyCreator.setCustomTargetSourceCreators(customTargetSourceCreator);        }        return bean;    }    @Override    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {        this.beanFactory = beanFactory;    }}

AnnotationAwareAspectJAutoProxyCreator 本身就是一个 BeanPostProcessor,我们现在要做的就是修改这个 BeanPostProcessor,BeanPostProcessor 是在 Spring 容器启动时候的 refresh 方法中去初始化的。caM28资讯网——每日最新资讯28at.com

BeanPostProcessor 初始化的时候,先初始化实现了 PriorityOrdered 接口的,再初始化实现了 Ordered 接口的,最后再去初始化那些没有实现任何排序接口的 BeanPostProcessor。caM28资讯网——每日最新资讯28at.com

而我们这里 SetCustomTargetSourceCreator 一定要赶在 AnnotationAwareAspectJAutoProxyCreator 之前进行初始化,这样,当 AnnotationAwareAspectJAutoProxyCreator 进行初始化的时候,就会用到 SetCustomTargetSourceCreator 这样一个后置处理器,进而在该处理器中修改 AnnotationAwareAspectJAutoProxyCreator 的属性。caM28资讯网——每日最新资讯28at.com

AnnotationAwareAspectJAutoProxyCreator 类间接实现了 Ordered 接口,默认优先级是最低,但是在 Spring 容器启动时,在处理 BeanFactoryPostProcessor 时(具体是 ConfigurationClassPostProcessor),将其优先级设置为最高。caM28资讯网——每日最新资讯28at.com

所以,我们如果想要让自定义的 SetCustomTargetSourceCreator 抢在 AnnotationAwareAspectJAutoProxyCreator 之前执行,那么就只能让 SetCustomTargetSourceCreator 去实现 PriorityOrdered 接口了,实现 PriorityOrdered 接口之后,重写 getOrder 方法,这个方法返回值是什么无所谓,反正都会在实现了 Ordered 接口的 BeanPostProcessor 之前执行。caM28资讯网——每日最新资讯28at.com

最后,我们再在启动类上开启自动代理即可:caM28资讯网——每日最新资讯28at.com

@Configuration@ComponentScan@EnableAspectJAutoProxypublic class JavaConfig {}

大功告成。caM28资讯网——每日最新资讯28at.com

这样,当 Spring 容器创建一个 Bean 的时候,就会提前被 BeanPostProcessor 拦截,然后给出一个 TargetSource,进而据此创建代理对象,这样就不需要后续常规的 Bean 创建流程了。好啦,感兴趣的小伙伴可以自己去试一试哦~caM28资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-15721-0.htmlSpring 冷知识:一个提前 AOP 的机会

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 作为前端开发者,你没有必要学 Rust

下一篇: 作为前端开发者,你没有必要学 Rust

标签:
  • 热门焦点
  • Mate60手机壳曝光 致敬自己的经典设计

    Mate60手机壳曝光 致敬自己的经典设计

    8月3日消息,今天下午博主数码闲聊站带来了华为Mate60的第三方手机壳图,可以让我们在真机发布之前看看这款华为全新旗舰的大致轮廓。从曝光的图片看,Mate 60背后摄像头面积依然
  • 6月iOS设备性能榜:M2稳居榜首 A系列只能等一手3nm来救

    6月iOS设备性能榜:M2稳居榜首 A系列只能等一手3nm来救

    没有新品发布,自然iOS设备性能榜的上榜设备就没有什么更替,仅仅只有跑分变化而产生的排名变动,毕竟苹果新品的发布节奏就是这样的,一年下来也就几个移动端新品,不会像安卓厂商,一
  • Flowable工作流引擎的科普与实践

    Flowable工作流引擎的科普与实践

    一.引言当我们在日常工作和业务中需要进行各种审批流程时,可能会面临一系列技术和业务上的挑战。手动处理这些审批流程可能会导致开发成本的增加以及业务复杂度的上升。在这
  • 19个 JavaScript 单行代码技巧,让你看起来像个专业人士

    19个 JavaScript 单行代码技巧,让你看起来像个专业人士

    今天这篇文章跟大家分享18个JS单行代码,你只需花几分钟时间,即可帮助您了解一些您可能不知道的 JS 知识,如果您已经知道了,就当作复习一下,古人云,温故而知新嘛。现在,我们就开始今
  • 这款新兴工具平台,让你的电脑效率翻倍

    这款新兴工具平台,让你的电脑效率翻倍

    随着信息技术的发展,我们获取信息的渠道越来越多,但是处理信息的效率却成为一个瓶颈。于是各种工具应运而生,都在争相解决我们的工作效率问题。今天我要给大家介绍一款效率
  • ESG的面子与里子

    ESG的面子与里子

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之三伏大幕拉起,各地高温预警不绝,但处于厄尔尼诺大&ldquo;烤&rdquo;之下的除了众生,还有各大企业发布的ESG报告。ESG是&ldquo;环境保
  • iQOO 11S或7月上市:搭载“鸡血版”骁龙8Gen2 史上最强5G Soc

    iQOO 11S或7月上市:搭载“鸡血版”骁龙8Gen2 史上最强5G Soc

    去年底,iQOO推出了“电竞旗舰”iQOO 11系列,作为一款性能强机,iQOO 11不仅全球首发2K 144Hz E6全感屏,搭载了第二代骁龙8平台及144Hz电竞屏,同时在快充
  • OPPO K11样张首曝:千元机影像“卷”得真不错!

    OPPO K11样张首曝:千元机影像“卷”得真不错!

    一直以来,OPPO K系列机型都保持着较为均衡的产品体验,历来都是2K价位的明星机型,去年推出的OPPO K10和OPPO K10 Pro两款机型凭借各自的出色配置,堪称有
  • 三翼鸟智能家居亮相电博会,让用户体验更真实

    三翼鸟智能家居亮相电博会,让用户体验更真实

    2021电博会在青岛国际会展中心开幕中,三翼鸟直接把“家”搬到了现场,成为了展会的一大看点。这也是三翼鸟继9月9日发布了行业首个一站式定制智慧家平台后的
Top
Baidu
map