多个同类型 Bean 注入怎么办?
问题:如若有多个同类型的 Bean 对象存储进 IoC 容器中,那么我们该如何准确获取该类型对象?
问题场景:
Users 类:
@Service public class Users { //通过方法注解,添加两个同类型 bean @Bean("user1") public User user1() { User user = new User(); user.setName("李四"); user.setId(22); return user; } @Bean("user2") public User user2() { User user = new User(); user.setName("王五"); user.setId(88); return user; } }
UserService 类:
@Service public class UserService { //对 user 进行属性注入 @Autowired User user; public void add() { //拿到 user 后打印内容,判断是哪个 user System.out.println(user.toString()); } }
Test 类:
public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); UserService userService = context.getBean("userService", UserService.class); userService.add(); } }
看结果:
它是说期望有一个 bean 进行匹配,但出现了两个:user1、user2
解决问题:
1.将属性的名字和 Bean 的名字对应上。
就比如我要得到的是 王五 这个对象,那我只需要将下面两点的名称对应上即可:
2.@Autowired 配合 @Qualifier 使用
这个就是在注入时,声明要注入的 bean 名称
属性注入分析:
优点: 使用简单
缺点:
- 无法注入 final 修饰的变量
- 只适用于 IoC 容器
- 容易违背单一设计原则
Setter 注入
我们写类属性时,可以对这些属性生成相应的 get 和 set 方法,Setter 注入就是针对 set 方法,进行的注入:
User 类:
@Service public class Users { @Bean("user") public User getUser() { User user = new User(); user.setName("老王"); user.setId(26); return user; } }
UserService 类:
@Service public class UserService { private User user; //对类属性 user 进行 Setter 注入 @Autowired public void setUser(User user) { this.user = user; } public void add() { System.out.println(user.toString()); } }
public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); UserService userService = context.getBean("userService", UserService.class); userService.add(); } }
注意:@Autowired 不能省略
Setter 注入分析:
优点:通常 Setter 只是注入一个属性,所以 Setter 更符合单一设计原则。
缺点:
- 无法注入一个 final 修饰的变量
- Setter 注入的对象可以被修改。(setter 是一个方法,既然是方法就可能被多次调用和修改)
构造方法注入(官方推荐)
构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所示:
只对 UserService 进行修改:
@Service public class UserService { private User user; //构造方法注入 @Autowired public UserService(User user) { this.user = user; } public void add() { System.out.println(user.toString()); } }
注意:如果只有一个构造方法,不写 @Autowired 也可以,但若是有多个构造方法,就得加上 @Autowired,表明是哪个构造方法需要注入。
构造方法注入分析:
优点:
- 可以注入一个 final 修饰的变量
- 注入的变量不会被修改,因为构造方法只加载一次
- 构造方法注入可以保证注入对象完全初始化
- 构造方法注入通用性更好
缺点:
- 写法比属性注入更复杂
- 使用构造方法注入,无法解决循环依赖的问题
其实 @Resource 的功能和 @Autowired 差不多,那它俩有啥区别呢?
@Autowired 与 @Resource 的区别:
- 出生不同:@Resource 来自 JDK,@Autowired 来自 Spring 框架
- 支持参数不同:@Resource 支持很多参数设置,@Autowired 只支持一个参数设置
- 使用上的区别:@Resource 不支持构造方法注入,@Autowired 支持构造方法注入
- idea 兼容性支持不同:使用 @Autowired 在 idea 专业版下可能会误报,@Resource 不存在误报问题(@Resource 相当于是亲儿子了)
关于第四点:因为@Autowired 来自 Spring 框架,@Resource 来自 JDK,所以执行顺序有差异,Spring 框架的执行是在 java 程序执行之后的,当我们使用 @Autowired 时,它不能检测到 IoC 容器中是否有这个类型的 Bean,所以就报错(运行起来不影响结果);使用 @Resource 的话,执行顺序是靠前的,它知道这个 Bean 是否存在。