Spring 創(chuàng)建Bean的6種方式

前言

本文講解了在Spring 應(yīng)用中創(chuàng)建Bean的多種方式,包括自動(dòng)創(chuàng)建,以及手動(dòng)創(chuàng)建注入方式,實(shí)際開(kāi)發(fā)中可以根據(jù)業(yè)務(wù)場(chǎng)景選擇合適的方案。

方式1:

使用Spring XML方式配置,該方式用于在純Spring 應(yīng)用中,適用于簡(jiǎn)單的小應(yīng)用,當(dāng)應(yīng)用變得復(fù)雜,將會(huì)導(dǎo)致XMl配置文件膨脹 ,不利于對(duì)象管理。

<bean id="xxxx"? class="xxxx.xxxx"/>

方式2:

使用@Component,@Service,@Controler,@Repository注解

這幾個(gè)注解都是同樣的功能,被注解的類將會(huì)被Spring 容器創(chuàng)建單例對(duì)象。

@Component : 側(cè)重于通用的Bean類

@Service:標(biāo)識(shí)該類用于業(yè)務(wù)邏輯

@Controler:標(biāo)識(shí)該類為Spring MVC的控制器類

@Repository: 標(biāo)識(shí)該類是一個(gè)實(shí)體類,只有屬性和Setter,Getter

@Component

public class User{

}

當(dāng)用于Spring Boot應(yīng)用時(shí),被注解的類必須在啟動(dòng)類的根路徑或者子路徑下,否則不會(huì)生效。

如果不在,可以使用@ComponentScan標(biāo)注掃描的路徑。

spring xml 也有相關(guān)的標(biāo)簽<component-scan />

@ComponentScan(value={"com.microblog.blog","com.microblog.common"})

public class MicroblogBlogApplication {

? ? public static void main(String args[]){

? ? ? ? SpringApplication.run(MicroblogBlogApplication.class,args);

? ? }

}

方式3:

使用@Bean注解,這種方式用在Spring Boot 應(yīng)用中。

@Configuration 標(biāo)識(shí)這是一個(gè)Spring Boot 配置類,其將會(huì)掃描該類中是否存在@Bean 注解的方法,比如如下代碼,將會(huì)創(chuàng)建User對(duì)象并放入容器中。

@ConditionalOnBean 用于判斷存在某個(gè)Bean時(shí)才會(huì)創(chuàng)建User Bean.

這里創(chuàng)建的Bean名稱默認(rèn)為方法的名稱user。也可以@Bean("xxxx")定義。

@Configuration

public class UserConfiguration{


? ? ? @Bean

    @ConditionalOnBean(Location.class)

? ? ? public User user(){

? ? ? ? ? return new User();

? ? ? }


}? ?

Spring boot 還為我們提供了更多類似的注解。

圖片描述(最多50字)

也和方式2一樣,也會(huì)存在掃描路徑的問(wèn)題,除了以上的解決方式,還有使用Spring boot starter 的解決方式

在resources下創(chuàng)建如下文件。META-INF/spring.factories.

Spring Boot 在啟動(dòng)的時(shí)候?qū)?huì)掃描該文件,從何獲取到配置類UserConfiguration。

圖片描述(最多50字)

spring.factories.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.log.config.UserConfiguration

如果不成功,請(qǐng)引入該依賴

? ? <dependency>

? ? ? ? ? ? <groupId>org.springframework.boot</groupId>

? ? ? ? ? ? <artifactId>spring-boot-configuration-processor</artifactId>

? ? ? ? ? ? <optional>true</optional>

? ? ? ? </dependency>? ?

這個(gè)方式也是創(chuàng)建SpringBoot-starter的方式。

方式4:

使用注解@Import,也會(huì)創(chuàng)建對(duì)象并注入容器中

@Import(User.class)

public class MicroblogUserWebApplication {

? ? public static void main(String args[]) {

? ? ? ? SpringApplication.run(MicroblogUserWebApplication.class, args);

? ? }

}

方式5:

使用ImportSelector或者ImportBeanDefinitionRegistrar接口,配合@Import實(shí)現(xiàn)。

在使用一些Spring Boot第三方組件時(shí),經(jīng)常會(huì)看到@EnableXXX來(lái)使能相關(guān)的服務(wù),這里以一個(gè)例子來(lái)實(shí)現(xiàn)。

創(chuàng)建測(cè)試類

@Slf4j

public class House {

? ? public void run(){

? ? ? ? log.info("House? run ....");

? ? }

}

@Slf4j

public class User {

? ? public void run(){

? ? ? ? log.info("User? run ....");

? ? }

}

@Slf4j

public class Student {

? ? public void run(){

? ? ? ? log.info("Student? run ....");

? ? }

}

實(shí)現(xiàn)ImportSelector接口

selectImports方法的返回值為需要?jiǎng)?chuàng)建Bean的類名稱。這里創(chuàng)建User類。

@Slf4j

public class MyImportSelector implements ImportSelector {

? ? @Override

? ? public String[] selectImports(AnnotationMetadata annotationMetadata) {

? ? ? ? log.info("MyImportSelector selectImports ...");

? ? ? ? return new String[]{

? ? ? ? ? ? User.class.getName()};

? ? }

}

實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口

beanDefinitionRegistry.registerBeanDefinition用于設(shè)置需要?jiǎng)?chuàng)建Bean的類名稱。這里創(chuàng)建House類。

@Slf4j

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

? ? @Override

? ? public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

? ? ? ? log.info("MyImportBeanDefinitionRegistrar? registerBeanDefinitions .....");

? ? ? ? BeanDefinition beanDefinition =? new RootBeanDefinition(House.class.getName());

? ? ? ? beanDefinitionRegistry.registerBeanDefinition(House.class.getName(),beanDefinition);

? ? }

}

創(chuàng)建一個(gè)配置類

這里創(chuàng)建Student類。

@Configuration

public class ImportAutoconfiguration {

? ? @Bean

? ? public Student student(){

? ? ? ? return new Student();

? ? }

}

創(chuàng)建EnableImportSelector注解

EnableImportSelector注解上使用@Import,引入以上的三個(gè)類。

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Target(ElementType.TYPE)

@Import({MyImportSelector.class,ImportAutoconfiguration.class,MyImportBeanDefinitionRegistrar.class})

public @interface EnableImportSelector {

? ? String value();

}

測(cè)試

@EnableImportSelector(value = "xxx")

@SpringBootApplication

public class ImportDemoApplication {

? ? public static void main(String[] args) {

? ? ? ? ConfigurableApplicationContext context =? SpringApplication.run(ImportDemoApplication.class, args);

? ? ? ? User user =? context.getBean(User.class);

? ? ? ? user.run();

? ? ? ? Student student =? context.getBean(Student.class);

? ? ? ? student.run();

? ? ? ? House house =? context.getBean(House.class);

? ? ? ? house.run();

? ? }

}

輸出,可以看到,三個(gè)類User Student House都創(chuàng)建成功,都可從Spring 容器中獲取到。

2019-06-20 17:53:39.528? INFO 27255 --- [? ? ? ? ? main] com.springboot.importselector.pojo.User? : User? run ....

2019-06-20 17:53:39.530? INFO 27255 --- [? ? ? ? ? main] c.s.importselector.pojo.Student? ? ? ? ? : Student? run ....

2019-06-20 17:53:39.531? INFO 27255 --- [? ? ? ? ? main] c.springboot.importselector.pojo.House? : House? run ....

方式6

手動(dòng)注入Bean容器,有些場(chǎng)景下需要代碼動(dòng)態(tài)注入,以上方式都不適用。這時(shí)就需要?jiǎng)?chuàng)建 對(duì)象手動(dòng)注入。

通過(guò)DefaultListableBeanFactory注入。

registerSingleton(String beanName,Object object);

這里手動(dòng)使用new創(chuàng)建了一個(gè)Location對(duì)象。并注入容器中。

@Component

public class LocationRegister implements BeanFactoryAware {

? ? @Override

? ? public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

? ? ? ? DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory)beanFactory;

? ? ? ? Location location = new Location();

? ? ? ? listableBeanFactory.registerSingleton("location1",location);

? ? }

}

這種方式的應(yīng)用場(chǎng)景是為接口創(chuàng)建動(dòng)態(tài)代理對(duì)象,并向SPRING容器注冊(cè)。

比如MyBatis中的Mapper接口,Mapper沒(méi)有實(shí)現(xiàn)類,啟動(dòng)時(shí)創(chuàng)建動(dòng)態(tài)代理對(duì)象,將該對(duì)象注冊(cè)到容器中,使用時(shí)只要@Autowired注入即可使用,調(diào)用接口方法將會(huì)被代理攔截,進(jìn)而調(diào)用相關(guān)的SqlSession執(zhí)行相關(guān)的SQL業(yè)務(wù)邏輯。

可以看以下它的繼承體系

DefaultListableBeanFactory 是ConfigurableListableBeanFactory的實(shí)現(xiàn)類。是對(duì)BeanFactory功能的擴(kuò)展。

圖片描述(最多50字)

測(cè)試代碼和以上一樣

Location location =? context.getBean(Location.class);

location.run();

歡迎工作一到五年的Java工程師朋友們加入Java程序員開(kāi)發(fā): 721575865

群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個(gè)知識(shí)點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時(shí)間來(lái)學(xué)習(xí)提升自己,不要再用"沒(méi)有時(shí)間“來(lái)掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來(lái)的自己一個(gè)交代!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容