为了后面深入的理解 springboot 的自动配置原理,先来看一下底层的注解,了解一下它们是如何完成相关的功能。
组件添加
新建User组件
package com.jaded.boot.bean;public class User {private String name;private Integer age;public User() {}public User(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}}

新建Pet组件
package com.jaded.boot.bean;public class Pet {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Pet() {}public Pet(String name) {this.name = name;}@Overridepublic String toString() {return "Pet{" +"name='" + name + '\'' +'}';}}

以前是如何向容器中添加组件的?
新建 beans.xml 配置文件


在里面写入:
<bean id="user01" class="com.jaded.boot.bean.User"><property name="name" value="zhangsan"></property><property name="age" value="18"></property></bean><bean id="cat01" class="com.jaded.boot.bean.Pet"><property name="name" value="tomcat"></property></bean>

这是以前用 spring xml 的方式配置的。
现在 springboot 是怎么给容器添加组件的?
使用 @Configuration 注解。
新建一个 MyConfig 配置类

给配置类加上 @Configuration 注解
给这个类加上 @Configuration 注解,告诉 springboot 这是一个配置类(等于配置文件)
编写组件方法
编写一个 User 类型返回值,组件id为方法名的方法。并加上 @Bean 注解。
@Bean注解:给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,方法返回的值就是容器保存的实例。如果想指定组件名,可以使用:@Bean(“组件名”) ,这样组件名就不是方法名,而是我们指定的组件名了。
@Beanpublic User user01() {return new User("zhangsan", 18);}

同样的,编写一个 Pet 类型返回值,组件id为方法名的方法。并加上 @Bean 注解
@Beanpublic Pet cat01() {return new Pet("tomcat");}

修改一下主程序类的 main 方法
// 返回 IOC 容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);// 查看容器里的组件String[] names = run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}
运行,等待 springboot启动完成后,就可以看到刚才注册的两个组件了。(user01、
cat01)
Lite 和 Full 模式
Lite(proxyBeanMethods = false)Full(proxyBeanMethods = true)
组件注册默认为单实例
以上我们给组件注册的两个组件,默认是单实例的。也就是说,无论我们从容器中获取多少次,都是同一个实例。
修改主程序的 main 方法:
// 返回 IOC 容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);// 查看容器里的组件String[] names = run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}// 从容器中获取组件Pet cat1 = run.getBean("cat01", Pet.class);Pet cat2 = run.getBean("cat01", Pet.class);System.out.println("******* 测试组件是否为单实例 **********************");System.out.println(cat1==cat2);
运行:
从结果上来看,我们可以判断确实是单实例。
配置类本身也是一个组件
修改主程序的 main 方法为:
// 返回 IOC 容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);// 查看容器里的组件String[] names = run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}// 从容器中获取组件Pet cat1 = run.getBean("cat01", Pet.class);Pet cat2 = run.getBean("cat01", Pet.class);System.out.println("******* 测试组件是否为单实例 **********************");System.out.println(cat1==cat2);// 查看配置类组件MyConfig myConfig=run.getBean(MyConfig.class);System.out.println("******* 查看配置类组件 **********************");System.out.println(myConfig);
运行:
从运行结果上来看,它本身确实也是一个组件。
proxyBeanMethods 属性
如果在主程序中多调用几次配置类的方法,那此时是从容器中直接获取,还是普通的方法调用?
修改主程序的 main 方法:
// 返回 IOC 容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);// 查看容器里的组件String[] names = run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}// 从容器中获取组件Pet cat1 = run.getBean("cat01", Pet.class);Pet cat2 = run.getBean("cat01", Pet.class);System.out.println("******* 测试组件是否为单实例 **********************");System.out.println(cat1 == cat2);// 查看配置类组件MyConfig myConfig = run.getBean(MyConfig.class);System.out.println("******* 查看配置类组件 **********************");System.out.println(myConfig);// 外部调用配置类的User user1 = myConfig.user01();User user2 = myConfig.user01();System.out.println("******* 是从容器中获取,还是普通的方法调用? **********************");System.out.println(user1 == user2);
运行:
得到的仍然是同一个对象。
这是因为 @Configuration 的 proxyBeanMethods 属性默认为 true。
此时我们获取到的 MyConfig 不是一个普通的对象,而是一个代理对象 EnhancerBySpringCGLIB ,一个被 springboot 增强了的代理对象。
如果是代理对象调用组件的注册方法,springboot 的默认逻辑就是,先检查容器中是否已经有组件的实例,如果有就拿,如果没有就再创建。
如果将配置类的 proxyBeanMethods 属性设为 false 。
@Configuration(proxyBeanMethods = false)


可以看到配置类此时就不是代理类了,判断结果也是false,说明这是两个组件的不同实例。
这是用来解决什么场景的?
组件依赖。
如何选择Full 模式和 Lite 模式?
如果设置为 Lite 模式,springboot 不会检查在容器中是否已经注册过该组件,springboot 启动和加载的速度会更快。
如果只是单单注册组件,别的组件也不依赖它,推荐使用 Lite 模式,那 springboot 启动和加载的速度会更快。
反之,如果这个组件将会被另一个组件作为依赖时,可以使用 Full 模式(单实例模式)。
