底层注解-@Configuration 详解
# 50.底层注解-@Configuration 详解
为了后续深入学习 SpringBoot 自动配置原理,我们先来学习一些底层注解,首先是@Configuration
# 给 IoC 容器添加组件
然后新增两个 Bean:一个 Pet,一个 User
package com.peterjxl.boot.bean;
public class Pet {
private String name;
}
2
3
4
5
package com.peterjxl.boot.bean;
public class User {
private String name;
private Integer age;
}
2
3
4
5
6
注意,构造方法,getter,setter 和 toString 方法请自行生成
如果是刚学 Spring 的时候,我们是需要 new 一个 beans.xml,然后使用 bean 标签来创建;而 SpringBoot 不用这么做,而是提供了注解@Configuration
我们新建一个类,加上@Configuration 注解:
package com.peterjxl.boot.config;
import com.peterjxl.boot.bean.Pet;
import com.peterjxl.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的实例
public User user01(){
return new User("peterjxl",18);
}
@Bean("tom")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这样,容器中就会有 user01 和 tom 这两个组件,并且 MyConfig 也会放到容器中
# 单例模式
注意,默认是单例的。然后我们可以获取多次,然后打印:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
// 1.返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
// 2.查看容器里面的组件
String[] names = run.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
// 3.从容器中获取组件
Pet tom01 = run.getBean("tom", Pet.class);
Pet tom02 = run.getBean("tom", Pet.class);
System.out.println("组件:tom01 == tom02 :" + ( tom01 == tom02 )); //运行结果:ture
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
比起 SpringBoot 1, 2 版本的@Configuration 注解多了一个属性:proxyBeanMethods
,其源码:
boolean proxyBeanMethods() default true;
这个属性是什么意思呢?首先,如果是 true,那么 SpringBoot 会生成 Myconfig 的代理对象,放到容器中,我们可以在主程序中打印下:
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean); //com.peterjxl.boot.config.MyConfig$$EnhancerBySpringCGLIB$$fff9f8b1@415e0bcb
2
同时,如果我们再次调用 user01 等方法,返回的对象也会是容器中的对象(代理对象里做的),这样就能保持组件单实例。我们可以试试:
User user = bean.user01();
User user1 = bean.user01();
System.out.println("组件:user == user1 :" + ( user == user1 )); //运行结果:ture
2
3
因此,目前有两种场景:
- Full(proxyBeanMethods = true)
- Lite(proxyBeanMethods = false),这样就不是创建代理对象,调用方法的时候创建的对象,也是新的。这样能解决组件依赖的问题
该注解也是 SpringBoot 2 比较大的一个更新,调成 false,那么就不会检查容器中是否有该组件,启动起来就比较快
# 组件依赖的场景
举个例子,假设 User 要养一个宠物,新增一个成员变量)和对应的 setter,getter:
package com.peterjxl.boot.bean;
public class User {
private String name;
private Integer age;
private Pet pet;
}
2
3
4
5
6
7
然后我们在配置类 MyConfig 里,给 User 设置一个 pet 对象:
package com.peterjxl.boot.config;
@Configuration
public class MyConfig {
@Bean //给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的实例
public User user01(){
User zhangsan = new User("zhangsan",18);
// user组件依赖了pet组件
zhangsan.setPet(tomcatPet());
return zhangsan;
}
@Bean("tom")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在主程序中获取 user:
User user01 = run.getBean("user01", User.class);
Pet tom = run.getBean("tom", Pet.class);
System.out.println("组件:user01.getPet() == tom :" + ( user01.getPet() == tom ));
2
3
由于默认情况下,注解@Configuration 里,proxyBeanMethods 的值是 true,上述结果打印 true;
但如果调整 proxyBeanMethods 是 false,那么上述场景则打印 false
# 其他注解
除此之外,还可以使用@Bean、@Component、@Controller、@Service、@Repository、@ComponentScan 来注册组件。
# 最佳实践
如果只是注册组件,没有组件依赖的问题,可以调成 false,这样加载起来就比较快。
已将本文源码上传到 Gitee (opens new window) 或 GitHub (opens new window) 的分支 demo3,读者可以通过切换分支来查看本文的示例代码