容器功能
在idea-springboot工程中新建一个springboot项目 b-springboot-annotation,新建一个bean包表示要让容器创建的类和一个config包表示存放配置类,具体使用如下
bean包中有两个类User和Pet
package com.studymyself.bean;
public class User {
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
package com.studymyself.bean;
public class Pet {
private String name;
public Pet() {
}
public Pet(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Pet{" +
"name='" + name + '\'' +
'}';
}
}
1、组件添加(容器创建javabean)
1.1 注解@Configuration
在config包中创建一个MyConfig类,其中定义类的上面添加@Configuration注解,该注解的主要作用就是表示这个类是一个配置类,等同于spring中的配置文件。在这个类中新建一个方法,返回值是一个User,方法内容是new一个User实例,然后返回,在方法定义的上面添加@Bean,表示该方法返回的对象作为组件放进spring容器中了,具体内容如下:
/**
* @Configuration:
* 1、在类上面添加该注解,告诉spring boot这个类是一个配置类,等同于容器的配置文件
* 2、当然,这个配置类的对象也是一个组件,被放到容器中。
*/
@Configuration
public class MyConfig {
/**
* @Bean:
* 1、放在方法上面表示给容器添加组件,组件值是返回的实例,默认将该方法名作为组件(返回的实例)的id,
* 2、通过测试,放进容器中的组件是单实例的
3、可以修改其默认的组件id名,注解中注解中 @Bean(name = "aa")这样修改
*/
@Bean
public User user01(){
User user = new User("张三");
return user;
}
@Bean
public Pet tomcat(){
Pet pet = new Pet("tomcat");
return pet;
}
}
在main主程序类中进行验证,内容如下:
@SpringBootApplication
public class BSpringbootAnnotationApplication {
public static void main(String[] args) {
//获取容器对象
ConfigurableApplicationContext run = SpringApplication.run(BSpringbootAnnotationApplication.class, args);
//验证从容器中获取在配置类中添加的组件user01和tomcat
User user1 = run.getBean("user01",User.class);
Pet pet = run.getBean("tomcat",Pet.class);
System.out.println(user1+"\n"+pet);
//再获取一次user01,跟第一次获取的比较,验证是否是同一个
User user2 = run.getBean("user01",User.class);
System.out.println(user1==user2);
//验证配置类也被创建对象作为组件放进容器中
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
//com.studymyself.config.MyConfig$$EnhancerBySpringCGLIB$$767ae820@56febdc
}
}
结果:
User{name='张三'}
Pet{name='tomcat'}
true
可以看到,容器中已经放入了User类和Pet类的组件,而且仍然默认是单实例的例的。配置类也是一个组件,而且默认是使用CGLIB动态代理进行功能增强了的代理对象组件。
验证当@Configuration注解中属性proxyBeanMethods=true时,调用配置类中的方法,到底是普通的调用方法还是到容器中获取,main方法中添加如下
@SpringBootApplication
public class BSpringbootAnnotationApplication {
public static void main(String[] args) {
//获取容器对象
ConfigurableApplicationContext run = SpringApplication.run(BSpringbootAnnotationApplication.class, args);
//验证从容器中获取在配置类中添加的组件user01和tomcat
User user1 = run.getBean("user01",User.class);
Pet pet = run.getBean("tomcat",Pet.class);
System.out.println(user1+"\n"+pet);// User{name='张三'} Pet{name='tomcat'}
//再获取一次user01,跟第一次获取的比较,验证是否是同一个
User user2 = run.getBean("user01",User.class);
System.out.println(user1==user2);//true
//验证配置类也被创建对象作为组件放进容器中
MyConfig myConfig = run.getBean(MyConfig.class);
System.out.println(myConfig);
//com.studymyself.config.MyConfig$$EnhancerBySpringCGLIB$$767ae820@56febdc
//验证@Configuration注解中属性proxyBeanMethods=true时,调用配置类中的方法
User user3 = myConfig.user01();
User user4 = myConfig.user01();
System.out.println(user3==user4);//true
}
}
结果:
User{name='张三'}
Pet{name='tomcat'}
true
com.studymyself.config.MyConfig$$EnhancerBySpringCGLIB$$767ae820@56febdc
true
可以看到,proxyBeanMethods=true时,调用该方法,业务是MyConfig类的代理对象调用方法,有功能增强,所以底层是先到容器中查看是否有User.class这个类的实例,如果没有,就执行new方法创建一个放到容器中,返回该实例,如果有直接从容器中获取返回。这样确保组件的单实例。
验证当@Configuration注解中属性proxyBeanMethods=false时,调用配置类中的方法,到底是普通的调用方法还是到容器中获取,main方法中添加如下
@SpringBootApplication
public class BSpringbootAnnotationApplication {
public static void main(String[] args) {
//获取容器对象
ConfigurableApplicationContext run = SpringApplication.run(BSpringbootAnnotationApplication.class, args);
//验证从容器中获取在配置类中添加的组件user01和tomcat
User user1 = run.getBean("user01",User.class);
Pet pet = run.getBean("tomcat",Pet.class);
System.out.println(user1+"\n"+pet);// User{name='张三'} Pet{name='tomcat'}
//再获取一次user01,跟第一次获取的比较,验证是否是同一个
User user2 = run.getBean("user01",User.class);
System.out.println(user1==user2);//true
//验证配置类也被创建对象作为组件放进容器中
MyConfig myConfig = run.getBean(MyConfig.class);
System.out.println(myConfig);
//com.studymyself.config.MyConfig$$EnhancerBySpringCGLIB$$767ae820@56febdc
//com.studymyself.config.MyConfig@4dd94a58
// //验证@Configuration注解中属性proxyBeanMethods=true时,调用配置类中的方法
// //这里就是代理对象调用方法
// User user3 = myConfig.user01();
// User user4 = myConfig.user01();
// System.out.println(user3==user4);//true
//验证@Configuration注解中属性proxyBeanMethods=false时,调用配置类中的方法
//这里就是MyConfig的普通对象调用方法
User user3 = myConfig.user01();
User user4 = myConfig.user01();
System.out.println(user3==user4);//false
}
}
结果:
User{name='张三'}
Pet{name='tomcat'}
true
com.studymyself.config.MyConfig@4dd94a58
false
可以看到,proxyBeanMethods=false时,调用该方法,业务是MyConfig类的普通对象调用方法,没有功能增强,所以直接调用运行其中的方法,创建了两个不同的对象。
由以上可以知道,proxyBeanMethods的值对应两种模式:Full模式(也叫全模式proxyBeanMethods=true)和Lite模式(也叫轻量级模式proxyBeanMethods=false)
使用Full模式的前提是你项目中的组件有组件依赖,比如说User类中有一个Pet类型的属性,当User和Pet类下组件都被放到容器中后,User组件中的Pet属性希望使用的是容器中的Pet组件,就是依赖于这个容器中的Pet组件,需要使用Full模式。
当我们使用的这些组件都不依赖于容器中的组件,那么就使用Lite模式。
--结论:
配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
除了@Bean注解是将扫描到的方法的返回值封装到容器中外,还有@Component、@Controller、@Service、@Respository这些之前用的注解写在被扫描的包的范围内的类上,被扫描到,创建修饰的类的对象,然后封装到容器。
主程序类,一般也被称为主配置类,可以写配置。一般我们自己创建配置类来写配置