成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

驚呆了,Spring中竟然有12種定義Bean的方法

開(kāi)發(fā) 架構(gòu)
在龐大的java體系中,spring有著舉足輕重的地位,它給每位開(kāi)發(fā)者帶來(lái)了極大的便利和驚喜。我們都知道spring是創(chuàng)建和管理bean的工廠(chǎng),它提供了多種定義bean的方式,能夠滿(mǎn)足我們?nèi)粘9ぷ髦械亩喾N業(yè)務(wù)場(chǎng)景。

[[409379]]

本文轉(zhuǎn)載自微信公眾號(hào)「蘇三說(shuō)技術(shù)」,作者因?yàn)闊釔?ài)所以堅(jiān)持ing 。轉(zhuǎn)載本文請(qǐng)聯(lián)系蘇三說(shuō)技術(shù)公眾號(hào)。

前言

在龐大的java體系中,spring有著舉足輕重的地位,它給每位開(kāi)發(fā)者帶來(lái)了極大的便利和驚喜。我們都知道spring是創(chuàng)建和管理bean的工廠(chǎng),它提供了多種定義bean的方式,能夠滿(mǎn)足我們?nèi)粘9ぷ髦械亩喾N業(yè)務(wù)場(chǎng)景。

那么問(wèn)題來(lái)了,你知道spring中有哪些方式可以定義bean?

我估計(jì)很多人會(huì)說(shuō)出以下三種:

沒(méi)錯(cuò),但我想說(shuō)的是以上三種方式只是開(kāi)胃小菜,實(shí)際上spring的功能遠(yuǎn)比你想象中更強(qiáng)大。

各位看官如果不信,請(qǐng)繼續(xù)往下看。

1. xml文件配置bean

我們先從xml配置bean開(kāi)始,它是spring最早支持的方式。后來(lái),隨著springboot越來(lái)越受歡迎,該方法目前已經(jīng)用得很少了,但我建議我們還是有必要了解一下。

1.1 構(gòu)造器

如果你之前有在bean.xml文件中配置過(guò)bean的經(jīng)歷,那么對(duì)如下的配置肯定不會(huì)陌生:

  1. <bean id="personService" class="com.sue.cache.service.test7.PersonService"
  2. </bean> 

這種方式是以前使用最多的方式,它默認(rèn)使用了無(wú)參構(gòu)造器創(chuàng)建bean。

當(dāng)然我們還可以使用有參的構(gòu)造器,通過(guò)標(biāo)簽來(lái)完成配置。

  1. <bean id="personService" class="com.sue.cache.service.test7.PersonService"
  2.    <constructor-arg index="0" value="susan"></constructor-arg> 
  3.    <constructor-arg index="1" ref="baseInfo"></constructor-arg> 
  4. </bean> 

其中:

  • index表示下標(biāo),從0開(kāi)始。
  • value表示常量值
  • ref表示引用另一個(gè)bean

1.2 setter方法

除此之外,spring還提供了另外一種思路:通過(guò)setter方法設(shè)置bean所需參數(shù),這種方式耦合性相對(duì)較低,比有參構(gòu)造器使用更為廣泛。

先定義Person實(shí)體:

  1. @Data 
  2. public class Person { 
  3.     private String name
  4.     private int age; 

它里面包含:成員變量name和age,getter/setter方法。

然后在bean.xml文件中配置bean時(shí),加上<:property>標(biāo)簽設(shè)置bean所需參數(shù)。

  1. <bean id="person" class="com.sue.cache.service.test7.Person"
  2.    <property name="name" value="susan"></constructor-arg> 
  3.    <property name="age" value="18"></constructor-arg> 
  4. </bean> 

1.3 靜態(tài)工廠(chǎng)

這種方式的關(guān)鍵是需要定義一個(gè)工廠(chǎng)類(lèi),它里面包含一個(gè)創(chuàng)建bean的靜態(tài)方法。例如:

  1. public class SusanBeanFactory { 
  2.     public static Person createPerson(String nameint age) { 
  3.         return new Person(name, age); 
  4.     } 

接下來(lái)定義Person類(lèi)如下:

  1. @AllArgsConstructor 
  2. @NoArgsConstructor 
  3. @Data 
  4. public class Person { 
  5.     private String name
  6.     private int age; 

它里面包含:成員變量name和age,getter/setter方法,無(wú)參構(gòu)造器和全參構(gòu)造器。

然后在bean.xml文件中配置bean時(shí),通過(guò)factory-method參數(shù)指定靜態(tài)工廠(chǎng)方法,同時(shí)通過(guò)設(shè)置相關(guān)參數(shù)。

  1. <bean class="com.sue.cache.service.test7.SusanBeanFactory" factory-method="createPerson"
  2.    <constructor-arg index="0" value="susan"></constructor-arg> 
  3.    <constructor-arg index="1" value="18"></constructor-arg> 
  4. </bean> 

1.4 實(shí)例工廠(chǎng)方法

這種方式也需要定義一個(gè)工廠(chǎng)類(lèi),但里面包含非靜態(tài)的創(chuàng)建bean的方法。

  1. public class SusanBeanFactory { 
  2.     public Person createPerson(String nameint age) { 
  3.         return new Person(name, age); 
  4.     } 

Person類(lèi)跟上面一樣,就不多說(shuō)了。

然后bean.xml文件中配置bean時(shí),需要先配置工廠(chǎng)bean。然后在配置實(shí)例bean時(shí),通過(guò)factory-bean參數(shù)指定該工廠(chǎng)bean的引用。

  1. <bean id="susanBeanFactory" class="com.sue.cache.service.test7.SusanBeanFactory"
  2. </bean> 
  3. <bean factory-bean="susanBeanFactory" factory-method="createPerson"
  4.    <constructor-arg index="0" value="susan"></constructor-arg> 
  5.    <constructor-arg index="1" value="18"></constructor-arg> 
  6. </bean> 

1.5 FactoryBean

不知道大家有沒(méi)有發(fā)現(xiàn),上面的實(shí)例工廠(chǎng)方法每次都需要?jiǎng)?chuàng)建一個(gè)工廠(chǎng)類(lèi),不方面統(tǒng)一管理。

這時(shí)我們可以使用FactoryBean接口。

  1. public class UserFactoryBean implements FactoryBean<User> { 
  2.     @Override 
  3.     public User getObject() throws Exception { 
  4.         return new User(); 
  5.     } 
  6.  
  7.     @Override 
  8.     public Class<?> getObjectType() { 
  9.         return User.class; 
  10.     } 

在它的getObject方法中可以實(shí)現(xiàn)我們自己的邏輯創(chuàng)建對(duì)象,并且在getObjectType方法中我們可以定義對(duì)象的類(lèi)型。

然后在bean.xml文件中配置bean時(shí),只需像普通的bean一樣配置即可。

  1. <bean id="userFactoryBean" class="com.sue.async.service.UserFactoryBean"
  2. </bean> 

輕松搞定,so easy。

注意:getBean("userFactoryBean");獲取的是getObject方法中返回的對(duì)象。而getBean("&userFactoryBean");獲取的才是真正的UserFactoryBean對(duì)象。

我們通過(guò)上面五種方式,在bean.xml文件中把bean配置好之后,spring就會(huì)自動(dòng)掃描和解析相應(yīng)的標(biāo)簽,并且?guī)臀覀儎?chuàng)建和實(shí)例化bean,然后放入spring容器中。

雖說(shuō)基于xml文件的方式配置bean,簡(jiǎn)單而且非常靈活,比較適合一些小項(xiàng)目。但如果遇到比較復(fù)雜的項(xiàng)目,則需要配置大量的bean,而且bean之間的關(guān)系錯(cuò)綜復(fù)雜,這樣久而久之會(huì)導(dǎo)致xml文件迅速膨脹,非常不利于bean的管理。

2. Component注解

為了解決bean太多時(shí),xml文件過(guò)大,從而導(dǎo)致膨脹不好維護(hù)的問(wèn)題。在spring2.5中開(kāi)始支持:@Component、@Repository、@Service、@Controller等注解定義bean。

如果你有看過(guò)這些注解的源碼的話(huà),就會(huì)驚奇得發(fā)現(xiàn):其實(shí)后三種注解也是@Component。

@Component系列注解的出現(xiàn),給我們帶來(lái)了極大的便利。我們不需要像以前那樣在bean.xml文件中配置bean了,現(xiàn)在只用在類(lèi)上加Component、Repository、Service、Controller,這四種注解中的任意一種,就能輕松完成bean的定義。

  1. @Service 
  2. public class PersonService { 
  3.     public String get() { 
  4.         return "data"
  5.     } 

其實(shí),這四種注解在功能上沒(méi)有特別的區(qū)別,不過(guò)在業(yè)界有個(gè)不成文的約定:

  • Controller 一般用在控制層
  • Service 一般用在業(yè)務(wù)層
  • Repository 一般用在數(shù)據(jù)層
  • Component 一般用在公共組件上

太棒了,簡(jiǎn)直一下子解放了我們的雙手。

不過(guò),需要特別注意的是,通過(guò)這種@Component掃描注解的方式定義bean的前提是:需要先配置掃描路徑。

目前常用的配置掃描路徑的方式如下:

在applicationContext.xml文件中使用標(biāo)簽。例如:

  1. <context:component-scan base-package="com.sue.cache" /> 

在springboot的啟動(dòng)類(lèi)上加上@ComponentScan注解,例如:

  1. @ComponentScan(basePackages = "com.sue.cache"
  2. @SpringBootApplication 
  3. public class Application { 
  4.  
  5.     public static void main(String[] args) { 
  6.         new SpringApplicationBuilder(Application.class).web(WebApplicationType.SERVLET).run(args); 
  7.     } 

直接在SpringBootApplication注解上加,它支持ComponentScan功能:

  1. @SpringBootApplication(scanBasePackages = "com.sue.cache"
  2. public class Application { 
  3.      
  4.     public static void main(String[] args) { 
  5.         new SpringApplicationBuilder(Application.class).web(WebApplicationType.SERVLET).run(args); 
  6.     } 

當(dāng)然,如果你需要掃描的類(lèi)跟springboot的入口類(lèi),在同一級(jí)或者子級(jí)的包下面,無(wú)需指定scanBasePackages參數(shù),spring默認(rèn)會(huì)從入口類(lèi)的同一級(jí)或者子級(jí)的包去找。

  1. @SpringBootApplication 
  2. public class Application { 
  3.      
  4.     public static void main(String[] args) { 
  5.         new SpringApplicationBuilder(Application.class).web(WebApplicationType.SERVLET).run(args); 
  6.     } 

此外,除了上述四種@Component注解之外,springboot還增加了@RestController注解,它是一種特殊的@Controller注解,所以也是@Component注解。

@RestController還支持@ResponseBody注解的功能,即將接口響應(yīng)數(shù)據(jù)的格式自動(dòng)轉(zhuǎn)換成json。

@Component系列注解已經(jīng)讓我們愛(ài)不釋手了,它目前是我們?nèi)粘9ぷ髦凶疃嗟亩xbean的方式。

3. JavaConfig

@Component系列注解雖說(shuō)使用起來(lái)非常方便,但是bean的創(chuàng)建過(guò)程完全交給spring容器來(lái)完成,我們沒(méi)辦法自己控制。

spring從3.0以后,開(kāi)始支持JavaConfig的方式定義bean。它可以看做spring的配置文件,但并非真正的配置文件,我們需要通過(guò)編碼java代碼的方式創(chuàng)建bean。例如:

  1. public class MyConfiguration { 
  2.  
  3.     @Bean 
  4.     public Person person() { 
  5.         return new Person(); 
  6.     } 

在JavaConfig類(lèi)上加@Configuration注解,相當(dāng)于配置了標(biāo)簽。而在方法上加@Bean注解,相當(dāng)于配置了標(biāo)簽。

此外,springboot還引入了一些列的@Conditional注解,用來(lái)控制bean的創(chuàng)建。

  1. @Configuration 
  2. public class MyConfiguration { 
  3.  
  4.     @ConditionalOnClass(Country.class) 
  5.     @Bean 
  6.     public Person person() { 
  7.         return new Person(); 
  8.     } 

@ConditionalOnClass注解的功能是當(dāng)項(xiàng)目中存在Country類(lèi)時(shí),才實(shí)例化Person類(lèi)。換句話(huà)說(shuō)就是,如果項(xiàng)目中不存在Country類(lèi),就不實(shí)例化Person類(lèi)。

這個(gè)功能非常有用,相當(dāng)于一個(gè)開(kāi)關(guān)控制著Person類(lèi),只有滿(mǎn)足一定條件才能實(shí)例化。

spring中使用比較多的Conditional還有:

  • ConditionalOnBean
  • ConditionalOnProperty
  • ConditionalOnMissingClass
  • ConditionalOnMissingBean
  • ConditionalOnWebApplication

如果你對(duì)這些功能比較感興趣,可以看看《spring中那些讓你愛(ài)不釋手的代碼技巧(續(xù)集)》,這是我之前寫(xiě)的一篇文章,里面做了更詳細(xì)的介紹。

下面用一張圖整體認(rèn)識(shí)一下@Conditional家族:

nice,有了這些功能,我們終于可以告別麻煩的xml時(shí)代了。

4. Import注解

通過(guò)前面介紹的@Configuration和@Bean相結(jié)合的方式,我們可以通過(guò)代碼定義bean。但這種方式有一定的局限性,它只能創(chuàng)建該類(lèi)中定義的bean實(shí)例,不能創(chuàng)建其他類(lèi)的bean實(shí)例,如果我們想創(chuàng)建其他類(lèi)的bean實(shí)例該怎么辦呢?

這時(shí)可以使用@Import注解導(dǎo)入。

4.1 普通類(lèi)

spring4.2之后@Import注解可以實(shí)例化普通類(lèi)的bean實(shí)例。例如:

先定義了Role類(lèi):

  1. @Data 
  2. public class Role { 
  3.     private Long id; 
  4.     private String name

接下來(lái)使用@Import注解導(dǎo)入Role類(lèi):

  1. @Import(Role.class) 
  2. @Configuration 
  3. public class MyConfig { 

然后在調(diào)用的地方通過(guò)@Autowired注解注入所需的bean。

  1. @RequestMapping("/"
  2. @RestController 
  3. public class TestController { 
  4.  
  5.     @Autowired 
  6.     private Role role; 
  7.  
  8.     @GetMapping("/test"
  9.     public String test() { 
  10.         System.out.println(role); 
  11.         return "test"
  12.     } 

聰明的你可能會(huì)發(fā)現(xiàn),我沒(méi)有在任何地方定義過(guò)Role的bean,但spring卻能自動(dòng)創(chuàng)建該類(lèi)的bean實(shí)例,這是為什么呢?

這也許正是@Import注解的強(qiáng)大之處。

此時(shí),有些朋友可能會(huì)問(wèn):@Import注解能定義單個(gè)類(lèi)的bean,但如果有多個(gè)類(lèi)需要定義bean該怎么辦呢?

恭喜你,這是個(gè)好問(wèn)題,因?yàn)锧Import注解也支持。

  1. @Import({Role.class, User.class}) 
  2. @Configuration 
  3. public class MyConfig { 

甚至,如果你想偷懶,不想寫(xiě)這種MyConfig類(lèi),springboot也歡迎。

  1. @Import({Role.class, User.class}) 
  2. @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, 
  3.         DataSourceTransactionManagerAutoConfiguration.class}) 
  4. public class Application { 
  5.  
  6.     public static void main(String[] args) { 
  7.         new SpringApplicationBuilder(Application.class).web(WebApplicationType.SERVLET).run(args); 
  8.     } 

可以將@Import加到springboot的啟動(dòng)類(lèi)上。

這樣也能生效?

springboot的啟動(dòng)類(lèi)一般都會(huì)加@SpringBootApplication注解,該注解上加了@SpringBootConfiguration注解。

而@SpringBootConfiguration注解,上面又加了@Configuration注解。

所以,springboot啟動(dòng)類(lèi)本身帶有@Configuration注解的功能。

意不意外?驚不驚喜?

4.2 Configuration類(lèi)

上面介紹了@Import注解導(dǎo)入普通類(lèi)的方法,它同時(shí)也支持導(dǎo)入Configuration類(lèi)。

先定義一個(gè)Configuration類(lèi):

  1. @Configuration 
  2. public class MyConfig2 { 
  3.  
  4.     @Bean 
  5.     public User user() { 
  6.         return  new User(); 
  7.     } 
  8.  
  9.     @Bean 
  10.     public Role role() { 
  11.         return new Role(); 
  12.     } 

然后在另外一個(gè)Configuration類(lèi)中引入前面的Configuration類(lèi):

  1. @Import({MyConfig2.class}) 
  2. @Configuration 
  3. public class MyConfig { 

這種方式,如果MyConfig2類(lèi)已經(jīng)在spring指定的掃描目錄或者子目錄下,則MyConfig類(lèi)會(huì)顯得有點(diǎn)多余。因?yàn)镸yConfig2類(lèi)本身就是一個(gè)配置類(lèi),它里面就能定義bean。

但如果MyConfig2類(lèi)不在指定的spring掃描目錄或者子目錄下,則通過(guò)MyConfig類(lèi)的導(dǎo)入功能,也能把MyConfig2類(lèi)識(shí)別成配置類(lèi)。這就有點(diǎn)厲害了喔。

其實(shí)下面還有更高端的玩法。

swagger作為一個(gè)優(yōu)秀的文檔生成框架,在spring項(xiàng)目中越來(lái)越受歡迎。接下來(lái),我們以swagger2為例,介紹一下它是如何導(dǎo)入相關(guān)類(lèi)的。

眾所周知,我們引入swagger相關(guān)jar包之后,只需要在springboot的啟動(dòng)類(lèi)上加上@EnableSwagger2注解,就能開(kāi)啟swagger的功能。

其中@EnableSwagger2注解中導(dǎo)入了Swagger2DocumentationConfiguration類(lèi)。

該類(lèi)是一個(gè)Configuration類(lèi),它又導(dǎo)入了另外兩個(gè)類(lèi):

  • SpringfoxWebMvcConfiguration
  • SwaggerCommonConfiguration

SpringfoxWebMvcConfiguration類(lèi)又會(huì)導(dǎo)入新的Configuration類(lèi),并且通過(guò)@ComponentScan注解掃描了一些其他的路徑。

SwaggerCommonConfiguration同樣也通過(guò)@ComponentScan注解掃描了一些額外的路徑。

如此一來(lái),我們通過(guò)一個(gè)簡(jiǎn)單的@EnableSwagger2注解,就能輕松的導(dǎo)入swagger所需的一系列bean,并且擁有swagger的功能。

還有什么好說(shuō)的,狂起點(diǎn)贊,簡(jiǎn)直完美。

4.3 ImportSelector

上面提到的Configuration類(lèi),它的功能非常強(qiáng)大。但怎么說(shuō)呢,它不太適合加復(fù)雜的判斷條件,根據(jù)某些條件定義這些bean,根據(jù)另外的條件定義那些bean。

那么,這種需求該怎么實(shí)現(xiàn)呢?

這時(shí)就可以使用ImportSelector接口了。

首先定義一個(gè)類(lèi)實(shí)現(xiàn)ImportSelector接口:

  1. public class DataImportSelector implements ImportSelector { 
  2.     @Override 
  3.     public String[] selectImports(AnnotationMetadata importingClassMetadata) { 
  4.         return new String[]{"com.sue.async.service.User""com.sue.async.service.Role"}; 
  5.     } 

重寫(xiě)selectImports方法,在該方法中指定需要定義bean的類(lèi)名,注意要包含完整路徑,而非相對(duì)路徑。

然后在MyConfig類(lèi)上@Import導(dǎo)入這個(gè)類(lèi)即可:

  1. @Import({DataImportSelector.class}) 
  2. @Configuration 
  3. public class MyConfig { 

朋友們是不是又發(fā)現(xiàn)了一個(gè)新大陸?

不過(guò),這個(gè)注解還有更牛逼的用途。

@EnableAutoConfiguration注解中導(dǎo)入了AutoConfigurationImportSelector類(lèi),并且里面包含系統(tǒng)參數(shù)名稱(chēng):spring.boot.enableautoconfiguration。

AutoConfigurationImportSelector類(lèi)實(shí)現(xiàn)了ImportSelector接口。

并且重寫(xiě)了selectImports方法,該方法會(huì)根據(jù)某些注解去找所有需要?jiǎng)?chuàng)建bean的類(lèi)名,然后返回這些類(lèi)名。其中在查找這些類(lèi)名之前,先調(diào)用isEnabled方法,判斷是否需要繼續(xù)查找。

該方法會(huì)根據(jù)ENABLED_OVERRIDE_PROPERTY的值來(lái)作為判斷條件。

而這個(gè)值就是spring.boot.enableautoconfiguration。

換句話(huà)說(shuō),這里能根據(jù)系統(tǒng)參數(shù)控制bean是否需要被實(shí)例化,優(yōu)秀。

我個(gè)人認(rèn)為實(shí)現(xiàn)ImportSelector接口的好處主要有以下兩點(diǎn):

  • 把某個(gè)功能的相關(guān)類(lèi),可以放到一起,方面管理和維護(hù)。
  • 重寫(xiě)selectImports方法時(shí),能夠根據(jù)條件判斷某些類(lèi)是否需要被實(shí)例化,或者某個(gè)條件實(shí)例化這些bean,其他的條件實(shí)例化那些bean等。我們能夠非常靈活的定制化bean的實(shí)例化。

4.4 ImportBeanDefinitionRegistrar

我們通過(guò)上面的這種方式,確實(shí)能夠非常靈活的自定義bean。

但它的自定義能力,還是有限的,它沒(méi)法自定義bean的名稱(chēng)和作用域等屬性。

有需求,就有解決方案。

接下來(lái),我們一起看看ImportBeanDefinitionRegistrar接口的神奇之處。

先定義CustomImportSelector類(lèi)實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口:

  1. public class CustomImportSelector implements ImportBeanDefinitionRegistrar { 
  2.  
  3.     @Override 
  4.     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 
  5.         RootBeanDefinition roleBeanDefinition = new RootBeanDefinition(Role.class); 
  6.         registry.registerBeanDefinition("role", roleBeanDefinition); 
  7.  
  8.         RootBeanDefinition userBeanDefinition = new RootBeanDefinition(User.class); 
  9.         userBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE); 
  10.         registry.registerBeanDefinition("user", userBeanDefinition); 
  11.     } 

重寫(xiě)registerBeanDefinitions方法,在該方法中我們可以獲取BeanDefinitionRegistry對(duì)象,通過(guò)它去注冊(cè)bean。不過(guò)在注冊(cè)bean之前,我們先要?jiǎng)?chuàng)建BeanDefinition對(duì)象,它里面可以自定義bean的名稱(chēng)、作用域等很多參數(shù)。

然后在MyConfig類(lèi)上導(dǎo)入上面的類(lèi):

  1. @Import({CustomImportSelector.class}) 
  2. @Configuration 
  3. public class MyConfig { 

我們所熟悉的fegin功能,就是使用ImportBeanDefinitionRegistrar接口實(shí)現(xiàn)的:

具體細(xì)節(jié)就不多說(shuō)了,有興趣的朋友可以加我微信找我私聊。

5. PostProcessor

除此之外,spring還提供了專(zhuān)門(mén)注冊(cè)bean的接口:BeanDefinitionRegistryPostProcessor。

該接口的方法postProcessBeanDefinitionRegistry上有這樣一段描述:

修改應(yīng)用程序上下文的內(nèi)部bean定義注冊(cè)表標(biāo)準(zhǔn)初始化。所有常規(guī)bean定義都將被加載,但是還沒(méi)有bean被實(shí)例化。這允許進(jìn)一步添加在下一個(gè)后處理階段開(kāi)始之前定義bean。

如果用這個(gè)接口來(lái)定義bean,我們要做的事情就變得非常簡(jiǎn)單了。只需定義一個(gè)類(lèi)實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor接口。

  1. @Component 
  2. public class MyRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { 
  3.     @Override 
  4.     public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { 
  5.         RootBeanDefinition roleBeanDefinition = new RootBeanDefinition(Role.class); 
  6.         registry.registerBeanDefinition("role", roleBeanDefinition); 
  7.  
  8.         RootBeanDefinition userBeanDefinition = new RootBeanDefinition(User.class); 
  9.         userBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE); 
  10.         registry.registerBeanDefinition("user", userBeanDefinition); 
  11.     } 
  12.  
  13.     @Override 
  14.     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
  15.     } 

重寫(xiě)postProcessBeanDefinitionRegistry方法,在該方法中能夠獲取BeanDefinitionRegistry對(duì)象,它負(fù)責(zé)bean的注冊(cè)工作。

不過(guò)細(xì)心的朋友可能會(huì)發(fā)現(xiàn),里面還多了一個(gè)postProcessBeanFactory方法,沒(méi)有做任何實(shí)現(xiàn)。

這個(gè)方法其實(shí)是它的父接口:BeanFactoryPostProcessor里的方法。

在應(yīng)用程序上下文的標(biāo)準(zhǔn)bean工廠(chǎng)之后修改其內(nèi)部bean工廠(chǎng)初始化。所有bean定義都已加載,但沒(méi)有bean將被實(shí)例化。這允許重寫(xiě)或添加屬性甚至可以初始化bean。

  1. @Component 
  2. public class MyPostProcessor implements BeanFactoryPostProcessor { 
  3.  
  4.     @Override 
  5.     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
  6.         DefaultListableBeanFactory registry = (DefaultListableBeanFactory)beanFactory; 
  7.         RootBeanDefinition roleBeanDefinition = new RootBeanDefinition(Role.class); 
  8.         registry.registerBeanDefinition("role", roleBeanDefinition); 
  9.  
  10.         RootBeanDefinition userBeanDefinition = new RootBeanDefinition(User.class); 
  11.         userBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE); 
  12.         registry.registerBeanDefinition("user", userBeanDefinition); 
  13.     } 

既然這兩個(gè)接口都能注冊(cè)bean,那么他們有什么區(qū)別?

  • BeanDefinitionRegistryPostProcessor 更側(cè)重于bean的注冊(cè)
  • BeanFactoryPostProcessor 更側(cè)重于對(duì)已經(jīng)注冊(cè)的bean的屬性進(jìn)行修改,雖然也可以注冊(cè)bean。

此時(shí),有些朋友可能會(huì)問(wèn):既然拿到BeanDefinitionRegistry對(duì)象就能注冊(cè)bean,那通過(guò)BeanFactoryAware的方式是不是也能注冊(cè)bean呢?

從下面這張圖能夠看出DefaultListableBeanFactory就實(shí)現(xiàn)了BeanDefinitionRegistry接口。

這樣一來(lái),我們?nèi)绻軌颢@取DefaultListableBeanFactory對(duì)象的實(shí)例,然后調(diào)用它的注冊(cè)方法,不就可以注冊(cè)bean了?

說(shuō)時(shí)遲那時(shí)快,定義一個(gè)類(lèi)實(shí)現(xiàn)BeanFactoryAware接口:

  1. @Component 
  2. public class BeanFactoryRegistry implements BeanFactoryAware { 
  3.     @Override 
  4.     public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 
  5.         DefaultListableBeanFactory registry = (DefaultListableBeanFactory) beanFactory; 
  6.         RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class); 
  7.         registry.registerBeanDefinition("user", rootBeanDefinition); 
  8.  
  9.         RootBeanDefinition userBeanDefinition = new RootBeanDefinition(User.class); 
  10.         userBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE); 
  11.         registry.registerBeanDefinition("user", userBeanDefinition); 
  12.     } 

重寫(xiě)setBeanFactory方法,在該方法中能夠獲取BeanFactory對(duì)象,它能夠強(qiáng)制轉(zhuǎn)換成DefaultListableBeanFactory對(duì)象,然后通過(guò)該對(duì)象的實(shí)例注冊(cè)bean。

當(dāng)你滿(mǎn)懷喜悅的運(yùn)行項(xiàng)目時(shí),發(fā)現(xiàn)竟然報(bào)錯(cuò)了:

為什么會(huì)報(bào)錯(cuò)?

spring中bean的創(chuàng)建過(guò)程順序,大致如下:

BeanFactoryAware接口是在bean創(chuàng)建成功,并且完成依賴(lài)注入之后,在真正初始化之前才被調(diào)用的。在這個(gè)時(shí)候去注冊(cè)bean意義不大,因?yàn)檫@個(gè)接口是給我們獲取bean的,并不建議去注冊(cè)bean,會(huì)引發(fā)很多問(wèn)題。

此外,ApplicationContextRegistry和ApplicationListener接口也有類(lèi)似的問(wèn)題,我們可以用他們獲取bean,但不建議用它們注冊(cè)bean。

 

責(zé)任編輯:武曉燕 來(lái)源: 蘇三說(shuō)技術(shù)
相關(guān)推薦

2022-02-14 12:04:43

前綴SpringJpa

2020-07-07 07:37:36

Integer源碼Java

2020-11-03 06:57:10

MyBatis數(shù)據(jù)庫(kù)

2019-09-18 15:20:16

MyBatisSQL數(shù)據(jù)庫(kù)

2020-04-02 07:31:53

RPC超時(shí)服務(wù)端

2024-07-05 11:47:43

2020-11-27 09:16:21

BlockingQue

2022-09-04 12:43:03

算法裁員Meta

2020-01-06 09:14:59

Java程序員線(xiàn)程

2009-06-17 17:04:37

BeanFactorySpring

2019-06-14 08:48:46

Tomcat日志SpringBoot

2021-03-17 11:47:37

tomcatJavaServerJava

2015-05-19 14:30:48

加密視頻加密億賽通

2015-07-20 15:26:56

WiFi感知

2023-07-27 08:14:29

2021-11-02 11:31:47

Go代碼模式

2021-12-13 22:52:37

iphone iOSHTML

2022-06-24 14:52:34

AI模型

2021-05-28 10:09:22

GC詳解Java JVM

2021-12-08 08:30:55

Java AQS機(jī)制 Java 基礎(chǔ)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 999久久久国产精品 欧美成人h版在线观看 | 蜜桃av鲁一鲁一鲁一鲁 | 欧美在线a | 羞羞视频免费观看入口 | 国产精品久久久久999 | 一区二区三区久久久 | 欧美色综合| 国产精品区二区三区日本 | 一区二区三区亚洲 | 91av在线免费看 | 久久久久亚洲av毛片大全 | 午夜a级理论片915影院 | 国产精品久久国产精品99 | 波多野吉衣久久 | 97精品国产一区二区三区 | 国产精品一区在线观看 | 男女羞羞视频在线免费观看 | 91久久久久久 | 精品国产欧美一区二区三区成人 | av天空| 99久久国产综合精品麻豆 | 亚洲a视频| 超碰伊人久久 | 欧美日韩福利视频 | 成人毛片视频在线播放 | 天天干干 | 国产成人99久久亚洲综合精品 | 色久伊人 | 无码国模国产在线观看 | 国产一区二区在线91 | 久久精品国产99国产精品 | 一区二区影视 | 色婷婷av久久久久久久 | 日韩视频―中文字幕 | 51ⅴ精品国产91久久久久久 | www.日本在线观看 | 日韩成人一区 | 国产在线精品一区二区三区 | 日韩中文字幕免费在线观看 | 欧美成人手机视频 | 国产精品99久久久久久久久久久久 |