Spring中@Bean注解的作用以及如何使用
Spring中@Bean注解的作用以及如何使用
一、Bean是啥
1、Java面向对象,对象有方法和属性,那么就需要对象实例来调用方法和属性(即实例化);
2、凡是有方法或属性的类都需要实例化,这样才能具象化去使用这些方法和属性;
3、规律:凡是子类及带有方法或属性的类都要加上注册Bean到Spring IoC的注解;
4、把Bean理解为类的代理或代言人(实际上确实是通过反射、代理来实现的),这样它就能代表类拥有该拥有的东西了
5、我们都在微博上@过某某,对方会优先看到这条信息,并给你反馈,那么在Spring中,你标识一个@符号,那么Spring就会来看看,并且从这里拿到一个Bean或者给出一个Bean
二、注解分为两类:
1、一类是使用Bean,即是把已经在xml文件中配置好的Bean拿来用,完成属性、方法的组装;比如@Autowired , @Resource,可以通过byTYPE(@Autowired)、byNAME(@Resource)的方式获取Bean;
2、一类是注册Bean,@Component , @Repository , @ Controller , @Service , @Configration这些注解都是把你要实例化的对象转化成一个Bean,放在IoC容器中,等你要用的时候,它会和上面的@Autowired , @Resource配合到一起,把对象、属性、方法完美组装。
两种方式的区别:
- @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 // 开启Security 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 // 开启Security public class SecurityConfig extends WebSecurityConfig { // ........ // ......... }
- yml配置文件:
role: admin: # 需要admin权限的访问路径 path: uri: - /admin/adminRole/** employee: # 需要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 // 开启Security 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中定义的配置文件也会生效。