Java注解之@Autowired,一文掌握@Autowired注解知识(1)

发布时间:2023年12月27日

在这里插入图片描述

🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。
🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。
🎉欢迎 👍点赞?评论?收藏

🏆 学习Java注解之@Autowired

🔎 Java 注解@Autowired学习(1)

🍁🍁 01、@Autowired 注解的作用是什么?如何使用它?

在Java中,@Autowired注解的作用是将依赖关系自动注入到类中,它是Spring框架中的一个核心注解之一。@Autowired可以用于自动装配一个类的成员变量、构造函数或者方法,以实现依赖注入(Dependency Injection)。

使用 @Autowired 注解的基本步骤如下:

1)在需要进行依赖注入的类中, 使用 @Autowired 注解来标记需要注入的成员变量、构造函数或者方法。

 public class MyClass {
     @Autowired
     private MyDependency myDependency;
     
     // 或者在构造函数中使用
     // @Autowired
     // public MyClass(MyDependency myDependency) {
     //     this.myDependency = myDependency;
     // }
     
     // 或者在方法中使用
     // @Autowired
     // public void setMyDependency(MyDependency myDependency) {
     //     this.myDependency = myDependency;
     // }
 }

2)在 Spring 的配置文件中或者使用注解配置的类中,需要配置自动扫描和启用自动装配功能。

  • 配置 XML 文件示例:
<context:component-scan base-package="com.example.package" />
  • 配置 Java 类示例:
@Configuration
@ComponentScan("com.example.package")
public class AppConfig {
    // ...
}

3)确保所依赖的类在 Spring 容器中有对应的 Bean 定义,以供注入。

  • 使用 @Component@Service@Repository@Controller等注解来标记需要创建 Bean 的类。

  • 或者在配置文件中进行显式的 Bean 定义。

通常,当Spring容器启动时,将会自动进行扫描和实例化,解析 @Autowired 注解,将相应的依赖注入到类中。这样,就可以在代码中直接使用被注入的依赖对象了。

需要注意的是,在进行依赖注入时,需要确保注入目标的类型和上下文中的 Bean 类型是兼容的,否则可能会导致注入失败。如果有多个候选 Bean,可以使用 @Qualifier 注解来指定具体的 Bean。另外,还可以使用 @Autowiredrequired 属性来控制是否强制进行依赖注入,默认值为 true

总结起来,@Autowired 注解使得在 Spring 中进行依赖注入变得更加简单和便捷,加快了开发速度和降低了代码的耦合度。

当我们使用 @Autowired 注解进行依赖注入时,还可以使用它的一些属性来调整注入行为。以下是一些常用的属性:

1) required 该属性用于指定依赖是否是必需的,默认值为 true。如果将 required 设置为 false,当找不到匹配的依赖时,Spring 容器不会抛出异常,而是将注入字段设置为 null

@Autowired(required = false)
private MyDependency myDependency;

2) qualifier:当有多个相同类型的 Bean 存在时,可以使用 @Qualifier 注解配合 @Autowired 使用,指定要注入的具体 Bean 的名称。

@Autowired
@Qualifier("myBean")
private MyDependency myDependency;

3) primary:在多个 Bean 候选项中,可以使用 @Primary 注解来标记一个主要的 Bean,让 Spring 在注入时优先选择该 Bean。

@Primary
@Component
public class PrimaryDependency implements MyDependency {
    // ...
}

4) valuename:这两个属性用于在没有使用 @Qualifier 注解时,指定要注入的具体 Bean 的名称。

@Autowired
@Qualifier("myBean")
// 或者
// @Autowired
// @Value("myBean")
private MyDependency myDependency;

需要注意的是,@Autowired 注解通常用于字段注入,但也可以用于构造函数注入和方法注入。

构造函数注入示例:

@Autowired
public MyClass(MyDependency myDependency) {
    this.myDependency = myDependency;
}

方法注入示例:

@Autowired
public void setMyDependency(MyDependency myDependency) {
    this.myDependency = myDependency;
}

这样,在创建 MyClass 类的实例时,Spring 框架会自动寻找合适的依赖对象,并注入到相应的位置。

总之,@Autowired 注解是 Spring 框架中用于实现依赖注入的关键注解之一,通过它可以方便地将依赖对象注入到类中,减少了手动的对象创建和依赖传递操作,提高了代码的可维护性和可测试性。

🍁🍁 02、@Autowired 注解的工作原理是什么?

@Autowired 注解是 Spring 框架中用于实现依赖注入的关键注解之一,它的工作原理可以简要描述如下:

  1. Spring 容器在启动时会初始化并管理一个应用程序的所有 Bean。

  2. 当发现一个类中带有 @Autowired 注解的字段、构造函数或者方法时,容器会尝试解析这个注解,并找到匹配的依赖对象。

  3. 容器首先会根据类型匹配查找相应的依赖对象。如果存在多个同类型的 Bean,容器会使用属性的名称或者 @Qualifier 注解指定的名称来进行进一步的匹配。

  4. 如果匹配成功,容器会将依赖对象注入到目标字段、构造函数或者方法中,完成依赖注入的过程。

需要注意的是,@Autowired 注解还可以和其他注解一起使用,例如 @Qualifier@Primary 等,来更精确地控制依赖注入的行为。

除了字段注入,@Autowired 注解也可以用于构造函数注入和方法注入。在构造函数注入中,Spring 容器会根据参数列表的类型和名称来匹配对应的依赖对象;在方法注入中,容器会根据方法的参数类型和名称来进行匹配。

总结起来,@Autowired 注解的工作原理可以简单概括为:通过类型匹配和名称匹配,将合适的依赖对象自动注入到目标位置,实现依赖注入的功能。这样可以避免手动创建和传递对象的繁琐过程,提高代码的可读性和可维护性。

当使用 @Autowired 注解进行依赖注入时,Spring 容器会按照以下步骤来找到匹配的依赖对象:

  1. 首先,Spring 容器会检查目标类型是否有多个具体实现类(多个相同类型的 Bean)。如果没有多个具体实现类,Spring 容器就会直接使用该类型的 Bean 进行注入。

  2. 如果目标类型有多个具体实现类,Spring 容器会进一步检查是否有一个 Bean 被标记为 @Primary。如果有,Spring 容器会选择 @Primary 注解标记的 Bean 进行注入。

  3. 如果没有 @Primary 注解的 Bean,或者不止一个 Bean 被标记为 @Primary,Spring 容器会尝试使用 @Qualifier 注解进行匹配。@Qualifier 注解可以与 @Autowired 注解一起使用,用于指定具体要注入的 Bean 的名称,解决多个相同类型的 Bean 的歧义性。

  4. 如果以上步骤都无法找到匹配的依赖对象,Spring 容器会抛出异常,指示找不到合适的依赖对象,除非依赖对象被标记为可选的(required = false)。

需要注意的是,@Autowired 注解除了可以用于字段注入外,还可以应用于构造函数注入和方法注入。在构造函数注入时,Spring 容器会尝试解析构造函数参数的类型和名称来找到匹配的依赖对象。在方法注入时,比如通过 @Autowired 注解标记的 setter 方法,Spring 容器会根据方法参数的类型和名称进行依赖匹配。

总结起来,@Autowired 注解通过类型匹配和名称匹配的方式,帮助 Spring 容器找到匹配的依赖对象进行注入。如果存在多个相同类型的 Bean,可以通过 @Primary 注解或者 @Qualifier 注解来指定具体要注入的 Bean。这样可以提高代码的灵活性和可维护性,支持应用程序的模块化和扩展。

🍁🍁 03、@Autowired 和 @Resource 注解有什么区别?

@Autowired@Resource 注解都是用于依赖注入的注解,它们的主要区别如下:

1) 来源

@Autowired 注解是 Spring 框架提供的注解,用于实现依赖注入。

@Resource 注解是 Java EE 提供的注解,从 JDK 1.6 版本开始引入,用于实现依赖注入。

2) 使用方式

@Autowired 注解可以用于字段、构造函数和方法上,实现自动装配。

@Resource 注解可以用于字段、setter 方法和构造函数上,实现资源注入。

3) 依赖解析方式

@Autowired 注解的解析是通过类型匹配和名称匹配来实现的。如果存在相同类型的依赖对象,可以使用 @Qualifier 注解指定具体的 Bean 名称。

@Resource 注解的解析是通过名称匹配来实现的。可以通过 name 属性指定具体要注入的 Bean 名称。

4) 兼容性

@Autowired 注解是 Spring 框架特有的注解,在使用 Spring 框架时才能生效。

@Resource 注解是 Java EE 规范的一部分,可以在不依赖于 Spring 容器的环境中使用。

5) 使用范围

@Autowired 注解可以用于任何 Spring 托管的 Bean 类中,无论是 XML 配置还是注解配置。

@Resource 注解可以用于任何 Java EE 容器管理的 Bean 类中,包括 EJB、Servlet、JSF 等。

总的来说,@Autowired 注解是 Spring 框架特有的,用于实现依赖注入,可以通过类型匹配和名称匹配来解析注入的依赖对象。而 @Resource 注解是 Java EE 规范的一部分,可以在不依赖于 Spring 容器的环境中使用,通过名称匹配来解析注入的依赖对象。

下面是 @Autowired@Resource 注解的区别表格说明:

区别@Autowired@Resource
注解来源Spring 框架提供的注解Java EE 规范的注解
使用方式可以用于字段、构造函数、方法可以用于字段、setter 方法、构造函数
依赖解析方式通过类型匹配和名称匹配来解析注入的依赖对象通过名称匹配来解析注入的依赖对象
兼容性只能在使用 Spring 容器的环境中使用可以在任何 Java EE 容器管理的 Bean 类中使用
使用范围可以用于任何 Spring 托管的 Bean 类中(包括 XML 配置和注解配置)可以用于任何 Java EE 容器管理的 Bean 类中(包括 EJB、Servlet、JSF 等)
指定依赖对象名称的方式通过 @Qualifier 注解来指定依赖对象名称,也可以不指定。通过 name 属性(或者 lookup 属性)来指定依赖对象名称,如果不指定则使用默认规则。(JDK 1.7 以上,也可以通过 type 属性指定类型)
是否支持可选依赖支持通过 required 属性来指定是否要求必须注入依赖对象,默认为 true(必须注入)。支持通过 required 属性来指定是否要求必须注入依赖对象,默认为 true(必须注入)。

总结:@Autowired 的优势在于其支持类型匹配和名称匹配,且可以用于任何 Spring 托管的 Bean 类中。同样,@Resource 的优势在于其遵循 Java EE 规范,兼容性更好,并且可以通过 name 属性来指定依赖对象的名称。对于选择哪种注解,取决于具体应用环境和需求。

🍁🍁 04、@Autowired 的依赖注入是按照什么规则进行的?如何修改默认的依赖注入规则?

Spring 的 @Autowired 注解默认按照类型匹配和名称匹配的方式进行依赖注入。具体来说,Spring 会在容器中查找与被注入对象属性类型相同的 Bean 实例。如果同一类型有多个 Bean 时,Spring 会根据属性名称再去查找与属性名称相同的 Bean 实例。

如果想要修改默认的依赖注入规则,可以通过以下方式之一:

  1. @Autowired 注解配合 @Qualifier 使用:使用 @Qualifier 注解指定要注入的 Bean 的名称,这样就可以解决同一类型有多个 Bean 的问题。

  2. @Autowired 注解配合 @Primary 使用:使用 @Primary 注解标注某个 Bean,表示该 Bean 是首选的 Bean,当同一类型有多个 Bean 的时候,优先选择该 Bean。

  3. 在 XML 中设置 autowire-candidate 属性:在 XML 中设置 <bean> 标签的 autowire-candidate 属性为 false,表示不将该 Bean 暴露给自动装配,从而避免自动装配时出现意外情况。

  4. 使用 QualifierAnnotationAutowireCandidateResolver:使用QualifierAnnotationAutowireCandidateResolver 这个类自定义自动装配规则,重写其 findAutowireCandidates() 方法。

通过以上的方式,就可以实现自定义注入方式。

当使用注解方式进行依赖注入时,默认的注入规则可以通过修改@Autowiredrequired属性来改变。@Autowired注解的required属性默认为true,表示必须要找到对应的依赖进行注入,如果找不到,会抛出异常。如果将required属性设置为false,则表示找不到依赖时不会抛出异常,该属性的修改可以通过在@Autowired注解中设置required=false来实现。

另外,Spring也提供了其他一些注解用于依赖注入,例如:

  • @Inject:与@Autowired类似,不过是Java规范中的注解,需要使用javax.inject包。

  • @Resource:也可以用于依赖注入,可以根据属性名称进行依赖查找。如果找到的匹配项是集合类型的话,Spring会将所有匹配项注入到属性中。

通过使用这些注解,我们可以更加灵活地进行依赖注入,满足不同的场景需求。

🍁🍁 05、@Autowired 注解是如何解决循环依赖的?

循环依赖是指两个或多个 Bean 互相依赖,形成一个闭环的情况。例如,Bean A 依赖于 Bean B,而 Bean B 又依赖于 Bean A。在这种情况下,如果使用 @Autowired 注解进行依赖注入,会导致循环依赖的问题。

Spring 使用了三级缓存来解决循环依赖的问题。具体的解决过程如下:

  1. 创建 Bean 的过程中,如果发现循环依赖,会将当前正在创建的 Bean 提前暴露为一个 ObjectFactory(延迟初始化Bean)。

  2. 当其他 Bean 需要依赖该 Bean 时,会通过 ObjectFactory 获取 Bean 的实例。这样就能在运行时获取到正在创建的 Bean,而不会产生循环依赖的错误。

  3. 当这个正在创建的 Bean 创建完毕后,会将其设置为已完成状态,这样其他依赖于它的 Bean 就可以正常注入了。

需要注意的是,Spring 对循环依赖的解决是通过延迟初始化和代理对象实现的。因此,如果循环依赖的 Bean 中有非默认的代理模式,或有非默认的初始化顺序,可能会导致解决循环依赖失败。

总结来说,@Autowired 注解本身并不能直接解决循环依赖的问题,而是通过 Spring 容器的循环依赖解决机制来完成。

或者

当两个或多个Bean之间存在循环依赖关系时,Spring的循环依赖解决方案如下:

  1. 首先,Spring会使用@Autowired注解的方式创建Bean对象的代理,而不是直接创建实例。

  2. 当Bean A创建时,Spring会将其包装在代理对象中,并将代理对象暴露给正在创建的Bean B。

  3. 当Bean B创建时,Spring会检查Bean B的依赖关系,发现它需要依赖Bean A。此时,Spring会检查Bean A是否已经创建过。

  4. 如果Bean A已经创建过且处于代理状态,Spring不会再次创建新的实例,而是将之前创建的代理对象注入到Bean B中。

  5. 同样地,当Bean A需要依赖Bean B时,Spring会发现Bean B已经创建过且处于代理状态,将之前创建的代理对象注入到Bean A中。

通过使用代理对象,Spring完成了循环依赖的注入。这种解决方案的前提是 Bean A 和 Bean B 都需要使用接口而不是具体的实现类进行注入。因为代理对象是基于接口生成的,如果依赖关系是基于具体的实现类,则无法创建代理对象来解决循环依赖。

需要注意的是,循环依赖可能会导致性能问题,并且过多的循环依赖可能会影响应用程序的可维护性。因此,尽量避免出现过多的循环依赖,并且要谨慎设计类之间的依赖关系。

🍁🍁 06、@Autowired 的 required 属性的作用是什么?

@Autowired 注解的 required 属性用于指定依赖注入是否是必需的,默认值为 true

required 属性为 true 时,如果找不到匹配的依赖对象,则会在应用程序上下文启动时抛出异常。这意味着如果没有找到适合的依赖对象,Spring 将无法自动装配该依赖项。

required 属性为 false 时,如果找不到匹配的依赖对象,Spring 将不会抛出异常,而是允许该依赖项为 null。这意味着该依赖项是可选的,如果没有找到依赖对象,也不会影响其他部分的正常运行。

对于非必需的依赖项,可以使用 @Autowired(required = false) 或简化的 @Autowired 注解来注入,如下所示:

@Autowired(required = false)
private SomeDependency someDependency;

或者:

@Autowired
private SomeDependency someDependency;

需要注意的是,如果设置 required 属性为 false,在代码中使用该依赖项时,应该先进行非空检查,以避免出现 NullPointerException

默认情况下,@Autowiredrequired 属性为 true,因此如果没有显式设置该属性,会抛出异常来标识需要的依赖项无法注入的情况。然而,根据具体情况,可以根据需要设置 required 属性来处理可选的依赖项。

🍁🍁 07、如何处理多个实现类时使用 @Autowired 注解的冲突?

当存在多个实现类时,使用 @Autowired 注解注入依赖项可能会引起冲突。Spring 提供了几种解决方案来解决这种冲突。

  1. 使用 @Qualifier 注解:使用 @Autowired 注解时,可以结合 @Qualifier 注解指定具体的实现类,如下所示:

    @Autowired
    @Qualifier("specificClassA")
    private SpecificInterface specificImplA;
    

    其中,@Qualifier 注解指定了 SpecificInterface 的具体实现类为 specificClassA

  2. 使用 @Primary 注解:在多个实现类中,可以使用 @Primary 注解指定一个默认的实现类。如下所示:

    @Component(value = "specificClassA")
    @Primary
    public class SpecificImplA implements SpecificInterface {
        // ...
    }
    

    在使用 @Autowired 注解时,可以不指定具体的实现类,Spring 将使用 @Primary 注解指定的实现类,如下所示:

    @Autowired
    private SpecificInterface specificInterface;
    
  3. 使用 ListMap 数据结构:当存在多个实现类时,可以将其注入到列表或 Map 数据结构中,然后通过循环遍历获取需要的实现类。如下所示:

    @Autowired
    private List<SpecificInterface> specificInterfaces;
    
    @Autowired
    private Map<String, SpecificInterface> specificInterfaceMap;
    
    public void doSomething() {
        for (SpecificInterface si : specificInterfaces) {
            // ...do something...
        }
    }
    

    或者:

    public void doSomething() {
        specificInterfaceMap.get("specificClassA").doSomething();
    }
    

以上三种方式各有优缺点,可以根据实际情况选择合适的方式来处理多个实现类时的依赖冲突。

以下是三种处理多个实现类时使用 @Autowired 注解的方式的优缺点和使用场景:

  1. 使用 @Qualifier 注解:

    • 优点:可以在注入时使用 @Qualifier 显式指定具体的实现类,灵活性高。
    • 缺点:需要手动指定每个实现类的 @Qualifier 值,容易出错。
    • 使用场景:当使用的实现类是可变的,可能需要在不同情况下注入不同的实现类时,使用 @Qualifier 注解是一个不错的选择。
  2. 使用 @Primary 注解:

    • 优点:可以设置一个默认的实现类,简化代码,不需要额外的注解。
    • 缺点:如果需要切换实现类,需要手动更改 @Primary 注解的位置或值。
    • 使用场景:当某个实现类是默认的实现类,大多数情况下都使用该实现类时,可以使用 @Primary 注解。
  3. 使用 ListMap 数据结构:

    • 优点:可以将多个实现类注入到集合或映射数据结构中,方便遍历或根据键进行查找。
    • 缺点:需要进行迭代或查找操作,稍微繁琐一些。
    • 使用场景:当需要同时处理多个实现类时,或者需要根据不同的条件选择不同的实现类时,可以将实现类注入到 ListMap 中。

选择适当的方式取决于具体的应用场景。如果只有少数的实现类或者是固定的实现类,使用 @Qualifier@Primary 注解可以更加简洁和明确。如果实现类较多或者需要动态选择实现类,使用 ListMap 数据结构能够更好地处理。

下表列举了三种处理多个实现类使用 @Autowired 注解的方式的优缺点和使用场景:

方式优点缺点使用场景
@Qualifier 注解- 可以显式指定具体的实现类
- 灵活性高
- 需要手动指定每个实现类的 @Qualifier
- 容易出错
- 使用的实现类是可变的
- 根据不同情况注入不同的实现类
@Primary 注解- 可以设置一个默认的实现类
- 简化代码
- 切换实现类时需要手动更改注解位置或值- 某个实现类是默认的实现类
- 大多数情况下都使用同一个实现类
ListMap 数据结构- 方便将多个实现类注入到集合或映射中
- 可以根据条件选择实现类
- 需要进行迭代或查找操作- 同时处理多个实现类
- 根据不同条件选择不同实现类

根据具体需求和情况选择适合的方式。如果只有少数实现类或固定的实现类,使用 @Qualifier@Primary 注解较为简洁和直观。如果有多个实现类或需要动态选择实现类,使用 ListMap 数据结构更加灵活。

🍁🍁 08、是否可以将 @Autowired 注解应用在非 Spring 管理的对象上?

@Autowired 注解是 Spring 框架提供的依赖注入的功能,主要用于将一个 Bean 对象自动注入到另一个 Bean 对象中。因此,@Autowired 注解通常用于在 Spring 容器中管理的对象之间建立依赖关系。

对于非 Spring 管理的对象,@Autowired 注解是无法直接应用的。因为 @Autowired 注解需要依赖 Spring 的自动装配功能,它要求被注入的对象必须由 Spring 容器管理。非 Spring 管理的对象没有被 Spring 托管,所以无法使用 @Autowired 进行自动注入。

但是,可以通过其他方式手动注入非 Spring 管理的对象,例如通过构造函数、Setter 方法或普通的方法参数传递等。这样可以手动建立对象之间的依赖关系,不依赖于 @Autowired 注解来完成注入。

总而言之,@Autowired 注解不能直接应用在非 Spring 管理的对象上,但可以通过其他手段来实现对象之间的依赖注入。

🍁🍁 09、如何在测试环境中模拟 @Autowired 注解的依赖注入?

在测试环境中模拟 @Autowired 注解的依赖注入,可以借助各种测试框架和工具来实现。下面是几种常用的方法:

  1. 使用测试框架(如JUnit、TestNG)和模拟工具(如Mockito、EasyMock):通过创建一个模拟对象(Mock Object),然后将其注入到被测试对象中。可以使用模拟工具提供的注解(如 @Mock)来模拟依赖对象,并通过依赖注入或设置的方式将模拟对象注入到被测试对象中。

  2. 手动创建并注入依赖对象:在测试环境中,可以手动创建依赖对象,并通过构造函数、Setter 方法或普通的方法参数传递等方式将其注入到被测试对象中。这样可以在测试过程中完全控制依赖对象的行为。

  3. 使用依赖注入容器:在测试环境中使用一个独立的依赖注入容器(如Spring TestContext Framework),在测试配置文件中定义依赖对象的模拟或替代实现,并通过容器进行依赖注入。可以使用 @Autowired 注解来标记需要注入依赖的字段或方法。

这些方法的选择取决于具体的测试环境和需求。通过模拟依赖对象,在测试中可以更灵活地控制依赖的行为和结果,从而有效地隔离被测试对象。

当使用测试框架和模拟工具时,可以按照以下步骤来模拟 @Autowired 注解的依赖注入:

  1. 在测试类中,使用模拟工具(如Mockito)创建一个模拟对象,并使用 @Mock 注解将其标记为模拟对象。例如:

    @Mock
    private DependencyObject dependencyObject;
    
  2. 使用测试框架(如JUnit) 中的 @Before 注解,在测试方法运行之前进行初始化设置。在初始化方法中,使用 MockitoAnnotations.initMocks(this) 初始化所有使用了 @Mock 注解的模拟对象。例如:

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }
    
  3. 在被测试类中,使用 @Autowired 注解进行依赖注入。需要将依赖对象的访问修饰符设置为包可见(或更宽松的访问权限),以便测试类可以访问到它。例如:

    @Autowired
    PackageVisibleDependency dependencyObject;
    
  4. 在测试方法中,通过模拟工具的方法来设置模拟对象的行为,并调用被测试方法进行测试。例如:

    @Test
    public void testMethod() {
        // 设置模拟对象的行为
        when(dependencyObject.someMethod()).thenReturn("mocked result");
    
        // 调用被测试方法
        String result = testedObject.methodUnderTest();
    
        // 验证测试结果
        assertEquals("expected result", result);
    }
    

通过这种方式,可以在测试环境中模拟 @Autowired 注解的依赖注入,控制依赖对象的行为,并对被测试方法进行单元测试。

在这里插入图片描述

文章来源:https://blog.csdn.net/m0_50308467/article/details/135240536
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。