常见注入Spring容器的六种方式
-
@Configuration
+@Bean
-
@ComponentScan
+@Component
-
@Import
配合接口进行导入 - 使用
FactoryBean
- 实现
BeanDefinitionRegistryPostProcessor
进行后置处理 -
Supplier
接口,但很少使用
1.@Configuration + @Bean
@Configuration
用来声明一个配置类,然后使用 @Bean
注解,用于声明一个bean,将其加入到Spring容器中。这种方式是我们最常用的一种
@Configuration
public class MyConfiguration {
@Bean
public Person person() {
Person person = new Person();
person.setName("spring");
return person;
}
}
2.@Component + @ComponentScan
@Componet中文译为组件,放在类名上面,然后@ComponentScan放置在我们的配置类上,然后可以指定一个路径,进行扫描带有@Componet
注解的bean,然后加至容器中。这种方式也较为常用,spring扫描包路径就是使用这种方式,这样可以一下子扫描很多个bean到容器
// 该类在com.shawn.*包下面
@Component
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
//*代表该包下匹配的所有包和类
@ComponentScan(basePackages = "com.shawn.*")
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
//结果输出Person{name='null'}
System.out.println(bean);
}
}
3.使用FactoryBean接口
1) 什么是FactoryBean
FactoryBean
是为IOC容器中的Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上,给Bean的实现加上了一个简单工厂模式和装饰模式。
一般情况下实例化一个Bean对象:Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。为此Spring提供了该接口,用户可以通过实现该接口,然后在getObject()
方法中灵活配置,来定制实例化Bean的逻辑。
FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现——(xxxFactoryBean)。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。
从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式。
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
// 返回Bean对象
@Nullable
T getObject() throws Exception;
// 返回Bean类型
@Nullable
Class<?> getObjectType();
// 是否为单例对象
default boolean isSingleton() {
return true;
}
}
2) 使用场景
FactoryBean 用来生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。
比如说你有一些同属于某一类型的Bean对象需要被创建,但是它们自己有各自的特点,你只需要把他们的特点注入FactoryBean中,就可以生产出属于该类型的各种实例。
3) 使用示例
【基础代码】
// 第1步:实体类
public class User {
}
// 第2步:创建FactoryBean
@Component
public class MyFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
User user = new User();
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
// 第3步:测试类
@SpringBootTest
public class Test {
@Autowired
private ApplicationContext applicationContext;
@org.junit.jupiter.api.Test
public void m1(){
// 获取bean对象
Object bean1 = applicationContext.getBean("myFactoryBean");
Object bean2 = applicationContext.getBean("myFactoryBean");
System.out.println(bean1);
System.out.println(bean2);
// 名称签名加&,获取FactoryBean本身
Object bean3 = applicationContext.getBean("&myFactoryBean");
Object bean4 = applicationContext.getBean("&myFactoryBean");
System.out.println(bean3);
System.out.println(bean4);
}
}
【测试结果】
// 多次返回的1个对象,代表单例
com.h3c.spring.User@3c54ddec
com.h3c.spring.User@3c54ddec
// 前面加&代表返回的是MyFactoryBean本身
com.h3c.spring.MyFactoryBean@6d69a0d3
com.h3c.spring.MyFactoryBean@6d69a0d3
在上面代码的基础上,将单例改为多例
@Component
public class MyFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
User user = new User();
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
// 这里变成false,代表多例
return false;
}
}
【测试结果】
// 每次获取都是创建新的bean
com.h3c.spring.User@1118d539
com.h3c.spring.User@601d6622
// 获取的FactoryBean一直都是1个
com.h3c.spring.MyFactoryBean@76216830
com.h3c.spring.MyFactoryBean@76216830
4.@Import注解导入
@Import
注解用到的并不是很多,但是非常重要,在进行Spring扩展时经常会用到。它通过搭配自定义注解进行使用,然后往容器中导入一个配置文件。它有四种使用方式。
@Import
注解的源码,表示只能放置在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* 用于导入一个class文件
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
1) Import直接导入类
直接使用@Import
导入了一个类,然后自动的就被放置在IOC容器中了。注意我们的Person类上 就不需要任何的注解了,直接导入即可
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
/**
* 直接使用@Import导入person类,然后尝试从applicationContext中取,成功拿到
**/
@Import(Person.class)
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
2) @Import + ImportSelector
自定义了一个MyImportSelector
实现了 ImportSelector
接口,重写selectImports
方法,然后将我们要导入的类的全限定名写在里面即可导入
@Import(MyImportSelector.class)
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//这里需要具体到类名
return new String[]{"com.shawn.Person"};
}
}
3) @Import + ImportBeanDefinitionRegistrar
这种方式需要实现 ImportBeanDefinitionRegistrar 接口中的方法。BeanDefinition可以简单理解为bean的定义(bean的元数据),也是需要放在IOC容器中进行管理的,先有bean的元数据,applicationContext再根据bean的元数据去创建Bean
@Import(MyImportBeanDefinitionRegistrar.class)
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 构建一个beanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
// 将beanDefinition注册到Ioc容器中
registry.registerBeanDefinition("person", beanDefinition);
}
}
4) @Import + DeferredImportSelector
这种方式也需要我们进行实现接口,其实它和@Import的第二种方式差不多,DeferredImportSelector 它是 ImportSelector 的子接口,所以实现的方法和第二种无异。只是Spring的处理方式不同,它和Spring Boot中的自动导入配置文件延迟导入有关,非常重要
@Import(MyDeferredImportSelector.class)
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyDeferredImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 也是直接将Person的全限定名放进去
return new String[]{Person.class.getName()};
}
}
5.使用 BeanDefinitionRegistryPostProcessor
利用后置处理器也可以注入Bean,在Spring初始化的时候,执行refresh()
,会执行后置处理器,其中就包括BeanDefinitionRegistryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
上面方法的参数,会把BeanDefinitionRegistry
传过来,这样我们就可以操作IOC容器中的BeanDefinition
,但此时IOC容器还没有把全部的BeanDefinition
注入进来,所以,我们更多是注入Bean,而不是删除Bean
【测试实例】
下述代码中我们手动向beanDefinitionRegistry中注册了Person
的BeanDefinition,最终成功将person加入到applicationContext中
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
applicationContext.refresh();
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 创建一个BeanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
// 注入
registry.registerBeanDefinition("person", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
6.Supplier
Supplier
也是注入方式的一种,但使用非常少,非常冷门
它存储BeanDefinition
中的属性,所以我们使用的时候,也需要修改其BeanDefinition
public <T> RootBeanDefinition(@Nullable Class<T> beanClass, String scope, @Nullable Supplier<T> instanceSupplier) {
super();
setBeanClass(beanClass);
setScope(scope);
// 看到在创建 RootBeanDefinition 的时候会赋值
setInstanceSupplier(instanceSupplier);
}
使用示例
// 第3步:测试
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
MyBeanFactoryPostProcessor beanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
applicationContext.addBeanFactoryPostProcessor(beanFactoryPostProcessor);
applicationContext.refresh();
Object person = applicationContext.getBean("person");
// 获取到的结果
System.out.println(person); // Person{name='张三'}
}
}
// 第2步:依旧通过后置处理器操作
class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 1.创建新的RootBeanDefinition
RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
// 2.注册Supplier, 这里为了简单直接设置为静态方法,如果时普通方法,需要先创建对象
beanDefinition.setInstanceSupplier(Person::getPerson);
// 3.注册
registry.registerBeanDefinition("person", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
// 第1步:创建Entity
class Person {
private String name;
public static Person getPerson() {
Person person = new Person();
person.name = "张三";
return person;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}