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 虽然标注已经被废弃了、但是不妨碍我们了解一下它。

网络异常,图片无法展示
|

  • 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 获取 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<String> 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<String, Object> 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<String, Object> 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 的情况


总结


自动注入相对显式注入、在实际场景中确实用得比较少、但是了解其过程还是会让你收获到一些相关的知识和流程



目录
相关文章
|
4月前
|
XML Java 程序员
Spring6框架中依赖注入的多种方式(推荐构造器注入)
依赖注入(DI)是一种过程,对象通过构造函数参数、工厂方法的参数或在对象实例构建后设置的属性来定义它们的依赖关系(即与其一起工作的其他对象)。
64 3
|
1月前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
22天前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
27 0
|
22天前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
30 0
|
2月前
|
Java Spring
spring注入的几种方式
spring注入的几种方式
18 0
|
3月前
|
Java Spring 容器
spring如何进行依赖注入,通过set方法把Dao注入到serves
spring如何进行依赖注入,通过set方法把Dao注入到serves
|
3月前
spring-boot报错循环注入报错:has been injected into other beans
spring-boot报错循环注入报错:has been injected into other beans
206 3
|
3月前
|
Java Linux 程序员
技术笔记:Spring生态研习【五】:Springboot中bean的条件注入
技术笔记:Spring生态研习【五】:Springboot中bean的条件注入
|
3月前
|
SQL 安全 Java
Spring Boot中的跨站点脚本攻击(XSS)与SQL注入防护
【6月更文挑战第15天】在现代Web应用程序开发中,安全性是一个至关重要的课题。跨站点脚本攻击(XSS)和SQL注入是最常见的两种攻击类型,它们可以严重威胁到应用程序的安全。
456 0
|
4月前
|
前端开发 Java 编译器
详解Spring与JDK注入
依赖注入是Spring框架的核心概念之一,它通过将对象之间的依赖关系外部化,实现了松耦合和可测试性。面向切面编程则允许开发人员将横切关注点(如日志、事务管理)从应用程序的主要业务逻辑中分离出来,以提高代码的模块化和可维护性。
44 4