05-SpringBoot容器与底层注解-1
容器功能
在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这些之前用的注解写在被扫描的包的范围内的类上,被扫描到,创建修饰的类的对象,然后封装到容器。
-
主程序类,一般也被称为主配置类,可以写配置。一般我们自己创建配置类来写配置