0%

spring | 注解 @Autowired

这个注解是核心注解之一。


参考资料



介绍


@Autowired:织入(Spring上下文已有实例(已注入IOC),@Autowired只是取一下)

@Autowired说:”请给我一个该类的实例,例如,我之前用@Bean或者@Component注释创建的一个实例进入IOC了”。

其实,用法的简单原理是这样的:

@Component 注解的类,会被 spring 扫描到,并且,实例化一个类到 spring IOC

而,@Autowired 注解的属性或者其他,会从 spring IOC 拿到之前注解的实例。

也就是,一般来说 @Component 配合 @Autowired 联用。

默认情况下必须要求依赖对象存在,如果要允许 null 值,可以设置它的required属性为false


使用


@Autowired 可以修饰

  • 构造器
  • 方法
  • 参数
  • 成员变量
  • 注解

关于上面的详细请参考。

我在这里只针对两个进行简要说明「主要是其他没有用到,未来可能会继续补充」

  • 成员变量
  • 构造器

成员变量

一般,我们都会这样使用该注解。

application.properties

1
2
person.name=123
person.age=1

user,java

1
2
3
4
5
6
7
@Data
@Component
@ConfigurationProperties(prefix = "person", ignoreUnknownFields = false)
public class User {
private String name;
private int age;
}

test.java

1
2
3
4
5
6
@service
public class TestService{

@Autowired
private User user;
}

但是,这样的使用方式在 IDEA 中会出现 warning

Spring Team recommends “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”.

如果要编辑器修改的话,会被修改成

1
2
3
4
5
private final User user;

public HelloController(User user) {
this.user = user;
}

这是 java 的书写规范问题,至于,为什么要有 final ,我搬运一下网上的答案。不过,这样子写,感觉破坏了唯一性。

  • spring 配置默认的 beanscopesingleton ,也就是启动后一直有。通过设置 beanscope 属性为 prototype 来声明该对象为动态创建。但是,如果你的 service 本身是 singleton,注入只执行一次。@Autowired本身就是单例模式,只会在程序启动时执行一次,即使不定义final也不会初始化第二次,所以这个final是没有意义的吧。可能是为了防止,在程序运行的时候,又执行了一遍构造函数;
  • 或者是更容易让人理解的意思,加上final只会在程序启动的时候初始化一次,并且在程序运行的时候不会再改变。

例子说明所用实例唯一

application.properties

1
2
person.name=123
person.age=1

user,java

1
2
3
4
5
6
7
@Data
@Component
@ConfigurationProperties(prefix = "person", ignoreUnknownFields = false)
public class User {
private String name;
private int age;
}

TestService.java

1
2
3
4
5
6
7
8
9
10
@service
public class TestService{

@Autowired
private User user;

public void test(){
System.out.println(user.getName());
}
}

InitService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class InitService{

@Autowired
private User user;
@Autowired
private TestService testService;

@PostConstruct
public void init(){
System.out.println(user.getName());
user.setName("test");
testService.test();
}
}

输出

123
test

构造器

假设有这样一个例子。

1
2
3
4
5
6
7
8
@Autowired
private User user;
private String school;


public UserAccountServiceImpl(){
this.school = user.getSchool();
}

很明显,这个例子运行并不成功,因为

Java类会先执行构造方法,然后再给注解了@Autowireduser注入值。

所以,构造方法中会出现空指针错误。

但是,我们可以给构造方法,添加该注解。

1
2
3
4
5
6
7
8
private User user;
private String school;

@Autowired
public UserAccountServiceImpl(User user){
this.user = user;
this.school = user.getSchool();
}

这是因为:

Java变量的初始化顺序为:静态变量或静态语句块–>实例变量或初始化语句块–>构造方法

因为 user 是实例变量,所以,会先初始化它。

当然,User 需要 @Component 之类的注解进行修饰。

多实例使用

这个请参考。

集合注入

除了指定 Bean 的方式注入,我们也可以通过集合的方式一次性注入接口的所有实现类:

1
2
3
4
5
6
7
8
@Component
public class InterfaceInject {
@Autowired
List<IWolf> list;

@Autowired
private Map<String,IWolf> map;
}

上面的两种形式都会将 IWolf 中所有的实现类注入集合中。如果使用的是 List 集合,那么我们可以取出来再通过 instanceof 关键字来判定类型;而通过 Map 集合注入的话,Spring 会将 Bean 的名称(默认类名首字母小写)作为 key 来存储,这样我们就可以在需要的时候动态获取自己想要的实现类。

注入为 null

有时候,我们使用这个注解,发现注入的值为 null,造成这种原因可能有以下几种情况

  • 该类没有托管给 spring 管理
    • 没有加入 @Component 之类的注解
  • 这个类有被 new 出来的实例的,new 过的对象不会交给 Spring 容器管理 所以里面的 service 或者 dao 注入不进来
    • 确实需要在这个 new 的类去注入某些类,但是用 @Autowired 又注入为 null ,这时候我们需要手动去弄 Spring 容器中的 Bean 实现 ApplicationContextAware 接口

举一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.lou.springboot;

import com.alibaba.fastjson2.JSON;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(Application.class, args);
Run run = new Run();
run.proc();
}


}


@Data
class Person {
private String name;
private Integer age;
}

@Service
class PersonService {
public void proc(String cppMsg) {
Person person = JSON.parseObject(cppMsg, Person.class);
System.out.println(person);
}
}

@Component
class Run {

@Autowired
private PersonService personService;

public void proc() {
personService.proc("{\"age\":12,\"name\":\"Antony\"}");
}
}

此时 personService.proc("{\"age\":12,\"name\":\"Antony\"}");personServicenull

使用 ConfigurableApplicationContext 进行获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.lou.springboot;

import com.alibaba.fastjson2.JSON;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(Application.class, args);
Run run = configurableApplicationContext.getBean(Run.class);
run.proc();
}


}


@Data
class Person {
private String name;
private Integer age;
}

@Service
class PersonService {
public void proc(String cppMsg) {
Person person = JSON.parseObject(cppMsg, Person.class);
System.out.println(person);
}
}

@Component
class Run {

@Autowired
private PersonService personService;

public void proc() {
personService.proc("{\"age\":12,\"name\":\"Antony\"}");
}
}

另一种方式请参考 spring | ApplicationContextAware 接口

请我喝杯咖啡吧~