文章图片
我们经常注入的方式都是类似这样子的
@Service
public class HelloService {
@Autowired
private BeanFactory beanFactory;
@Autowired
public HelloService(ApplicationContext applicationContext) {
}
@Autowired
public void setEnvironment(Environment environment) {
}
}
不管是构造函数注入还是属性注入、我们都可以称为显式注入。
我们再来看看一个我们常用的注解
@Bean
public @interface Bean {@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
我们关注的属性
autowire
虽然标注已经被废弃了、但是不妨碍我们了解一下它。文章图片
NO
默认值、代表不会自动注入BY_NAME
、通过beanName
进行自动注入BY_TYPE
、通过参数的类型进行自动注入
@Configuration
public class Config {
@Bean(autowire = Autowire.NO)
public HelloService helloService() {
return new HelloService();
}
}public class HelloService {
public void setEnvironment(Environment environment) {
System.out.println("invoke " + environment);
}
}
我们使用默认的配置、不对
HelloService
进行自动注入、毫无疑问、setEnvironment
是不会被 Spring 框架调起的。Autowire.BY_NAME 我们将上面的代码改为
@Bean(autowire = Autowire.BY_NAME)
会发现
setEnvironment
被调起、并且参数 environment 就是 Spring 中的 StandardServletEnvironment文章图片
这一步如果对 Spring 获取 bean 流程有印象的、Spring 获取单例流程(三) 可以得知其在 AbstractAutowireCapableBeanFactory#populateBean 中被调用
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// 将自动注入的值放入到 newPvs 中
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
..........
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
对应的代码也是非常的简单的
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {// 根据 setter 方法获取需要主要的 beanName、解释 setter 方法
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
for (String propertyName : propertyNames) {
// 如果不在 BeanFactory 中、直接忽略、不会报错
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
// 加入到 pvs 中
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
}
else {
// 如果不存在该 bean、不报错、直接忽略
}
}
}
可以看到 BY_NAME 的方式、是通过解释 setter 方法名来获取需要注入的 beanName。如果 beanName 不存在 BeanFactory 中、直接忽略、类似于 @Autowired(required=false) 。不是强制性注入
但是你的方法名称必须是 setXxx 开头、符合一定的命名规则、跟 BeanInfo 的规则类似(Java 内省)、但是比它宽松、这里不限定返回值一定要是
void
Autowire.BY_TYPE
autowireByType
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {.......
Set autowiredBeanNames = new LinkedHashSet<>(4);
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
// 遍历 setter 方法解释获取其属性名
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
if (Object.class != pd.getPropertyType()) {
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
// AutowireByTypeDependencyDescriptor 这个类很关键、它返回的 getDependencyName 一直为 null
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
}
}
}
跟 BY_NAME 很相似的、那假如我自动注入的对象不存在 BeanFactory 中、是不是也不会抛异常呢?答案是的、这里主要是因为使用了这个类 AutowireByTypeDependencyDescriptor
private static class AutowireByTypeDependencyDescriptor extends DependencyDescriptor {public AutowireByTypeDependencyDescriptor(MethodParameter methodParameter, boolean eager) {
super(methodParameter, false, eager);
}@Override
public String getDependencyName() {
return null;
}
}
// 父类
public DependencyDescriptor(MethodParameter methodParameter, boolean required, boolean eager) {
super(methodParameter);
.......
this.required = required;
.......
}
第一个关键点就是 requied 为 false、当所依赖的 bean 不存在 BeanFactory 的时候、false 则不会抛异常。
第二个关键点则是、如果存在两个相同的 bean 、如果这两个 bean 都没有声明 Primary 或者 PriorityOrderd 的话、那么它还是会抛出异常的、而不会根据 beanName 去匹配筛选出合适的 bean、因为 getDependencyName 一直返回 null
这种情况跟 @Autowired 的方式不太一样、因为 BY_TYPE 的方式是不依赖 beanName的
@Nullable
protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) {
Class> requiredType = descriptor.getDependencyType();
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
for (Map.Entry entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
// resolvableDependencies
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
// BY_TYPE matchesBeanName 返回 false
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
return null;
}
文章图片
resolvableDependencies 中存放 key-value 的情况
总结 自动注入相对显式注入、在实际场景中确实用得比较少、但是了解其过程还是会让你收获到一些相关的知识和流程
【Spring — 自动注入 ()】给自己和所有看到这篇文章的同学加油~!!
文章图片
微信公众号:CoderLi
推荐阅读
- VS Code 2022路线图(大量Spring Boot优化提上日程!难道是被JB Code吓到了())
- Spring Boot 3.0.0 发布第一个里程碑版本M1,你的 Java 升到17 了吗()
- 稀疏数组与数组的关系与转化
- Synchronized的底层实现原理(看这篇就够了)
- main函数你到底知道多少
- 比postman更好用的API调试工具?apifox,永远滴神
- Dubbo拓展点加载机制
- Spring boot 2.0的Redis缓存应用
- 保姆级教程,终于搞懂脏读、幻读和不可重复读了!
- Spring Boot 2 中的默认日志管理与 Logback 配置详解