通过五大类注解可以更便捷的将对象存储到 Spring 中,同样也可以使用注解将已经储存的对象取出来,直接赋值到注解所在类的一个属性中,这一个过程也叫做对象的装配或者叫对象的注入,即 DI。
获取 Bean 对象也叫做对象装配,就是把对象取出来放到某个类中,有时候也叫对象注入。
对象装配(对象注入)的实现方法以下 3 种:
常见有关对象注入的注解有两个,一个是@Autowired,另外一个是@Resource
🍂它们两者的区别如下:
属性注入只需要在需要注入对象的属性上加上 @Autowired 或者 @Resource 注解就可以了,这里以 @Autowired 为例。
首先来看第一种情况,待注入的同类对象只有一个,此时我们直接使用 @Autowired 注解就好,不必设置参数,例如我们在UserController类里面注入UserService对象。
下面UserService的结构,先使用 @Service 将 Bean 存放到 Spring 中:
package com.tr.demo.service; import org.springframework.stereotype.Service; @Service public class UserService { public void sayHi() { System.out.println("Hello, UserService~"); } }
属性注入:
package com.tr.demo.controller; import com.tr.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { //属性注入 @Autowired private UserService userService; public void sayHi() { userService.sayHi(); } }
此时我们就可以在启动类中,使用上下文对象来获取到UserController对象,通过执行UserController对象的sayHi方法来进而调用到注入的UserService对象中的sayHi方法了,此时的UserService对象就不是我们自己new出来的了。
import com.tr.demo.controller.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); UserController usercontroller = context.getBean("userController", UserController.class); usercontroller.sayHi(); } }
运行结果:
上面说的是同类对象只有一个的情况,而如果存在多个同类对象,我们就得通过参数来告知容器我们要注入哪一个对象,不告知就会报错。
比如我们将多个User对象添加到容器中,如下:
package com.tr.demo.model; // User 结构 public class User { private int id; private String name; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } package com.tr.demo.model; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class UserBeans { // 使用方法注解添加多个 User 对象到容器中 @Bean("user1") public User user1(){ User user = new User(); user.setId(1); user.setName("张三"); return user; } @Bean("user2") public User user2(){ User user = new User(); user.setId(2); user.setName("李四"); return user; } @Bean("user3") public User user3(){ User user = new User(); user.setId(3); user.setName("王五"); return user; } }
而在UserController2类中需要注入User对象,此时我们运行程序就会报错:
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController2 { @Autowired private User user; public void sayHi() { System.out.println("Hello, " + user); } }
我们试着以同样的方法来调用sayHi方法:
运行结果:
@Autowired 依赖注入流程首先是先根据类型从容器中获取对象,如果只能获取到一个,那么就直接将此对象注入到当前属性上;如果能获取到多个对象,此时会使用 BeanName 进行匹配,而我们添加到 Spring 中的对象是没有一个叫user的,所以程序就报错了。
此时就需要我们来告知容器我们需要哪一个具体的 Bean,要获得目标对象主要有下面三种方法:
在构造方法加上 @Autowired 注解就可,要注意 @Resource 注解是不支持构造方法注入的,我们就直接演示如何获取取多个同类对象中的其中一个了,还是用上面添加到容器中的多个 User 对象。
方法1:将构造方法形参名设置为user1
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController3 { private User user; @Autowired public UserController3(User user1) { this.user = user1; } public void sayHi() { System.out.println("Hello, " + user); } }
启动类就不贴代码了,一样的,运行结果如下:
方法2:@Autowired 搭配 @Qualifier
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; @Controller public class UserController4 { private User user; @Autowired public UserController4(@Qualifier(value = "user2") User user) { this.user = user; } public void sayHi() { System.out.println("Hello, " + user); } }
运行结果:
对了,如果一个类中只有一个构造方法,@Autowired 是可以省略的,演示一下:
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.stereotype.Controller; @Controller public class UserController5 { private User user; public UserController5(User user3) { this.user = user3; } public void sayHi() { System.out.println("Hello, " + user); } }
此时仍然是可以成功注入对象。
如果有多个构造方法,要注意此时是不能省略 @Autowired 的,会导致会注入对象失败。
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.stereotype.Controller; @Controller public class UserController6 { private User user; public UserController6(User user1) { this.user = user1; } public UserController6() { System.out.println("调用无参构造"); } public void sayHi() { System.out.println("Hello, " + user); } }
此时可以看到注入对象失败了,输出的结果是null。
当然此时加上 @Autowired 注解就能正常注入了,就不做展示了。
Setter 注入就是在 setXXX 系列方法上加上 @Resource 或者 @Autowired 进行注入,和构造方法注入大同小异,简单演示一下。
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; @Controller public class UserController7 { private User user; @Autowired public void setUser(@Qualifier(value = "user2") User user) { this.user = user; } public void sayHi() { System.out.println("Hello, " + user); } }
启动类和运行结果:
这里这里第一行输入的是因为启动程序会将上面写的UserController6也添加到容器中,UserController6的无参构造方法是我们自定义的。
在早期的 Spring 版本中,官方推荐使用的 Setter 注入,最开始说的原因就是不符合单一设计原则吧,而现在比较新的 Spring 版本(Sring 4.x 之后)中,官方最使用推荐的又是构造方法注入了,说法是因为它的通用性最好。
🎯属性注入
优点:
缺点:
🎯Setter 注入
优点:
缺点:
🎯构造方法注入
优点:
缺点:
在 Spring 项⽬中,通过 main ⽅法获取到 Controller 类,调⽤ Controller ⾥⾯通过注⼊的⽅式调⽤ Service 类,Service 再通过注⼊的⽅式获取到 Repository 类,Repository 类⾥⾯有⼀个⽅法构建⼀ 个 User 对象,返回给 main ⽅法。Repository ⽆需连接数据库,使⽤伪代码即可。
首先要清楚的是在 main 方法中是不能使用依赖注入的,因为类的静态部分是在 Spring 注入之前的加载的,仔细想一下,在类加载时就要使用一个还没注入的对象这是不现实的。
所以我们要在 main 中执行的是将扫描路径中的类添加到 Spring 中,对象的注入要在 mian 方法所在类的外部去实现。
package com.tr.demo.model; public class User { private int id; private String name; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } package com.tr.demo.repository; import com.tr.demo.model.User; import org.springframework.stereotype.Repository; @Repository public class UserRepository { public User getUser(){ // 伪代码 User user = new User(); user.setId(1); user.setName("张三"); return user; } } package com.tr.demo.service; import com.tr.demo.model.User; import com.tr.demo.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserRepository userRepository; public User getUser(){ return userRepository.getUser(); } } package com.tr.demo.contoller; import com.tr.demo.model.User; import com.tr.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { @Autowired private UserService userService; public User getUser(){ return userService.getUser(); } } package com.tr.demo; import com.tr.demo.contoller.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 启动类 */ public class App { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); UserController userController = context.getBean("userController", UserController.class); System.out.println(userController.getUser()); } }
运行结果: