为了后面深入的理解 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;
}
@Override
public 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;
}
@Override
public 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(“组件名”) ,这样组件名就不是方法名,而是我们指定的组件名了。
@Bean
public User user01() {
return new User("zhangsan", 18);
}
同样的,编写一个 Pet 类型返回值,组件id为方法名的方法。并加上 @Bean 注解
@Bean
public 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 模式(单实例模式)。