@Bean
在spring中,将组件注入ioc容器的方式通常分为两种
- 第一种也就是我们常用的
@Component
、@Controller
、@ServicResponse
以及@Respository
注解。
- 使用
@Bean
注解来注入组件。
两种方式的区别:
-
@Component
注解作用于类上,而@Bean
注解作用于配置类中的某一个方法上;
-
@Component
表明告知Spring为当前的这个类创建一个bean,而@Bean
表明告知Spring此方法将会返回一个对象,将返回的对象注入到容器中。
-
@Bean
注解的使用更加灵活。
如何使用?
在没有了解@Bean
注解之前,相信大家都是以这种方式创建对象的:
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
private String name;
private Integer age;
}
然后直接通过@Autowired
从容器中取出该对象
@Autowired
private User user
同样的场景,来使用@Bean
试试。
@Configuration
public class MyConfiguration {
@Bean
public User user() {
return new User();
}
}
该注解必须要在标有@Configuration
的配置类中使用才会有效。
上述代码表示,将@Bean
注解修饰的方法的返回值注入到IOC容器中。
@Autowired
private User user
通过上述几段代码,你可能很难发现@Bean
注解灵活在哪里,反而还要作用在配置类中,更麻烦!
使用场景
场景1: 有时候我们需要根据条件来注入组件。
@Configuration
public class MyConfiguration {
@Bean
public User user() {
int i = 10;
if(i < 7) {
return new User("jack", 20);
} else {
return new User("david", 18);
}
}
}
场景2: 想象一下如果我们需要使用外部引入的lib中的组件时怎么办?使用@Component
注解标注到别人的源码上面?显然这是不现实的,这个时候@Bean
就可以发挥其优势了。
@Configuration
public class MyConfiguration {
@Bean
public ArrayList<User> list() {
ArrayList<User> list = new ArrayList<>();
list.add(new User("nacy", 17));
return list;
}
}
使用@Bean注解在实际项目中解决的问题
在微服务的两个模块,其中模块A作为公共模块,模块B(SpringBoot模块)导入了模块A的Maven依赖(dependency),在项目运行时只启动模块B,模块A相当于一个静态的公共模块,不会去使用SpringBoot启动它。
模块A:
- 配置类:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private EmployeeRolePath employeeRolePath;
@Override
protected void configure(HttpSecurity http) throws Exception {
List<String> uri = employeeRolePath.getUri();
}
- EmployeeRolePath:
@Component
@ConfigurationProperties("role.employee.path")
public class EmployeeRolePath {
private List<String> uri;
public List<String> getUri() {
return uri;
}
public void setUri(List<String> uri) {
this.uri = uri;
}
}
模块B:
- 配置类,继承了模块A的配置类
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfig {
}
- yml配置文件:
role:
admin:
path:
uri:
- /admin/adminRole/**
employee:
path:
uri:
- /admin/category/**
- /admin/dish/**
- /admin/flavor/**
- /admin/setmeal/**
先说一下我要实现的功能:要使上述yaml配置文件中的配置成功绑定到EmployeeRolePath类中并生效。
很显然,上述代码肯定不会生效,原因就是我们启动模块B时,Spring只能够扫描到本模块中的配置类SecurityConfig,以及它继承模块A中的配置类WebSecurityConfig,而作用在EmployeeRolePath类上的注解是不可能为生效的,原因就是模块A根本没启动,没有人去扫描他,它只是一个静态的模块而已。
解决:
在模块A的配置类中使用@Bean
注解注入EmployeeRolePath组件。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
List<String> uri = employeeRolePath().getUri();
}
@Bean
public EmployeeRolePath employeeRolePath() {
return new EmployeeRolePath();
}
如上述在本配置类中要使用该组件,直接调用employeeRolePath()
就能获取到容器中的EmployeeRolePath
组件了。
为什么这样可以生效?
上述说了,当模块B启动时,会先初始化加载模块B的配置类,而模块B的配置类又继承了模块A的配置类,所以Spring是能够扫描到模块A中的配置的,并且它们是在同一个IOC容器中的,所以在模块B中定义的配置文件也会生效。