Spring注解-(六)@Autowired的多种用法详解

本文最后更新于:June 13, 2022 pm

Spring 是目前主流的 Java Web 开发框架,是 Java 世界最为成功的框架。Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。该框架是一个轻量级的开源框架,具有很高的凝聚力和吸引力。Spring 框架不局限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。Spring 框架还是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术和框架的能力。

目录

源码

1
2
3
4
5
6
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}

@Autowired 会先根据类型进行查找,然后再根据名称进行查找。

类型注入

目标Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.SpringTestAnnotation.TestValue;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 21:56
* @墨水记忆 www.tothefor.com
*/
@Component
public class OrderS {
}

默认名称为:orderS。

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

@Autowired
private OrderS orderS111;

public void show(){
System.out.println("Per show "+orderS111);
}

}

可以看见,除了类型为OrderS,名称是对不上的。

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.SpringTestAnnotation;

import com.SpringTestAnnotation.TestValue.Per;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
public class TestAnnotation {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class);
Per per = applicationContext.getBean("per", Per.class);
per.show();
}
}

这样是成功运行的。所以,默认就是用的类型进行注入。而名称是无所谓的。但是,当同一个类型有多个时,就必须要指明名称了。

名称注入

注入失败

当同一类型有多个时,任意写名称。

目标Bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.SpringTestAnnotation.TestValue;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 21:56
* @墨水记忆 www.tothefor.com
*/
@Component
public class OrderS {
}

配置类:

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
package com.SpringTestAnnotation;

import com.SpringTestAnnotation.TestValue.OrderS;
import com.SpringTestAnnotation.TestValue.OurAnno;
import com.SpringTestAnnotation.TestValue.Per;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@ComponentScan(value = "com.SpringTestAnnotation")
public class TestConfig {

@Bean
public OrderS orderS(){
return new OrderS();
}

@Bean
public OrderS orderS1(){
return new OrderS();
}

@Bean
public OrderS orderS2(){
return new OrderS();
}

}

这里配置了同一类型的三个不同名称的Bean。

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

@Autowired
private OrderS orderS111;

public void show(){
System.out.println("Per show "+orderS111);
}

}

这里的名称在容器中并没有。

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.SpringTestAnnotation;

import com.SpringTestAnnotation.TestValue.Per;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
public class TestAnnotation {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class);
Per per = applicationContext.getBean("per", Per.class);
per.show();
}
}

运行报错如下:

1
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'per': Unsatisfied dependency expressed through field 'orderS111'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.SpringTestAnnotation.TestValue.OrderS' available: expected single matching bean but found 3: orderS,orderS1,orderS2

大概意思说:找到的类型中只有这三个名称的Bean:orderS,orderS1,orderS2。对于要注入的Bean名称为orderS111的并没有。

注入成功

代码同上,修改一个地方即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

@Autowired
private OrderS orderS; //前面为orderS111

public void show(){
System.out.println("Per show "+orderS);
}

}

其中,属性的名称可以为:orderS,orderS1,orderS2三个中的任意一个。

setter注入

在上面代码的基础上修改:

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
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

private OrderS orderS1;

@Autowired
public void setOrderS1(OrderS orderS1) {
this.orderS1 = orderS1;
}

public void show(){
System.out.println("Per show "+orderS1);
}

}

原理同在属性上使用@Autowired一样,会先根据类型注入,然后再根据名称进行注入。这里的类型值的是参数里面的类型,名称是参数里面的名称,即:public void setOrderS1(OrderS orderS1)这里面的类型和名称。

📢注意:

  • 在这里!仅限在这里!(和被static修饰不一样)方法的名称可以任意写,不用写setXxxx什么之类的,最后都会根据参数进行匹配,然后再进行调用。如下:(正常运行)
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
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

private OrderS orderS1;

@Autowired
public void asdf(OrderS orderS1) { //不是setXXXX
this.orderS1 = orderS1;
}

public void show(){
System.out.println("Per show "+orderS1);
}
}

测试代码同上。

  • 方法没有参数,方法依旧可以执行。只是最后Bean输出的是null而已。如下:
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
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

private OrderS orderS1;

@Autowired
public void asdf() {
System.out.println("Per Bean");
}

public void show(){
System.out.println("Per show "+orderS1);
}

}

测试代码同上。输出结果:

1
2
3
信息: Autowired annotation should only be used on methods with parameters: public void com.SpringTestAnnotation.TestValue.Per.asdf() //警告提示,说Bean的注入是应该是用在一个有参数的地方
Per Bean //方法里面的语句
Per show null

构造注入

示例一

代码都同上,只写改变的代码。

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
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

private OrderS orderS1;

public Per() {
System.out.println("无参");
}

public Per(OrderS orderS1) {
System.out.println("有参");
this.orderS1 = orderS1;
}

public void show(){
System.out.println("Per show "+orderS1);
}

}

测试输出:

1
2
无参
Per show null

可以发现,Spring调用的是无参构造方法。

示例二

再如下代码:

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
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

private OrderS orderS1;


public Per(OrderS orderS1) {
System.out.println("有参1");
this.orderS1 = orderS1;
}
public Per(OrderS orderS,OrderS orderS1) {
System.out.println("有参2");
this.orderS1 = orderS1;
}

public void show(){
System.out.println("Per show "+orderS1);
}

}

在这种情况下测试运行后,Spring不会用任何一个构造方法,会出现报错情况,如下:

1
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'per' defined in file [/Users/dragonone/IdeaProjects/SpringStudy/Study001_DoubleBegin/Spring1_28/target/classes/com/SpringTestAnnotation/TestValue/Per.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.SpringTestAnnotation.TestValue.Per]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.SpringTestAnnotation.TestValue.Per.<init>()

原因:因为有两个构造方法,而Spring不知道使用哪一个,然后就会尝试去用无参的构造方法,但是又没有无参构造方法,所以就找不到而报错。也就是说,当Spring不知道选用哪一个构造方法时,就会去选用无参构造方法。

在Spring不知道使用哪一个构造方法时,我们可以指定使用哪一个。使用@Autowired注解指明使用哪一个构造方法,如下:

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
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

private OrderS orderS1;

@Autowired //指明构造方法
public Per(OrderS orderS1) {
System.out.println("有参1");
this.orderS1 = orderS1;
}
public Per(OrderS orderS,OrderS orderS1) {
System.out.println("有参2");
this.orderS1 = orderS1;
}

public void show(){
System.out.println("Per show "+orderS1);
}

}

测试输出:

1
2
有参1
Per show com.SpringTestAnnotation.TestValue.OrderS@44f75083

@Autowired还可以写在方法参数的前面,但是写了是没什么用的。具体可以见源码中的注释。

自定义注解

除了上面的用法之外,还可以用在自定义注解上面。在前面的有关注解的博客中,多次使用过自定义注解。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @Author DragonOne
* @Date 2022/6/12 08:08
* @墨水记忆 www.tothefor.com
*/

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Autowired
public @interface OurAnno {
}

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

@OurAnno
private OrderS orderS1;

public void show(){
System.out.println("Per show "+orderS1);
}

}

这样一样的可以实现注入效果。

static修饰属性

当@Autowired作用于被static修饰的属性或方法时,是不会生效的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.SpringTestAnnotation.TestValue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;

/**
* @Author DragonOne
* @Date 2022/6/10 11:16
* @墨水记忆 www.tothefor.com
*/
@Component
public class Per {

@Autowired
private static OrderS orderS1;

public void show(){
System.out.println("Per show "+orderS1);
}

}

测试输出:

1
2
信息: Autowired annotation is not supported on static fields: private static com.SpringTestAnnotation.TestValue.OrderS com.SpringTestAnnotation.TestValue.Per.orderS1 //提示信息
Per show null

提示信息也说明了:@Autowired不支持写在一个static的字段上。

不支持用于static的原因:当Bean声明为@Scope(“prototype”)多例,而且也有多处进行注入,那么这多处的注入最后都是以最后一次的注入为最后结果的,即都是用的同一个。所以,多个注入间是相互影响的。