1. 前言
之前我们使用Spring往IOC容器配置Bean是使用xml或者注解来配置,但是在SpringBoot框架中使用@Configuration+@Bean
或@Component+@ConfigurationProperties
(这个主要用来绑定配置文件参数)。
1.1 Component+ConfigurationProperties
- 在
application.properties
配置文件中配置属性值
account.id=1
account.name=lisi
account.money=3500
- 编写一个Account类作为注入的组件,并使用@ConfigurationProperties来映射配置文件的值。
@ConfigurationProperties(prefix = "account")
@Component
public class Account {
private int id ;
private String name ;
private double money;
//省略get/set方法
}
public class Person {
private String name;
private int age;
}
public class User {
private String name;
private String sex;
private Account account;
}
- 在SpringBoot的启动类获取容器组件。
@SpringBootApplication
public class Demo02Application {
public static void main(String[] args) {
//1、返回我们IOC容器
ConfigurableApplicationContext run = SpringApplication.run(Demo02Application.class, args);
//2、从容器中获取组件
Person person = run.getBean("person", Person.class);
System.out.println(person);
Account account = run.getBean("account", Account.class);
System.out.println(account);
}
}
测试结果:
1.2 Configuration+Bean
- SpringBoot推荐给容器中添加组件的方式,推荐使用全注解的方式
- 使用@Configuration配置类------spring配置文件
- 使用@Bean给容器中添加组件------spring中bean标签
创建一个配置类,并在类上添加@Configuration标明是SpringBoot配置类,使用@Bean给容器中添加组件
/**
* 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
* 2、配置类本身也是组件
* 3、proxyBeanMethods:代理bean的方法
* Full(proxyBeanMethods = true):保证每个@Bean方法被调用多少次返回的组件都是单实例的
* Lite(proxyBeanMethods = false):每个@Bean方法被调用多少次返回的组件都是新创建的
* 组件依赖必须使用Full模式默认。其他默认是否Lite模式
*/
@Configuration(proxyBeanMethods = true)
public class MyConfig {//告诉SpringBoot这是一个配置类 == 配置文件
/**
* Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
* @return
*/
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型。返回的值,就是组件在容器中的实例
public User user(){
User user= new User("zhangsan","男");
user.setAccount(account());
return user;
}
@Bean
public Account account(){
return new Account(1,"lisi",3500);
}
}
在SpringBoot的启动类获取容器组件。
@SpringBootApplication
public class Demo02Application {
public static void main(String[] args) {
//1、返回我们IOC容器
ConfigurableApplicationContext run = SpringApplication.run(Demo02Application.class, args);
//2、从容器中获取组件
Person person = run.getBean("person", Person.class);
System.out.println(person);
Account account = run.getBean("account", Account.class);
System.out.println(account);
Account account1 = run.getBean("account", Account.class);
System.out.println("从容器中获取account: "+(account==account1));
//4、com.example.test.config.MyConfig$$EnhancerBySpringCGLIB$$ef5e1484@5ebffb44
MyConfig bean = run.getBean(MyConfig.class);
System.out.println("配置类"+bean);
//如果@Configuration(proxyBeanMethods = true)代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。
//保持组件单实例
account = bean.account();
account1 =bean.account();
System.out.println("通过调用@bena方法获取account: "+(account==account1));
User user = run.getBean("user", User.class);
Account account3 = run.getBean("account", Account.class);
System.out.println("用户的账户:"+(user.getAccount() == account3));
}
}
测试结果
我们发现Configuration中proxyBeanMethods()属性默认为true,这个有什么作用呢。
- 如果@Configuration(proxyBeanMethods = true)代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。
也就是无论是从容器中获取还是通过调用方法都是先从容器中获取,保证单实例的。通俗来说就是保证每个@Bean方法被调用多少次返回的组件都是单实例的。 - 如果@Configuration(proxyBeanMethods = false)对象调用方法。SpringBoot不会检查这个组件是否在容器中有。而是直接新建对象。保证每个@Bean方法被调用多少次返回的组件都是新创建的
2. Configuration总结
2.1 使用
Full模式和Lite模式是针对spring配置而言的,和xml配置无关。
- 如果配置类组件之间无依赖关系用Lite模式(
@Configuration(proxyBeanMethods = false)
)加速容器启动过程,减少判断。 - 如果配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(
@Configuration(proxyBeanMethods = true)
)。
2.2 Lite模式
何时为Lite模式:
- 类上有@Component注解
- 类上有@ComponentScan注解
- 类上有@Import注解
- 类上有@ImportResource注解
- 类上没有任何注解,但是类中存在@Bean方法
- 类上有@Configuration(proxyBeanMethods = false)注解
Lite总结:运行时不用生成CGLIB子类,提高运行性能,降低启动时间,可以作为普通类使用。但是不能声明@Bean之间的依赖。
2.3 Full模式
何时为Full模式:
- 标注有@Configuration或者@Configuration(proxyBeanMethods = true)的类被称为Full模式的配置类。
Full模式总结:单例模式能有效避免Lite模式下的错误。性能没有Lite模式好。
3. 其他注解
除了我们本篇文章介绍到的注解,之前Spring使用的注解都可以使用,分别是:
- @Component
- @Controller
- @Service
- @Repository
- @ComponentScan
- @Import
3.1 Import
接下来我们重点说一下@Import注解,可以批量注入组件。直接在配置类标明@Import注解即可,给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
// 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
@Import({User.class, Person.class})
@Configuration(proxyBeanMethods = false)
public class MyConfig {//告诉SpringBoot这是一个配置类 == 配置文件
}
在SpringBoot的启动类获取容器组件。
//5.获取组件
beanNamesForType = run.getBeanNamesForType(User.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
System.out.println("==========");
Person person1 = run.getBean("person", Person.class);
System.out.println(person1);
测试结果
3.2 ImportSelector
使用@Import
可以直接把类加入到容器中,但是如果我们需要加入很多组件就不太适合了。可以使用ImportSelector
接口并重新其selectImports()
方法来批量返回需要导入的组件的全类名数组。
- 定义一个MyImportSelector类并实现ImportSelector。
//自定义逻辑返回需要的组件
public class MyImportSelector implements ImportSelector {
//返回需要导入的组件的全类名数组
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String[] strings = new String[2];
strings[0]="com.example.test.bean.Color";
strings[1]="com.example.test.bean.Red";
//方法不要返回null值
return strings;
}
}
- 直接在配置类中使用
// 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
@Import({User.class, Person.class,MyImportSelector.class})
- 测试结果
3.3 ImportBeanDefinitionRegistrar
定义一个类MyImportBeanDefinitionRegistrar 并实现ImportBeanDefinitionRegistrar 接口中的registerBeanDefinitions就可以手动注册Bean。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata:当前类的注解信息
* @param registry:BeanDefinition注册类
* 把所有需要添加到容器的bean
* 调用BeanDefinitionRegistry.registerBeanDefinition手动注册进来。
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition= registry.containsBeanDefinition("com.example.test.bean.Color");
boolean definition1= registry.containsBeanDefinition("com.example.test.bean.Red");
if (definition && definition1){
BeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
/**
* rainBow:可以自定义bean名
*/
registry.registerBeanDefinition("rainBow",beanDefinition );
}
}
}
直接在配置类使用即可
// 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
@Import({User.class, Person.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
@Configuration(proxyBeanMethods = false)
public class MyConfig {//告诉SpringBoot这是一个配置类 == 配置文件