Spring — 自动注入 ()

Spring — 自动注入 ()
文章图片

我们经常注入的方式都是类似这样子的

@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 虽然标注已经被废弃了、但是不妨碍我们了解一下它。
Spring — 自动注入 ()
文章图片

  • NO 默认值、代表不会自动注入
  • BY_NAME 、通过 beanName 进行自动注入
  • BY_TYPE、通过参数的类型进行自动注入
Autowire.NO
@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 — 自动注入 ()
文章图片

这一步如果对 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; }

Spring — 自动注入 ()
文章图片

resolvableDependencies 中存放 key-value 的情况
总结 自动注入相对显式注入、在实际场景中确实用得比较少、但是了解其过程还是会让你收获到一些相关的知识和流程
【Spring — 自动注入 ()】给自己和所有看到这篇文章的同学加油~!!Spring — 自动注入 ()
文章图片

微信公众号:CoderLi

    推荐阅读