如何手动注册Bean
技术介绍
Spring-boot-mybatis-starter 是如何将mapper放入Bean容器的,feign是怎么让接口能够autowired的,都是通过springboot的手动注册完成的。
BeanDefinitionRegistryPostProcessor部分
BeanDefinitionRegistryPostProcessor是springboot手动注册bean 的组件之一,于其相似的还有importBeanDefinitionRegistrar,区别在于前者需要注解@Component,后者需要别的组件@Import。
值得注意的是无论前者还是后者,都无法直接在类中@Autowired,需要实现ApplicationContextAware接口变相取得已注册Bean。以下为主要代码:
@Component
public class Test implements BeanDefinitionRegistryPostProcessor , ApplicationContextAware {
@Autowired
ILogger logger; // logger == null 这里依赖注入是无效的
private static ApplicationContext applicationContext; // 上下文,在这里用来取Bean
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 手动注册的Bean类型 , 由于我要注册的其实是 接口的实现类
// 所以采用FactoryBean方式创建Bean
// 若仅仅需要对Bean注入值,可以直接指定想创建的Bean的class
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TestFactory.class); //这里可以直接改成TestFactory.class
AbstractBeanDefinition definition = builder.getRawBeanDefinition();
//亦可通过setBeanClass方式指定创建的类
//definition.setBeanClass(TestFactory.class);
//由于TestBean同时有两个不同名的Bean实例,需要手动指定注入
TestBean c = applicationContext.getBean("testBean", TestBean.class);
definition.getPropertyValues().addPropertyValue("testBean",c); //set 方式autowired,需要指定属性有set方法
// 构造方法autowired,FactoryBean 的 construct 方法必须有 , 两种注入方式选其一
// definition.getConstructorArgumentValues().addGenericArgumentValue();
// 对 TestFactory 中的所有属性默认使用 byType 的依赖注入,若同一class有多个bean,则会报错。
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
beanDefinitionRegistry.registerBeanDefinition(TestService.class.getName(), definition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("postProcessBeanFactory");
}
// 获得上下文
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
上文所述实体类
TestFactory extends FactoryBean 创建Bean的工厂
实现了FactoryBean的类在注册Bean的时候,会调用getObject和getObjectType并注册他们(相当于@Bean),而不会注册本身。
//@Component //这里注解入bean 会导致提前触发getObject创建Bean 导致重复创建Bean的问题
@Getter
@Setter
//如果需要注册的bean实现了FactoryBean 接口,则会注册getObject以及getObjectType的结果而不是其本身
public class TestFactory implements FactoryBean {
ILogger iLogger;
TestBean testBean;
//相当于 @Bean return的东西
//如果是类似@Mapper那种效果感觉这里可以整个泛型
@Override
public TestService getObject() throws Exception {
System.out.println("iLogger : " + iLogger + "-" + "testBean :" + testBean);
if (testBean != null) {
testBean.sout();
}
return () -> System.out.println(1);
}
@Override
public Class<?> getObjectType() {
return TestService.class;
}
}
注册所用接口 TestService
public interface TestService {
void invoke();
}
TestBean 测试依赖注入用的Bean 意义不大
@Component
public class TestBean {
public void sout() {
System.out.println(this.getClass().getName());
}
//创建两个Bean,方便展示手动注册如何处理多个同类型Bean注入
@Bean
public TestBean testBean2() {
return new TestBean() {
@Override
public void sout() {
System.out.println("override:"+this.getClass().getName());
}
};
}
}