容器功能

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