創(chuàng)新互聯(lián)建站專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、興安網(wǎng)絡(luò)推廣、小程序開(kāi)發(fā)、興安網(wǎng)絡(luò)營(yíng)銷、興安企業(yè)策劃、興安品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)建站為所有大學(xué)生創(chuàng)業(yè)者提供興安建站搭建服務(wù),24小時(shí)服務(wù)熱線:028-86922220,官方網(wǎng)址:muchs.cn
常見(jiàn)的定義Bean的方式有:
通過(guò)xml的方式,例如:
<bean id="dictionaryRelMap" class="java.util.HashMap"/>
通過(guò)注解的方式,在Class上使用@Component等注解,例如
@Componentpublic class xxxServicer{ .... }
通過(guò)在@Configuration類下的@Bean的方式,例如
@Configurationpublic class xxxConfiguration{ @Bean public myBean myBean(){ return new myBean(); } }
雖然這三種定義Bean的方式不一樣,對(duì)應(yīng)的處理細(xì)節(jié)也不一樣,但是從大的邏輯上來(lái)看,都是一樣。主要的流程如下圖: 最關(guān)鍵的就是問(wèn)題就是這么去找到定義Bean的方式,然后生成BeanDefinition后注冊(cè)到Spring上下文中,由Spring自動(dòng)創(chuàng)建Bean的實(shí)例。
BeanDefinition是一個(gè)接口,用來(lái)描述一個(gè)Bean實(shí)例,例如是SINGLETON還是PROTOTYPE,屬性的值是什么,構(gòu)造函數(shù)的參數(shù)是什么等。簡(jiǎn)單來(lái)說(shuō),通過(guò)一個(gè)BeanDefinition我們就可以完成一個(gè)Bean實(shí)例化。 BeanDefinition及其主要的子類:
下面簡(jiǎn)單說(shuō)一下各個(gè)子類:
RootBeanDefinition和ChildBeanDefinition: 這2個(gè)BeanDefinition是相對(duì)的關(guān)系,自Spring 2.5 出來(lái)以后,已經(jīng)被GenericBeanDefinition代替。因?yàn)檫@樣強(qiáng)迫我們?cè)诰帉懘a的時(shí)候就必須知道他們之間的關(guān)系。
GenericBeanDefinition: 相比于RootBeanDefinition和ChildBeanDefinition在定義的時(shí)候就必須硬編碼,GenericBeanDefinition的優(yōu)點(diǎn)可以動(dòng)態(tài)的為GenericBeanDefinition設(shè)置parent。
AnnotatedBeanDefinition:看名字就是知道是用來(lái)讀取通過(guò)注解定義Bean。
通過(guò)xml定義Bean是最早的Spring定義Bean的方式。因此,怎么把xml標(biāo)簽解析為BeanDefinition(), 入口是在org.springframework.beans.factory.xml.XmlBeanDefinitionReader
這個(gè)類,但是實(shí)際干活的是在org.springframework.beans.factory.xml.BeanDefinitionParserDelegate
。代碼很多,但實(shí)際邏輯很簡(jiǎn)單,就是解析Spring定義的<bean> <property> 等標(biāo)簽 。 之前寫過(guò)一篇文章介紹過(guò)如何自定義Spring標(biāo)簽 ,并解析后注冊(cè)到Spring中——傳送門
如果要使用@Component
等注解定義Bean,一個(gè)前提條件是:有<context:component-scan/>
或者@ComponentScan
注解。但這2個(gè)方式還是有一點(diǎn)點(diǎn)區(qū)別:
由于<context:component-scan/>
是一個(gè)xml標(biāo)簽,因此是在解析xml,生成的類org.springframework.context.annotation.ComponentScanBeanDefinitionParser
,關(guān)鍵代碼:
@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) { //獲取base-package標(biāo)簽 String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // 實(shí)際處理類是ClassPathBeanDefinitionScanner ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); //掃描basePackage下所有的類,如果有@Component等標(biāo)簽就是注冊(cè)到Spring中 Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
注解對(duì)應(yīng)生成的類是org.springframework.context.annotation.ComponentScanAnnotationParser
其實(shí)最后實(shí)際干活的還是ClassPathBeanDefinitionScanner
這個(gè)。ComponentScanAnnotationParser
類的生成是伴隨著@Configuration
這個(gè)注解處理過(guò)程中(意思說(shuō)@ComponentScan
必須和@Configuration
一起使用)。而處理@Configuration
其實(shí)是org.springframework.context.annotation.ConfigurationClassPostProcessor
。是不是感覺(jué)有點(diǎn)繞。
其實(shí)簡(jiǎn)單來(lái)說(shuō),在處理@Configuration
的時(shí)候發(fā)現(xiàn)有@ComponentScan
注解,就會(huì)生成ComponentScanAnnotationParser
去掃描@Component注解
上面說(shuō)到了,無(wú)論注解還是標(biāo)簽的方式,最后都會(huì)交給ClassPathBeanDefinitionScanner
這個(gè)類來(lái)處理,這個(gè)類做的就是1.掃描basePackage下所有class,如果有@Component等注解,讀取@Component相關(guān)屬性,生成ScannedGenericBeanDefinition
,注冊(cè)到Spring中。
前面說(shuō)了@ComponentScan是在@Configuration處理過(guò)程中的一環(huán),既然@Bean注解也是必須和@Configuration一起使用,那么說(shuō)明@Bean的處理也是在@Configuration中,其實(shí)最后是交給ConfigurationClassBeanDefinitionReader
這個(gè)類來(lái)處理的,關(guān)鍵代碼:
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { //如果自己是通過(guò)@Import注解定義的,那么需要把自己注冊(cè)到Spring中 if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } //這里就是處理方法上的@Bean for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } //處理@ImportResource,里面解析xml就是上面說(shuō)到的解析xml的XmlBeanDefinitionReader loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
前面分別說(shuō)了怎么把不同定義Bean的方式轉(zhuǎn)換為BeanDefinition加入到Spring中去(確切來(lái)說(shuō)是保持在BeanFactory的BeanDefinitionMap中),實(shí)例化這些BeanDefinition是在ApplicationContext最后階段,關(guān)鍵代碼在DefaultListableBeanFactory中
preInstantiateSingletons() = (!bd.isAbstract() && bd.isSingleton() && ! FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + (System.getSecurityManager() != && factory = AccessController.doPrivileged( PrivilegedAction<Boolean> ((SmartFactoryBean<?>= (factory SmartFactoryBean &&<?>
通過(guò)getBean實(shí)例化BeanFactory,代碼是在AbstractAutowireCapableBeanFactory中
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { //處理xxAware接口 if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // 調(diào)用BeanPostProcessors#postProcessBeforeInitialization wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //初始化,先判斷是否是InitializingBean, invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // 調(diào)用BeanPostProcessors#postProcessAfterInitialization wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
從上面初始化和注釋內(nèi)容可以看出,InitializeBean接口的方法(以及Init-method)和BeanPostProcessors的調(diào)用順序
綜上分析,Spring加載Bean其實(shí)大的思想都是一樣的,先讀取相關(guān)信息生成BeanDefinition,然后通過(guò)BeanDefinition初始化Bean。如果知道了上面了套路以后,就可以清楚怎么自定義Xml標(biāo)簽或者自定義注解向Spring中注入Bean。
當(dāng)前題目:Spring多種加載Bean方式簡(jiǎn)析
本文地址:http://muchs.cn/article16/jcpsgg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、標(biāo)簽優(yōu)化、網(wǎng)站改版、外貿(mào)網(wǎng)站建設(shè)、用戶體驗(yàn)、網(wǎng)站設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)