BeanFactoryPostProcessor
BeanDefinitionRegistryPostProcessor
ImportBeanDefinitionRegistrar
有的時候神汹,我們需要在spring運行的時候,根據(jù)需要 動態(tài)添加之前沒有定義的spring的bean,比如mybatis掃描某個包下的接口,轉(zhuǎn)化為Mapper Bean础废。
Bean的定義在Spring中的組件名叫:BeanDefinition饥伊。
當(dāng)我們創(chuàng)建了一個BeanDefinion后可以通過BeanDefinitionRegistry接口(ApplicationContext會實現(xiàn)此接口)將新的BeanDefinition注冊到spring上下文中。
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
BeanFactoryPostProcessor(Bean工廠的后置處理器)
這個接口的作用是在Spring上下文的注冊Bean定義的邏輯都跑完后智亮,但是所有的Bean都還沒真正實例化之前調(diào)用俄删。
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
這個方法的主要用途就是通過注入進來的BeanFactory宏怔,在真正初始化Bean之前奏路,再對spring上下文做一些動態(tài)修改。增加或者修改某些Bean定義的值臊诊,甚至再動態(tài)創(chuàng)建一些BeanDefinition都可以鸽粉。
Demo:
@Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition beanDefinition = getGenericBeanDefinition();
((DefaultListableBeanFactory) beanFactory)
.registerBeanDefinition("dynamicUser", beanDefinition);
}
private GenericBeanDefinition getGenericBeanDefinition() {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(User.class);
beanDefinition.getPropertyValues().add("name", "張三");
return beanDefinition;
}
BeanDefinitionRegistryPostProcessor(Bean注冊器的后置處理器)
如上我們可以定義一個有關(guān)User這個Bean的定義,并且預(yù)先設(shè)置此Bean的Name屬性值是
“張三”妨猩,最后注冊到beanFactory中潜叛。
有鑒于此秽褒,Spring特意提供了一個更具體的子接口BeanDefinitionRegistryPostProcessor
這個子接口的方法postProcessBeanDefinitionRegistry會被Spring先于postProcessBeanFactory這個方法執(zhí)行壶硅。不過呢,其實這2個方法都是注入的spring的上下文销斟,只不過聲明類型不同罷了庐椒。所以可以做的事一模一樣。
private GenericBeanDefinition getGenericBeanDefinition() {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(User.class);
beanDefinition.getPropertyValues().add("name", "張三");
return beanDefinition;
}
@Override
public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry) throws BeansException {
GenericBeanDefinition beanDefinition = getGenericBeanDefinition();
registry.registerBeanDefinition("dynamicUser2",beanDefinition);
}
最后調(diào)用此spring上下文得到這個2個Bean:
public static void main(String[] args)
{
ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args);
User dynamicUser = ctx.getBean("dynamicUser", User.class);
System.out.println(dynamicUser);
User dynamicUser2 = ctx.getBean("dynamicUser2", User.class);
System.out.println(dynamicUser2);
}
BeanDefinitionBuilder
建造者模式去創(chuàng)建一個BeanDefinition蚂踊。從2.5之后GenericBeanDefinition代替了原本的RootBeanDefinition和ChildBeanDefinition约谈。我們通常使用GenericBeanDefinition就足夠了。
我們通過Builder模式去設(shè)置BeanDefinition一些常用方法:
//設(shè)置Bean的構(gòu)造函數(shù)傳入的參數(shù)值
public BeanDefinitionBuilder addConstructorArgValue(Object value)
//設(shè)置構(gòu)造函數(shù)引用其他的bean
public BeanDefinitionBuilder addConstructorArgReference(String beanName)
//設(shè)置這個bean的 init方法和destory方法
public BeanDefinitionBuilder setInitMethodName(String methodName)
public BeanDefinitionBuilder setDestroyMethodName(String methodName)
//設(shè)置單例/多例
public BeanDefinitionBuilder setScope(String scope)
//設(shè)置是否是個抽象的BeanDefinition犁钟,如果為true棱诱,表明這個BeanDefinition只是用來給子BeanDefinition去繼承的,Spring不會去嘗試初始化這個Bean涝动。
public BeanDefinitionBuilder setAbstract(boolean flag)
//是否懶加載迈勋,默認(rèn)是false
public BeanDefinitionBuilder setLazyInit(boolean lazy)
//自動注入依賴的模式,默認(rèn)不注入
public BeanDefinitionBuilder setAutowireMode(int autowireMode)
//檢測依賴醋粟。
public BeanDefinitionBuilder setDependencyCheck(int dependencyCheck)
以上靡菇,參考資料:http://www.logicbig.com/tutorials/spring-framework/spring-core/bean-definition/
ImportBeanDefinitionRegistrar
這個接口的實現(xiàn)類的作用是當(dāng)這個接口的實現(xiàn)類被@Import接口引入某個被標(biāo)記了@Configuration的注冊類時,可以得到這個類的所有注解米愿,然后做一些動態(tài)注冊Bean的事兒厦凤。
比如在使用spring cloud feign組件的時候,我們這些寫的
@SpringBootApplication
@EnableFeignClients(basePackages = "newbie.yyf.service.api.api")
public class Application {
觀察EnableFeignClients這個注解里面是不是就使用了
"@Import(FeignClientsRegistrar.class)"
而FeignClientsRegistrar就實現(xiàn)了ImportBeanDefinitionRegistrar接口育苟,所以這個接口的實現(xiàn):
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
這里面的AnnotationMetadata到底會包含什么呢较鼓?其實就是被Import標(biāo)記的Application這個注冊文件的所有注解。
我們可以通過拿到注解上的一些配置信息去動態(tài)生成BeanDefinition违柏,如果你熟悉Feign的作用的話笨腥,你可以想到主要是Feign的客戶端接口的Mock Bean,就像mybatis一樣勇垛。