Spring 中 @Autowired 修饰构造方法时注意事项

代码演示

给定一个类 One,然后看下的几种构造方法什么时候被调用

1、假设现在只有一个默认的空构造方法,代码如下:

@Component
public class One {

}

然后追踪源码,如下所示:

在这里插入图片描述在这里插入图片描述

先拿到所有声明的构造方法

在这里插入图片描述

然后挨个判断构造方法上是否标注了注解 @Autowired、@Value 注解

在这里插入图片描述

AutowiredAnnotationBeanPostProcessor 类在空构造方法中就已经默默的把这两个注解放入到了内存中

在这里插入图片描述

如果构造方法中没标注上述两个注解,并且构造方法中也没有参数,那么就将这个 candidate 候选的构造方法对象赋值给 defaultConstructor 默认的构造方法对象

在这里插入图片描述

最后就是一堆的判断条件,如下所示:

在这里插入图片描述

现在这里假设的是默认的空参数构造方法,所以只能走到 else 逻辑,直接 new 一个数组,不过没啥作用,最终返回的是 null,因为 candidateConstructors 集合中并没有保存对象。

继续回到外面调用处,如下所示:

在这里插入图片描述

determineConstructorsFromBeanPostProcessors() 返回 null,表示没有找到构造方法,那么就只能走最后的兜底逻辑,也就是采用无参构造方法进行实例化 One 类了。

最后构造方法的反射实例化 One 类。

在这里插入图片描述

总结:其实因为类里面不显示书写构造方法的话,默认其实也是有一个无参构造方法存在的,不然你怎么创建对象,那没有构造方法你怎么去实例化对象。从而推断出是不是类里面只要有并且只有1个构造方法存在的时候,不管你是不是无参还是有参,其实从人类的认知里面,只有1个,那么肯定就用你嘛,别无选择。所以 Spring
里面也是这样的,合乎常理。

现在我就把只写一个有参数的构造方法,如下所示:

@Component
public class One {
	public One(Two tw) {
		System.out.println("带有 @Autowired 注解的1个有参构造方法 tw==" + tw);
	}
}

测试后之后很显然是成功的,结果可以正常打印,One 也可以正常实例化,那分析下源码怎么走的,如下所示:

先拿到 One 类中所有的构造方法,目前仅有1个,如下所示:

在这里插入图片描述

然后挨个遍历构造方法,查找是否被 @Autowired 、@Value 注解修饰

在这里插入图片描述

最后就是一堆的判断条件,如下所示:

在这里插入图片描述

可以发现 else if() 逻辑成立,刚好就一个构造方法,并且还是一个有参的构造方法,所以最终 return 出去的就是这个有参构造方法。

然后直接进入 autowireConstructor() 方法,里面会把参数进行实例化,主要调用 getBean(),参数实例化好了之后,在通过反射调用构造方法实例化 One。

在这里插入图片描述

参数的实例化源码如下:

在这里插入图片描述在这里插入图片描述

参数实例化完成之后,通过有参构造方法的调用实例化 One。

总结:只有一个构造方法时,Spring 只会认识这1个构造方法,并且也会把参数实例化。就和加了 @Autowired 注解一个效果感觉。

2、假设一个无参构造方法,还有一个1个参数的有参构造方法,如下所示:

@Component
public class One {
	public One() {
		System.out.println("空的不带 @Autowired 注解的构造方法");
	}

	public One(Two tw) {
		System.out.println("带有 @Autowired 注解的1个有参构造方法 tw==" + tw);
	}
}

Spring 会选择哪个构造方法进行实例化呢?继续追踪源码,如下:

在这里插入图片描述

调用 determineConstructorsFromBeanPostProcessors() 方法取选择用哪个构造方法进行实例化 bean

在这里插入图片描述

获取 One 类的所有构造方法,发现有两个,如下所示:

在这里插入图片描述

findAutowiredAnnotation() 挨个判断构造方法是否被 @Autowired、@Value 注解修饰,很显然目前这里还没有修饰,所以 ann 结果为 null

在这里插入图片描述

将无参构造方法赋值给变量 defaultConstructor ,表示这是无参构造方法,但是并没有什么意义说实话感觉

在这里插入图片描述

最终也是要走这一段逻辑判断,最终结果返回 null

在这里插入图片描述

继续回到外面调用处,如下所示:

在这里插入图片描述

determineConstructorsFromBeanPostProcessors() 返回 null,表示没有找到构造方法,那么就只能走最后的兜底逻辑,也就是采用无参构造方法进行实例化 One 类了

最后构造方法的反射实例化 One 类。

总结:当你有多个构造方法并且没有标注 @Autowired 、@Value 注解时,Spring 最终只会认识无参构造方法,此时如果没有无参构造方法,那么必然就不能够实例化,最终报错。

那么此时我就是不想用这个空构造方法,我就是想用这个有参构造方法,该怎么办么呢?这个时候就可以加上 @Autowired 注解,加上这个注解,Spring 就只会认这个有注解修饰的构造方法,其他的不会用了。

3、多个构造方法时,指定具体的构造方法实例化 bean,如下所示:

指定 Spring 用1个参数的构造方法实例化 One bean

@Component
public class One {
    public One() {
		System.out.println("空的不带 @Autowired 注解的构造方法");
	}


	public One(Two tw, Three th) {
		System.out.println("带有 @Autowired 注解的2个有参构造方法 tw==" + tw + ",th=" + th);
	}

	@Autowired
	public One(Two tw) {
		System.out.println("带有 @Autowired 注解的1个有参构造方法 tw==" + tw);
	}
}

分析下源码如下:

在这里插入图片描述

查找有没有可以使用的构造方法

在这里插入图片描述

获取到 One 类中所有的构造方法

在这里插入图片描述

挨个遍历构造方法是否被 @Autowired 注解修饰,目前这里只有1个被注解修饰

在这里插入图片描述

找到一个被 @Autowired 注解修饰的构造方法,然后加入到 candidates 候选集合中,并且把 requiredConstructor 变量赋值成了当前这个被 @Autowired 注解修饰的构造方法,为什么要赋值这个 requiredConstructor 变量呢?

在这里插入图片描述

为什么要赋值这个 requiredConstructor 变量呢?是因为怕你有很多构造方法时,你都给加上了 @Autowired 注解,这样对于 Spring 其实又不知道该怎么选择了,其实也合乎常理,换做是你,你也选择不知道用哪个。

所以直接就给你抛异常,除非你在把所有的注解 @Autowired(required = false) 这样就不会抛异常了,但是这样 Spring 只能自己去选择参数最多的那个构造方法进行实例化操作了,没有任何意义说实话。

在这里插入图片描述

话题扯回来,继续跟踪源码,又回到这一堆的判断逻辑,不过这个时候,if 判断条件成立,candidates 集合不为空,因为找到了被 @Autowired 注解修饰的构造方法,最后将这个构造方法对象返回出去

在这里插入图片描述

最终进入 autowirdConstructor() 方法

在这里插入图片描述

这是对构造方法中参数的赋值操作

在这里插入图片描述

最后在通过构造方法反射出 One 的实例。

注意在构造方法中出现了循环依赖,会抛出异常,如下所示:

@Component
public class One {
    public One(Two two) {
		System.out.println("空的不带 @Autowired 注解的构造方法 two="+two);
	}
}
@Component
public class Two {
	public Two(One one) {
		System.out.println("空的不带 @Autowired 注解的构造方法 one="+one);
	}
}

因为此时并没有实例化完成,还没有使用到三级缓存,Spring 中解决循环依赖是依赖三级缓存,所以在这里就会出现异常,异常如下:

在这里插入图片描述

另外,我们可以通过构造方法给 Controller 注入 Service 值,如下所示:

@Controller
public class HelloController {

	private final HelloService helloService;

	public HelloController(HelloService helloService) {
		this.helloService = helloService;
		System.out.println("======>"+this.helloService);
	}
}

@Service
public class HelloService {
}

目前这种方式注入是市面上也比较常用的,也是比 @Autowired 注入方式更友好的,毕竟再也不用看黄色警告⚠️了

总结:

1、当有且仅有1个构造方法时,Spring 都只会使用这个构造方法创建实例。

2、Spring 优先选择 被 @Autowired 标注的构造方法

3、当有多个构造方法同时存在时,Spring 默认选择空构造方法,若此时没有空构造方法,就会报错。

4、当有多个方法同时存在时,想指定 Spring 具体用哪个构造方法,可以加上 @Autowired 注解来标识,如果此时有多个 @Autowired 同时存在,需要将所有的 required 修改成 false ,Spring 默认使用参数最多的构造方法

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔道不误砍柴功

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值