当前位置: 首页 >文章 > Spring奇技淫巧之扩展点的应用
收藏
分享

Spring奇技淫巧之扩展点的应用

举报小虎转载君小虎转载君发布于 2021-04-10644阅读0点赞
bean生命周期的最后一个扩展点,该方法用于执行一些bean销毁前的准备工作,比如将当前bean持有的一些资源释放掉...

最近在看公司项目和中间件的时候,看到一些Spring扩展点的使用,写篇文章学习下,对大家之后看源码都有帮助

「首先先介绍下Bean的生命周期」

我们知道Bean的生命周期分为几个主干流程

  • Bean(单例非懒加载)的实例化阶段
  • Bean的属性注入阶段
  • Bean的初始化阶段
  • Bean的销毁阶段

下面是整个Spring容器的启动流程,可以看到除了上述几个主干流程外,Spring还提供了很多扩展点

下面详细介绍下Spring的常见的扩展点

Spring常见扩展点

「BeanFactoryPostProcessor#postProcessBeanFactory」

有时候整个项目工程中bean的数量有上百个,而大部分单测依赖都是整个工程的xml,导致单测执行时需要很长时间(大部分时间耗费在xml中数百个单例非懒加载的bean的实例化及初始化过程)

解决方法:利用Spring提供的扩展点将xml中的bean设置为懒加载模式,省去了Bean的实例化与初始化时间

public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor {

    @Override

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory;

        Map<String, AbstractBeanDefinition> map = (Map<String, AbstractBeanDefinition>) ReflectionTestUtils.getField(fac, "beanDefinitionMap");

        for (Map.Entry<String, AbstractBeanDefinition> entry : map.entrySet()) {

            //设置为懒加载

            entry.getValue().setLazyInit(true);

        }

    }

}

「InstantiationAwareBeanPostProcessor#postProcessPropertyValues」

非常规的配置项比如

<context:component-scan base-package="com.zhou" />

Spring提供了与之对应的特殊解析器

正是通过这些特殊的解析器才使得对应的配置项能够生效

而针对这个特殊配置的解析器为 ComponentScanBeanDefinitionParser

在这个解析器的解析方法中,注册了很多特殊的Bean

public BeanDefinition parse(Element element, ParserContext parserContext) {

  //...

  registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

    //...

  return null;

}

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(

   BeanDefinitionRegistry registry, Object source)
 
{

  Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);



  //...

    //@Autowire

  if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {

   RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);

   def.setSource(source);

   beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));

  }

  // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.



   //@Resource

  if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {

      //特殊的Bean

   RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);

   def.setSource(source);

   beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));

  }

  //...

  return beanDefs;

 }

以@Resource为例,看看这个特殊的bean做了什么

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor

  implements InstantiationAwareBeanPostProcessorBeanFactoryAwareSerializable 
{

     

      public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, 

      Object bean, String beanName)
 throws BeansException 
{

          InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass());

          try {

            //属性注入

            metadata.inject(bean, beanName, pvs);

          }

          catch (Throwable ex) {

            throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);

          }

          return pvs;

    }

    

}

我们看到在postProcessPropertyValues方法中,进行了属性注入

「invokeAware」

实现BeanFactoryAware接口的类,会由容器执行setBeanFactory方法将当前的容器BeanFactory注入到类中

@Bean

class BeanFactoryHolder implements BeanFactoryAware{

   

    private static BeanFactory beanFactory;

    

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

        this.beanFactory = beanFactory;

    }

}

「BeanPostProcessor#postProcessBeforeInitialization」

实现ApplicationContextAware接口的类,会由容器执行setApplicationContext方法将当前的容器applicationContext注入到类中

@Bean

class ApplicationContextAwareProcessor implements BeanPostProcessor {

    private final ConfigurableApplicationContext applicationContext;

    public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {



      this.applicationContext = applicationContext;

    }

    @Override



    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {

      //...

      invokeAwareInterfaces(bean);

      return bean;

    }

    private void invokeAwareInterfaces(Object bean) {



        if (bean instanceof ApplicationContextAware) {

          ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);

        }

    }

}

我们看到是在BeanPostProcessor的postProcessBeforeInitialization中进行了setApplicationContext方法的调用

class ApplicationContextHolder implements ApplicationContextAware{

   

    private static ApplicationContext applicationContext;

    

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        this.applicationContext = applicationContext;

    }

}

「afterPropertySet()和init-method」

目前很多Java中间件都是基本Spring Framework搭建的,而这些中间件经常把入口放到afterPropertySet或者自定义的init中

「BeanPostProcessor#postProcessAfterInitialization」

熟悉aop的同学应该知道,aop底层是通过动态代理实现的

当配置了<aop:aspectj-autoproxy/>时候,默认开启aop功能,相应地调用方需要被aop织入的对象也需要替换为动态代理对象

不知道大家有没有思考过动态代理是如何「在调用方无感知情况下替换原始对象」的?

根据上文的讲解,我们知道:

<aop:aspectj-autoproxy/>

Spring也提供了特殊的解析器,和其他的解析器类似,在核心的parse方法中注册了特殊的bean

这里是一个BeanPostProcessor类型的bean

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

 @Override

 public BeanDefinition parse(Element element, ParserContext parserContext) {

    //注册特殊的bean

  AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);

  extendBeanDefinition(element, parserContext);

  return null;

    }

}

将于当前bean对应的动态代理对象返回即可,该过程对调用方全部透明

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {

  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if (bean != null) {

          Object cacheKey = getCacheKey(bean.getClass(), beanName);

          if (!this.earlyProxyReferences.containsKey(cacheKey)) {

            //如果该类需要被代理,返回动态代理对象;反之,返回原对象

            return wrapIfNecessary(bean, beanName, cacheKey);

          }

        }

        return bean;

 }

}

正是利用Spring的这个扩展点实现了动态代理对象的替换

「destroy()和destroy-method」

bean生命周期的最后一个扩展点,该方法用于执行一些bean销毁前的准备工作,比如将当前bean持有的一些资源释放掉


本文原创,未经作者允许不可转载!
更多内容,欢迎关注作者微信公众号:月伴飞鱼!


0条评论
别默默看啦~登录/注册一起参与讨论吧~

暂无评论

请选择举报理由

违反法律法规

侵犯个人权益

有害网站环境

更多训练营>>

为你推荐 · 训练营(全勤打卡报名费全额返累计全额返用户134,638人)

【10月】零基础动态表情包创作训练营
距离开班仅剩9天36人已报名
【10月】多风格插画入门训练营
距离开班仅剩9天30人已报名
10个实用版式构图技法
距离开班仅剩22天18人已报名
特惠
充值
7折购
今日还在继续学习的你,太棒了!
7
折扣券可用于
年费无限VIP
立 即
使 用
此活动优惠不可与其他活动叠加使用
有效期:000000
消息
登录即可查看消息记录
建议
意见
官方
客服
在线咨询

您可以与在线客服进行沟通获得帮助

工作日:9:00~22:00节假日:9:00~18:00

联系在线客服
虎课
积分
免费学习91000+个教程!
配套素材、源文件一键下载!
昨日学员已学习了33,157
并提交了168份作业!
登录后立即学习!
loading
微信扫码关注即可登录
您需要同意协议才可以进行登录
登录虎课网,每天免费学课程全站 91000+ 视频会员教程 | 每日可免费学 1
为确保账户信息安全
请先进行真实姓名验证后进行充值付款
立即验证