<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近在看公司專案和中介軟體的時候,看到一些Spring擴充套件點的使用,寫篇文章學習下,對大家之後看原始碼都有幫助
首先先介紹下Bean的生命週期:
我們知道Bean的生命週期分為幾個主幹流程
下面是整個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 InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable { 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持有的一些資源釋放掉
到此這篇關於Java中Spring技巧之擴充套件點的應用的文章就介紹到這了,更多相關Spring擴充套件點應用內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45