文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-03 06:50

关注

本文转载自微信公众号「月伴飞鱼」,作者日常加油站。转载本文请联系月伴飞鱼公众号。  

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

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

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

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

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

Spring常见扩展点

「BeanFactoryPostProcessor#postProcessBeanFactory」

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

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

  1. public class LazyBeanFactoryProcessor implements BeanFactoryPostProcessor { 
  2.     @Override 
  3.     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
  4.         DefaultListableBeanFactory fac = (DefaultListableBeanFactory) beanFactory; 
  5.         Map map = (Map) ReflectionTestUtils.getField(fac, "beanDefinitionMap"); 
  6.         for (Map.Entry entry : map.entrySet()) { 
  7.             //设置为懒加载 
  8.             entry.getValue().setLazyInit(true); 
  9.         } 
  10.     } 

「InstantiationAwareBeanPostProcessor#postProcessPropertyValues」

非常规的配置项比如

  1. "com.zhou" /> 

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

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

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

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

  1. public BeanDefinition parse(Element element, ParserContext parserContext) { 
  2.   //... 
  3.   registerComponents(parserContext.getReaderContext(), beanDefinitions, element); 
  4.     //... 
  5.   return null
  1. public static Set registerAnnotationConfigProcessors( 
  2.    BeanDefinitionRegistry registry, Object source) { 
  3.  
  4.   Set beanDefs = new LinkedHashSet(4); 
  5.   //... 
  6.     //@Autowire 
  7.   if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { 
  8.    RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); 
  9.    def.setSource(source); 
  10.    beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); 
  11.   } 
  12.  
  13.   // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. 
  14.    //@Resource 
  15.   if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { 
  16.       //特殊的Bean 
  17.    RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); 
  18.    def.setSource(source); 
  19.    beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); 
  20.   } 
  21.   //... 
  22.   return beanDefs; 
  23.  } 

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

  1. public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor 
  2.   implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable { 
  3.       
  4.       public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds,  
  5.       Object bean, String beanName) throws BeansException { 
  6.           InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass()); 
  7.           try { 
  8.             //属性注入 
  9.             metadata.inject(bean, beanName, pvs); 
  10.           } 
  11.           catch (Throwable ex) { 
  12.             throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex); 
  13.           } 
  14.           return pvs; 
  15.     } 
  16.      

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

「invokeAware」

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

  1. @Bean 
  2. class BeanFactoryHolder implements BeanFactoryAware{ 
  3.     
  4.     private static BeanFactory beanFactory; 
  5.      
  6.     public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 
  7.         this.beanFactory = beanFactory; 
  8.     } 

「BeanPostProcessor#postProcessBeforeInitialization」

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

  1. @Bean 
  2. class ApplicationContextAwareProcessor implements BeanPostProcessor { 
  3.  
  4.     private final ConfigurableApplicationContext applicationContext; 
  5.  
  6.     public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) { 
  7.       this.applicationContext = applicationContext; 
  8.     } 
  9.  
  10.     @Override 
  11.     public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException { 
  12.       //... 
  13.       invokeAwareInterfaces(bean); 
  14.       return bean; 
  15.     } 
  16.  
  17.     private void invokeAwareInterfaces(Object bean) { 
  18.         if (bean instanceof ApplicationContextAware) { 
  19.           ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); 
  20.         } 
  21.     } 

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

  1. class ApplicationContextHolder implements ApplicationContextAware{ 
  2.     
  3.     private static ApplicationContext applicationContext; 
  4.      
  5.     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
  6.         this.applicationContext = applicationContext; 
  7.     } 

「afterPropertySet()和init-method」

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

「BeanPostProcessor#postProcessAfterInitialization」

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

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

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

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

  1.  

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

这里是一个BeanPostProcessor类型的bean

  1. class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser { 
  2.  @Override 
  3.  public BeanDefinition parse(Element element, ParserContext parserContext) { 
  4.     //注册特殊的bean 
  5.   AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); 
  6.   extendBeanDefinition(element, parserContext); 
  7.   return null
  8.     } 

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

  1. public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator { 
  2.   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
  3.         if (bean != null) { 
  4.           Object cacheKey = getCacheKey(bean.getClass(), beanName); 
  5.           if (!this.earlyProxyReferences.containsKey(cacheKey)) { 
  6.             //如果该类需要被代理,返回动态代理对象;反之,返回原对象 
  7.             return wrapIfNecessary(bean, beanName, cacheKey); 
  8.           } 
  9.         } 
  10.         return bean; 
  11.  } 

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

「destroy()和destroy-method」

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

 

来源:月伴飞鱼内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯