Bootstrap

将对象交给 Spring 管理的几种方式

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 {

}
;