DI 依赖注入

@Autowired 注入

1. 属性注入

示例1:容器中 Person 类型的 bean 只有一个

@Autowired
private Person person; // 属性名换成 person123、user 都可以

示例2:容器中 Person 类型的 bean 有多个

@Bean
public Person marry(){ return new Person(); }

@Bean
public Person bob(){ return new Person(); }

注入结果情况:

@Autowired
private Person asdf; // 注入失败。Person 有两个,会根据名称再次匹配,但是没有 asdf 的,项目启动时就会报错
 
@Autowired
private Person marry; // 注入成功。根据名称能确定唯一
 
@Autowired
private Person bob; // 注入成功。同上,根据名称也能确定唯一

@Autowired
private List<Person> personList; // 注入成功。根据类型获取多个
 
@Autowired
private Map<String, Person> personMap; // 注入成功。也是根据类型获取多个(key 是 bean 名称,value 是 bean 对象)
 
@Autowired
private ApplicationContext applicationContext; // ICO 容器也能注入

上面示例中第一种方式的解决方案有2个

  1. 注册 bean 时,给任一 bean 加 @Primary 注解

    @Primary // 当 asdf 找不到而系统又有多个 Person 的 bean,当前的优先使用
    @Bean
    public Person marry(){ return new Person(); }
    
  2. 注入 bean 时,使用 @Qualifier("marry") 获取指定名称的 bean

    @Qualifier("marry") // 这时 asdf 就无效了,会使用 marry 再次匹配
    @Autowired
    private Person asdf; 
    

注入 bean 时,根据类型如果一个都找不到,还可以这样配置

@Autowired(required=false) // 只适用于根据类型一个都找不到;如果根据类型能找到多个,根据名称找不到,这种情况是不适用的
private Person asdf; 

2. 构造注入

因为不推荐属性注入,所以构造注入的规则一直在调整从而更灵活兼容更多可能,在不同的 Spring 版本中表现也不一致

  • 只有一个构造方法,可以不用加 @Autowired (Spring4.3及之后,Spring4.3 之前哪怕一个构造也要加)
  • 有多个构造方法,必须在其中一个构造上加 @Autowired,如果都不加就会调用无参构造(没无参就报错;有无参不报错但属性都是 null)

示例1:多个构造,都不加 @Autowired

private User user3;

private Person person;

public TestController(User user3) {
    this.user3 = user3;
}

public TestController(Person person) {
    this.person = person;
}

public TestController() { // 选取这个无参构造反射创建对象(user3 和 person 会是 null)。要是没有无参启动会报错
}

示例2:一个构造,可省略 @Autowired

private User user3;

private Person person;

public TestController(User user3) { // 没得选,只能使用这个,可以不用加 @Autowired(person 会是 null)
    this.user3 = user3;
}

示例3:多个构造,其中一个加 @Autowired

private User user3;

private Person person;

public TestController(User user3) {
    this.user3 = user3;
}

@Autowired
public TestController(Person person) { // 选择这个构造
    this.person = person;
}

public TestController() {
}

示例4:设置属性不注入

private User user3; // 假设这本来就不是一个 bean

private Person person;

@Autowired
public TestController(@Autowired(required=false)) User user3, Person person) {
    this.user3 = user3;
  	this.person = person;
}

3. 方法注入

示例1:setter 方法

@Component
public class Test {
    
    // 属性名无所谓
    private Person asdfasdf;
 
    // 如果 Person 的 bean 是多个,也能使用 @Qualifier 或 @Primary 解决
    @Autowired
    public void setPerson(@Qualifier("marry") Person person) {
        this.asdfasdf = person;
    }
}

示例2:不是 setter 也能注入

@Component
public class Test {
    
    private Person asdfasdf;
  
  	private User user;
 
    @Autowired
    public void otherMethod(@Qualifier("marry") Person person, User user) { // 不是 setter 也会自动注入
        this.asdfasdf = person;
      	this.user = user;
    }
}

@Resource 注入

这是 JDK 提供的注解,也能完成自动注入,和 @Autowired 相比有以下区别:

  • 优先根据名称获取 bean,如果获取不到,再根据类型获取
  • 不支持 required=false
  • 不支持构造注入(不能标注在构造方法上)
  • 方法注入时参数只能是一个(@Autowired 不限制)

示例1:容器中 User 类型的 bean 只有一个

@Resource
private User user11111; // 优先根据名称找,找不到再根据类型找

示例2:容器中 User 类型的 bean 有多个

假设系统中 User 类型的 bean 有两个:user1 和 user2

@Resource
private User user11111; // 根据名称找不到,再根据类型找,找到两个,启动报错

解决办法和 @Autowired 一样也有两个:

1️⃣ 注册 bean 时,给任一bean添加 @Primary 注解

2️⃣ 注入 bean 时,指定名称。@Autowired 是配合 @Qualifier 来指定,@Resource 是通过 name 属性来指定

@Resource(name="user1")
private User user11111; // 会使用 user1 去找,能找到一个,直接注入

示例3:方法注入

方法注入时也只能单个属性注入(所以 @Resource 都是用作属性注入而不是方法注入)

private Person person;
private User user;

@Resource
public void otherMethod(User user) {
    this.user = user;
}

@Resource
public void otherMethod(Person person) {
    this.person = person;
}

下面这种会报错:

private Person person;
private User user;

@Resource
public void otherMethod(Person person, User user) { // 同时注入多个属性,是不支持的
    this.person = person;
    this.user = user;
}

@Inject 注入

  • 也是来于 JDK,@Resource 是 JSR250 的规范,@Inject 是 JSR330 的规范

  • 功能上比 @Resource 更强大(支持构造注入,也是先根据类型匹配。和 @Autowired 大部分功能重叠)

  • 需要引入额外的 maven 依赖

    <!-- JSR-330 @Inject 标准注解 -->
    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version> <!-- 最新版本为 1 -->
    </dependency>
    

注入AOP的属性

AOP 看这篇文章

public interface IPerson{
  	public void sayHell0();
}

public class Man implements IPerson{
  	@Override
  	public void sayHell0(){};
}

@Aspect
@Conponent
public class MyAspect{
  	@Before("execution(* com.example.service.Man.*(..))") // 增强 Man 的方法
  	public void logUserId() {
      	
    }
}

/**
 * 在 SpringBoot 中有可能会报错,因为 AOP 会生成代理对象,实际注入的是代理类而不是原始的 Man 对象,而是代理对象
 * 如果使用 cglib 方式,代理对象的类是继承自 Man,所以不会报错
 * 如果使用 JDK 方式,代理对象的类是 IPerson 的子类(IPerson 两个子类Man和生成的代理类)所以强转会报错,解决办法就是使用 IPerson 替换 Man
 */
@Autowired
private Man man;  // 如果是 JDK 动态代理,换成 private IPerson man;
posted @ 2024-06-29 18:34  CyrusHuang  阅读(35)  评论(0)    收藏  举报