詳解Spring Boot 使用Java代碼創(chuàng)建Bean并注冊(cè)到Spring中
從 Spring3.0 開(kāi)始,增加了一種新的途經(jīng)來(lái)配置Bean Definition,這就是通過(guò) Java Code 配置 Bean Definition。
與Xml和Annotation兩種配置方式不同點(diǎn)在于:
前兩種Xml和Annotation的配置方式為預(yù)定義方式,即開(kāi)發(fā)人員通過(guò) XML 文件或者 Annotation 預(yù)定義配置 bean 的各種屬性后,啟動(dòng) spring 容器,Spring 容器會(huì)首先解析這些配置屬性,生成對(duì)應(yīng)都?Bean Definition,裝入到 DefaultListableBeanFactory 對(duì)象的屬性容器中去。與此同時(shí),Spring 框架也會(huì)定義一些內(nèi)部使用的 Bean 定義,如 bean 名為”org.springframework.context.annotation.internalConfigurationAnnotationProcessor”的 ConfigurationClassPostProcessor 定義。
而后此刻不會(huì)做任何 Bean Definition 的定義解析動(dòng)作,Spring 框架會(huì)根據(jù)前兩種配置,過(guò)濾出 BeanDefinitionRegistryPostProcessor 類(lèi)型的 Bean 定義,并通過(guò) Spring 框架生成其對(duì)應(yīng)的 Bean 對(duì)象(如 ConfigurationClassPostProcessor 實(shí)例)。結(jié)合 Spring 上下文源碼可知這個(gè)對(duì)象是一個(gè) processor 類(lèi)型工具類(lèi),Spring 容器會(huì)在實(shí)例化開(kāi)發(fā)人員所定義的 Bean 前先調(diào)用該 processor 的 postProcessBeanDefinitionRegistry(…) 方法。此處實(shí)現(xiàn)基于 Java Code 配置Bean Definition的處理。
基于 Java Code 解析 Bean 的順序圖(查看大圖)
基于 Java Code 的配置方式,其執(zhí)行原理不同于前兩種。它是在 Spring 框架已經(jīng)解析了基于 XML 和 Annotation 配置后,通過(guò)加入 BeanDefinitionRegistryPostProcessor 類(lèi)型的 processor 來(lái)處理配置信息,讓開(kāi)發(fā)人員通過(guò) Java 編程方式定義一個(gè) Java 對(duì)象。其優(yōu)點(diǎn)在于可以將配置信息集中在一定數(shù)量的 Java 對(duì)象中,同時(shí)通過(guò) Java 編程方式,比基于 Annotation 方式具有更高的靈活性。并且該配置方式給開(kāi)發(fā)人員提供了一種非常好的范例來(lái)增加用戶(hù)自定義的解析工具類(lèi)。其主要缺點(diǎn)在于與 Java 代碼結(jié)合緊密,配置信息的改變需要重新編譯 Java 代碼,另外這是一種新引入的解析方式,需要一定的學(xué)習(xí)成本。
另外提及一點(diǎn)的就是,Spring框架有3個(gè)主要的Hook類(lèi),分別是:
org.springframework.context.ApplicationContextAware
它的setApplicationContext 方法將在Spring啟動(dòng)之前第一個(gè)被調(diào)用。我們用來(lái)同時(shí)啟動(dòng)Jdon框架。
org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
它的postProcessBeanDefinitionRegistry 和 postProcessBeanFactory 方法是第二和第三被調(diào)用,它們?cè)贐ean初始化創(chuàng)建之前啟動(dòng),如果Spring的bean需要的其他第三方中的組件,我們?cè)谶@里將其注入給Spring。
org.springframework.context.ApplicationListener
用于在初始化完成后做一些事情,當(dāng)Spring所有XML或元注解的Bean都啟動(dòng)被創(chuàng)建成功了,這時(shí)會(huì)調(diào)用它的唯一方法onApplicationEvent。
下面我們來(lái)完成一個(gè),自己通過(guò)java代碼創(chuàng)建bean,并注冊(cè)為Spring管理。
本例中,我們創(chuàng)建一個(gè)接口,然后創(chuàng)建該接口的2個(gè)實(shí)現(xiàn)類(lèi),分別命名不同的名字,然后在需要注入的地方使用@Qualifier 指定注入對(duì)應(yīng)的實(shí)例。
1、接口Shanhy.java
package org.springboot.sample.config; public interface Shanhy { void display(); }
2、實(shí)現(xiàn)類(lèi)ShanhyA.java
package org.springboot.sample.config; public class ShanhyA implements Shanhy { @Override public void display() { System.out.println("AAAAAAAAAAAA"); } }
3、實(shí)現(xiàn)類(lèi)ShanhyB.java
package org.springboot.sample.config; public class ShanhyB implements Shanhy { @Override public void display() { System.out.println("BBBBBBBBBBBB"); } }
4、定義接口BeanDefinitionRegistryPostProcessor的實(shí)現(xiàn)
package org.springboot.sample.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.annotation.AnnotationBeanNameGenerator; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.AnnotationScopeMetadataResolver; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ScopeMetadata; import org.springframework.context.annotation.ScopeMetadataResolver; /** * 實(shí)現(xiàn)自己實(shí)例化bean并注冊(cè)為Spring管理 * * @author 單紅宇(365384722) * @create 2016年1月21日 */ @Configuration public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { private static final Logger logger = LoggerFactory.getLogger(MyBeanDefinitionRegistryPostProcessor.class); private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { logger.info("Invoke Metho postProcessBeanFactory"); // 這里可以設(shè)置屬性,例如 BeanDefinition bd = beanFactory.getBeanDefinition("dataSourceA"); MutablePropertyValues mpv = bd.getPropertyValues(); mpv.addPropertyValue("driverClassName", "com.mysql.jdbc.Driver"); mpv.addPropertyValue("url", "jdbc:mysql://localhost:3306/test"); mpv.addPropertyValue("username", "root"); mpv.addPropertyValue("password", "123456"); } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { logger.info("Invoke Metho postProcessBeanDefinitionRegistry"); registerBean(registry, "shanhyA", ShanhyA.class); registerBean(registry, "shanhyB", ShanhyB.class); registerBean(registry, "dataSourceA", org.apache.tomcat.jdbc.pool.DataSource.class); } private void registerBean(BeanDefinitionRegistry registry, String name, Class<?> beanClass){ AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); // 可以自動(dòng)生成name String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); } }
5、使用測(cè)試
和平常一樣可以直接注入我們的對(duì)象,對(duì)于同樣接口的我們需要指定name
/** * 測(cè)試參數(shù)注入 * * @author 單紅宇(365384722) * @create 2016年1月13日 */ @Configuration public class MyConfiguration { @Bean public FilterRegistrationBean filterRegistrationBean(@Qualifier("shanhyB") Shanhy shanhy) { FilterRegistrationBean filterRegistration = new FilterRegistrationBean(); shanhy.display(); // 省略代碼 return filterRegistration; } }
使用@Resource 或者 @Autowired并指定@Qualifier 也可以
@RestController @RequestMapping("/hello") public class HelloController { @Resource(name="shanhyA") private Shanhy shanhyA; @Autowired @Qualifier("shanhyB") private Shanhy shanhyB; // 省略代碼 }
這里有點(diǎn)經(jīng)驗(yàn)要說(shuō)一下,在 @Configuration 中,不能使用注入屬性的方式注入,只能通過(guò)參數(shù)的方式注入,其原因就是@Configuration的類(lèi)一開(kāi)始變被加載,此時(shí)你想進(jìn)行屬性注入,需要注入的bean對(duì)象都還不存在呢。
下一篇文章,我們將使用這種方法動(dòng)態(tài)創(chuàng)建基于MyBatis的多數(shù)據(jù)源。
下面的代碼片段也可以注冊(cè)Bean,比較簡(jiǎn)單:
@Configuration @Import(Registrar.class) public class TestConfig { } class Registrar implements ImportBeanDefinitionRegistrar { private static final String BEAN_NAME = "myTestBean"; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(BEAN_NAME)) { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(ExamplePostProcessor.class); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(BEAN_NAME, beanDefinition); } } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Spring超詳細(xì)講解創(chuàng)建BeanDefinition流程
- SpringBoot詳細(xì)講解如何創(chuàng)建及刷新Spring容器bean
- Spring-Bean創(chuàng)建對(duì)象的步驟方式詳解
- Spring創(chuàng)建Bean的6種方式詳解
- Spring BPP中如何優(yōu)雅的創(chuàng)建動(dòng)態(tài)代理Bean詳解
- Spring工廠(chǎng)方法創(chuàng)建(實(shí)例化)bean實(shí)例代碼
- Spring創(chuàng)建Bean完成后執(zhí)行指定代碼的幾種實(shí)現(xiàn)方式
相關(guān)文章
java高級(jí)應(yīng)用:線(xiàn)程池的全面講解(干貨)
這篇文章主要介紹了java高級(jí)應(yīng)用:線(xiàn)程池的全面講解(干貨),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02Java實(shí)現(xiàn)Swing組件定制Button示例
這篇文章主要介紹了Java實(shí)現(xiàn)Swing組件定制Button,涉及java Swing組件Button相關(guān)屬性設(shè)置與使用操作技巧,需要的朋友可以參考下2018-01-01基于Java實(shí)現(xiàn)ssh命令登錄主機(jī)執(zhí)行shell命令過(guò)程解析
這篇文章主要介紹了基于Java實(shí)現(xiàn)ssh命令登錄主機(jī)執(zhí)行shell命令過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12詳解Java CompletableFuture使用方法以及與FutureTask的區(qū)別
CompletableFuture實(shí)現(xiàn)了CompletionStage接口和Future接口,前者是對(duì)后者的一個(gè)擴(kuò)展,增加了異步回調(diào)、流式處理、多個(gè)Future組合處理的能力,使Java在處理多任務(wù)的協(xié)同工作時(shí)更加順暢便利2021-10-10詳解elasticsearch之metric聚合實(shí)現(xiàn)示例
這篇文章主要為大家介紹了elasticsearch之metric聚合實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01深入理解 Java、Kotlin、Go 的線(xiàn)程和協(xié)程
這篇文章主要介紹了深入理解 Java、Kotlin、Go 的線(xiàn)程和協(xié)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12