本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!
- 🚀 魔都架构师 | 全网30W技术追随者
- 🔧 大厂分布式系统/数据中台实战专家
- 🏆 主导交易系统百万级流量调优 & 车联网平台架构
- 🧠 AIGC应用开发先行者 | 区块链落地实践者
- 🌍 以技术驱动创新,我们的征途是改变世界!
- 👉 实战干货:编程严选网
1 案例:定义的 Bean 缺少隐式依赖
有时把一个类定义成 Bean,又觉得这Bean定义除了加些 Spring 注解,好像平淡无奇!以致在后续使用时随心定义:
package com.javaedge.spring.service;
@Service
public class MyService {
private String myServiceName;
public MyService(String myServiceName) {
this.myServiceName = myServiceName;
}
public String sayHello() {
return "hello Java";
}
}
MyService显式定义了一个构造器。但上面代码不是永远都能正确运行,有时报错:
***********************************
APPLICATION FAILED TO START
***********************************
Description:
Parameter 0 of constructor in com.javaedge.spring.service.MyService required a bean of type 'java.lang.String' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
2 解惑
创建一个 Bean 时,调AbstractAutowireCapableBeanFactory的
2.1 createBeanInstance
- 寻找构造器
- 通过反射调用构造器创建实例
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
2.2 determineConstructorsFromBeanPostProcessors
Spring先执行它获取构造器:
仅一个有效实现类。然后通过 autowireConstructor 带着构造器去创建实例。
看ConstructorResolver的
2.3 instantiate
autowireConstructor方法要创建实例:
- 不仅要知道是啥构造器
- 还要知道构造器对应参数
从最后创建实例的方法名也可看出:
private Object instantiate(
String beanName, RootBeanDefinition mbd, Constructor<?> constructorToUse, Object[] argsToUse)
argsToUse咋获取?即当已知构造器ServiceImpl(String serviceName),要创建 ServiceImpl 实例,咋确定serviceName值?
使用 Spring,不能直接显式 new 创建实例。Spring只能寻找依赖作为构造器调用参数。那这参数咋获取?
看ConstructorResolver的
2.4 autowireConstructor
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
可调用 createArgumentArray 构建调用构造器的参数数组,最终从 BeanFactory 获取 Bean:
即根据参数寻找对应 Bean。本案例中,如找不到对应 Bean,抛异常,提示装配失败。
3 修正
定义一个类为 Bean,若再显式定义构造器,则该 Bean 在构建时,会自动根据构造器参数定义寻找对应 Bean,反射创建该 Bean。可直接定义一个能让 Spring 装配给 MyService 构造器参数的 Bean,如:
package com.javaedge.spring.service;
@Service
public class MyService {
private String myServiceName;
public String sayHello() {
return "hello Java";
}
// 该bean装配给MyService的构造器参数-myServiceName
@Bean
public String myServiceName() {
return "MyServiceName";
}
}
综上,使用 Spring 时,别想当然认为定义的 Bean 也可在非 Spring 场合直接new!
若不精通 Spring 的隐式规则,在修正问题后,可能写出更多看起来好像可运行的程序:
@Service
public class MyService {
private String myServiceName;
public MyService(String myServiceName) {
this.myServiceName = myServiceName;
}
public MyService(String myServiceName, String otherParam) {
this.myServiceName = myServiceName;
}
review这段代码,可能不会发现什么问题,毕竟 String 类型可自动装配,无非加个 String 参数。但若你知 Spring 内部用反射构建 Bean,就发现问题:存在两个构造器,都可调用时,到底调用哪个?最终 Spring 无从选择,只能尝试调用默认构造器,而默认构造器不存在:
所以报错:
本文由博客一文多发平台 OpenWrite 发布!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。