接下来学习一下依赖注入DI的细节.
依赖注入是一个过程, 是指IoC容器在创建Bean时, 去提供运行时所依赖的资源, 而资源指的就是对象. 在之前的案例中, 使用了@Autowired这个注解, 完成了依赖注入这个操作.
简单来说, 就是把对象取出来放到某个类的属性中.
在一些文章中, 依赖注入也称为"对象注入", "属性装配", 具体含义需要结合文章的上下文理解.
关于依赖注入, Spring提供了三种方式:
1.属性注入(Field Injection)
2.构造方法注入(Constructor Injection)
3.Setter注入(Setter Injection).
属性注入通过@Autowired实现的, 这里将Service类注入到Controller类中.
@Service public class MyService { public void sayHi() { System.out.println("Hi, MyService"); } }
@Controller //将对象存储到Spring中 public class MyController2 { //注入方法1: 属性注入 @Autowired private MyService myService; public void sayHi() { System.out.println("Hi, UserController..."); myService.sayHi(); } }
使用:
@SpringBootApplication public class SpringbootDemoApplication { public static void main(String[] args) { //获取Spring上下文对象 ApplicationContext context = SpringApplication.run(SpringbootDemoApplication.class); MyController2 myController = context.getBean(MyController2.class); myController.sayHi(); } }
最终运行结果如下:
构造方法注入是在类的构造方法中实现注入, 如下所示:
@Controller //将对象存储到Spring中 public class MyController2 { private MyService myService; //注入方法2: 构造方法注入 @Autowired public MyController2(MyService myService) { this.myService = myService; } public void sayHi() { System.out.println("Hi, UserController..."); myService.sayHi(); } }
注意事项: 如果类中只有一个构造方法, 那么@Autowired注解可以省略(在Spring中, 如果一个类只有一个构造方法, 并且该构造方法不包含任何参数, 那么Spring在实例化这个类的时候会自动将其作为一个Bean注入到容器中); 如果类中有多个构造方法, 那么需要添加上@Autowired来明确指明到底使用哪个构造方法.
Setter注入和属性的Setter方法实现类似, 只不过在设置set方法的时候需要加上@Autowired注解:
@Controller //将对象存储到Spring中 public class MyController2 { private MyService myService; //注入方法3: Setter方法注入 @Autowired public void setMyService(MyService myService) { this.myService = myService; } public void sayHi() { System.out.println("Hi, UserController..."); myService.sayHi(); } }
这里注意, 对于Setter方法, 是一定要写@Autowired的.
当同一类型存在多个bean时, 使用@Autowired会存在问题.
@Component public class BeanConfig { @Bean("u1") public User user1() { User user = new User(); user.setName("lisi"); user.setAge(20); return user; } @Bean public User user2() { User user = new User(); user.setName("zhangsan"); user.setAge(18); return user; } }
@Controller public class MyController4 { @Autowired private User user; public void sayHi() { System.out.println("hi, UserController4..."); System.out.println(user); } }
运行结果:
报错的原因是, 非唯一的Bean对象.
如何解决上述问题呢? Spring提供了以下几种解决方案:
@Primary
@Qualifier
@Resource
使用@Primary注解: 当存在多个相同类型的Bean注入时, 加上@Primary注解, 来确定默认的实现.
@Component public class BeanConfig { @Primary // 指定该bean为默认的bean实现. @Bean("u1") public User user1() { User user = new User(); user.setName("lisi"); user.setAge(20); return user; } @Bean public User user2() { User user = new User(); user.setName("zhangsan"); user.setAge(18); return user; } }
使用@Qualifier注解: 指定要注入的bean对象. 在@Qualifier的value属性中,指定注入bean的名称.
@Qualifier注解不能单独使用, 必须配合@Autowired使用.
@Controller public class MyController4 { @Qualifier("user2") //指定bean的名称. @Autowired private User user; public void sayHi() { System.out.println("hi, UserController4..."); System.out.println(user); } }
使用@Resource注解: 是按照bean的方式注入. 通过name属性指定要注入的bean名称.
@Controller public class MyController4 { @Resource(name = "user2") private User user; public void sayHi() { System.out.println("hi, UserController4..."); System.out.println(user); } }
常见面试题:
@Autowired和@Resource的区别
@Autowired是Spring框架提供的注解, 而@Resource是JDK提供的注解.(@Primary, @Qualifier是Spring提供的注解).
@Autowired默认是按照类型注入, 而@Resource是按名称注入. 相比于@Autowired来说, @Resource支持更多的参数配置, 例如name设置, 通过name获取bean.