1、实现 BeanDefinitionRegistryPostProcessor 接口
该接口其实是 BeanFactoryPostProcessor 接口的一个衍生子类,除了父类提供的 postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 获取 BeanFactory 对象的方法,子类还扩充了一个可以获取 postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 注册器的方法。有了 BeanFactory 工厂和 BeanDefinitionRegistry 注册器你就可以在对象实例化之前做很多事情(修改某个类中的属性值、添加或者删除 BeanDefinition 等)
1.1、通过 BeanDefinitionRegistry 注册 BeanDefinition 模版
我们先定义一个类 Apple,代码如下
public class Apple {
}
然后要讲这个类交给 Spring 管理,也就是将这个类放到 Spring 的单例缓冲池中,这里借助 BeanDefinitionRegistryPostProcessor 接口来实现这需求,这里首先通过 postProcessBeanDefinitionRegistry() 方法,我们可以拿到注册器,注册器就会把 BeanDefinition 往 BeanFactory 加工厂中注册。 代码如下:
@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 直接手动 new 一个 GenericBeanDefinition,因为 Spring 会根据 BeanDefinition
// 帮我们生产 bean 实例
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(ProcessorEntity.class);
// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
}
}
这个方法就是通过自己去创建 BeanDefinition 模版,然后将这个模版交给了 Spring 管理,就可以帮我们生产处我们想要的 ProcessorEntity 对象。那么我们不想这样干,可不可以不定义 BeanDefinition 就直接注册的到 Spring 容器(其实就是 BeanFactory 容器中)中呢。绝对可以的,看下面操作。
1.2、通过 ConfigurableListableBeanFactory 注册对象
依旧是借助 BeanDefinitionRegistryPostProcessor 接口,只不过这次借助 postProcessBeanFactory() 方法,我们可以在这个方法中获取到 BeanFactory,直接往通过这个 BeanFactory 暴露的 API 往容器中扔对象。
BeanFactory 工厂就提供了很多的方法给我们做 CRUD 操作了。
@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 获取到默认工厂 DefaultListableBeanFactory 啥都好办兄弟
// 接口 ConfigurableListableBeanFactory 方法太少了,强转成 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)context.getBeanFactory();
// 直接通过手动方式将类交给 Spring 管理
beanFactory.registerSingleton("apple", new Apple());
// 删除一、二、三级缓冲池中创建好的实例 bean
// beanFactory.destroySingleton("apple");
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 直接手动 new 一个 GenericBeanDefinition,因为 Spring 会根据 BeanDefinition
// 帮我们生产 bean 实例
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(ProcessorEntity.class);
// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
}
}
BeanFactory 因为接口中方法比较少,所以我们可以通过他的子类来获取更多的 API 操作,第一时间想到的必须是默认工厂 DefaultListableBeanFactory。最终通过调用 getBean(Apple.class) 是可以获取到 Apple 实例的。
2、通过 AnnotationConfigApplicationContext 注解工厂
这个其实类似于上面的 BeanFactory 直接注册,只不过不需要去实现 BeanDefinitionRegistryPostProcessor 接口而已。代码如下:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class);
// 获取到默认工厂 DefaultListableBeanFactory 啥都好办兄弟
// 接口 ApplicationContext 方法太少了
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)context.getBeanFactory();
// 直接通过手动方式将类交给 Spring 管理
beanFactory.registerSingleton("apple", new Apple());
Apple apple = context.getBean(Apple.class);
System.out.println("apple = " + apple);
// 删除一、二、三级缓冲池中创建好的实例 bean
// beanFactory.destroySingleton("apple");
}
最终通过调用 getBean(Apple.class) 是可以获取到 Apple 实例的。
3、通过 @Bean 注册实例
定义一个不带 @Component 而且不被 @ComponentScan 扫描到的实体类,如下:
public class Blue {
}
然后通过 @Bean 注解将上面的实体交给 Spring 管理,如下:
@BeansScanner(basePackage = "com.gwm.scan.beans")
public class TestBean {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class);
Blue blue = context.getBean(Blue.class);
System.out.println("blue = " + blue);
}
@Bean
public Blue blue() {
System.out.println("======>invoke blue...");
return new Blue();
}
}
这种方法的原理其实很简单,就是 Spring 通过反射调用 @Bean 修饰的方法,然后将他返回的结果注册到了 Spring 容器中而已。
4、实现 BeanFactoryAware 接口
代码如下:
public class Dog {
}
@Component
public class DogAware implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory factory = (DefaultListableBeanFactory)beanFactory;
factory.registerSingleton("dog",new Dog());
}
}
@ComponentScan
public class BeanAwareDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanAwareDemo.class);
Dog dog = context.getBean(Dog.class);
System.out.println("dog = " + dog);
}
}
这个其实本质还是通过获取到 BeanFactory 工厂直接将 Dog 交给 Spring 管理
5、实现 FactoryBean 接口
FactoryBean 是 Spring 中留个开发很好扩展并且自定义实例的一个机会,MyBatis 框架就利用这个接口完美整合
代码实例:
@Component
public class MyDogFactoryBean implements FactoryBean<Dog> {
@Override
public Dog getObject() throws Exception {
return new Dog();
}
@Override
public Class<?> getObjectType() {
return Dog.class;
}
}
这里需要注意以下三点:
1、beanName=&myDogFactoryBean 表示获取到的是 MyDogFactoryBean 类的实例,如果 beanName=myDogFactoryBean 表示获取到的是 Dog 类的实例。
2、只有使用到了 Dog 类才会触发 MyDogFactoryBean 类调用 getObject() 方法,有点懒加载的感觉。
3、MyDogFactoryBean 类实例存储在单例缓冲池中,Dog 类实例存在 FactoryBeanObjectCache 缓存中。
6、@Import 注解注入实例
@Import
可以用来导入第三方的提供的类,它有三种用法:
1、@Import(Apple.class) 直接导入 Apple 类,如下:
public class Apple {
}
@Configuration
@Import(Apple.class)
public class MainConfig {
}
2、@Import(AppleImportSelector.class) 通过实现接口 ImportSelector 来间接导入 Apple 类,如下:
public class Apple {
}
public class AppleImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{Apple.class.getName()};
}
}
@Configuration
@Import(AppleImportSelector.class)
public class MainConfig {
}
3、@Import(AppleImportBeanDefinitionRegistrar.class) 通过实现接口 ImportBeanDefinitionRegistrar 来间接导入 Apple 类,如下:
public class Apple {
}
public class AppleImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(Apple.class);
registry.registerBeanDefinition("apple",genericBeanDefinition);
}
}
@Configuration
@Import(AppleImportBeanDefinitionRegistrar.class)
public class MainConfig {
}