Spring(六)自动注入和复杂属性注入

326 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情

DI相关注解

如果一个bean已经放入Spring容器中了。那么我们可以使用下列注解实现属性注入,让Spring容器帮我们完成属性的赋值。

@Value

主要用于String,Integer等可以直接赋值的属性注入。不依赖setter方法,支持SpEL表达式。

例如:

@Service("userService")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserServiceImpl implements UserService {
    private UserDao userDao;
    @Value("199")
    private int num;
    @Value("好好学习")
    private String str;
    @Value("#{19+3}")
    private Integer age;


    public void show() {
        userDao.show();
    }
}

@AutoWired

一个 Bean 要依赖另一个 Bean,在 xml 中可以声明 ref 属性,用注解就可以使用 @AutoWired

在 Bean 中直接在 属性 / setter 方法 上标注 @Autowired 注解,IOC 容器会按照属性对应的类型,从容器中找对应类型的 Bean 赋值到对应的属性上,实现自动注入。

Person :

@Component
public class Person {
    private String name = "administrator";
    // setter

Dog :

@Component
public class Dog {
    
    @Value("dogdog")
    private String name;
    
    private Person person;
    // toString() ......

给Dog注入Person的三种方式

对于 @Autowired 的使用,只需要在属性上标注即可:

@Component
public class Dog {
    // ......
    @Autowired
    private Person person;

也可以使用构造器注入方式:

@Component
public class Dog {
    // ......
    private Person person;
    
    @Autowired
    public Dog(Person person) {
        this.person = person;
    }

亦可以使用 setter 方法注入:

@Component
public class Dog {
    // ......
    private Person person;
    
    @Autowired
    public void setPerson(Person person) {
        this.person = person;
    }

编写启动类,把上面的 Person 和 Dog 都扫描进 IOC 容器,之后取出 Dog 并打印:

public class InjectComplexFieldAnnoApplication { 
    public static void main(String[] args) throws Exception { 
        ApplicationContext ctx = new AnnotationConfigApplicationContext("com.linkedbear.spring.basic_di.d_complexfield.bean");
        Dog dog = ctx.getBean(Dog.class); 
        System.out.println(dog); 
    } 
}

运行,打印 Dog ,发现 Dog 里已经依赖了 Person :

Dog{name='dogdog', person=Person{name='administrator'}}

注入的Bean不存在

将 Person 上面的 @Component 暂时的注释掉,此时 IOC 容器中应该没有 Person 了 这时就会报错

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.linkedbear.spring.basic_di.d_autowired.bean.Person' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

本来想找一个类型为 Person 的 Bean ,但一个也没找到@Component 注解被注释掉了,自然就不会注册了。如果出现这种情况下又不想让程序抛异常,就需要在 @Autowired 注解上加一个属性:required = false 。

@Autowired(required = false) 
private Person person;

输出

Dog{name='dogdog', person=null}

@Qualifier

如果相同类型的bean在容器中有多个时,单独使用@AutoWired就不能满足要求,这时候可以再加上@Qualifier来指定bean的名字从容器中获取bean注入。

    @Autowired
    @Qualifier("userDao2")
    private UserDao userDao;

注意:该直接不能单独使用。单独使用没有作用,必须配合 @Autowired 使用

【面试题】@Autowired注入的原理逻辑

先拿属性对应的类型,去 IOC 容器中找 Bean ,如果找到了一个,直接返回;如果找到多个类型一样的 Bean , 把属性名拿过去,跟这些 Bean 的 id 逐个对比,如果有一个相同的,直接返回;如果没有任何相同的 id 与要注入的属性名相同,则会抛出 NoUniqueBeanDefinitionException 异常。

image.png