理解Spring中@Autowired的用法

351

我正在阅读Spring 3.0.x参考文档,以了解Spring Autowired注释:

3.9.2 @Autowired和@Inject

我不理解以下示例。我们需要在XML中做些什么才能使其工作?

示例1:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

例子2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
如何在实现相同接口并使用相同类的情况下,进行两个类的自动装配?
示例:
class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

哪种设计方法会被调用?我该如何确保调用红色类的设计方法而不是蓝色类的?

3个回答

590

TL;DR

The @Autowired annotation automatically injects dependencies for you.

Full explanation

The @Autowired annotation spares you the need to manually configure dependency injection in XML files or other ways. It finds what needs to be injected and does so for you. If your package is com.mycompany.movies, you can use this tag in your application context file:

<context:component-scan base-package="com.mycompany.movies" />

这个标签将进行自动扫描。假设每个需要成为bean的类都用正确的注释进行了标注,例如@Component(用于简单的bean)、@Controller(用于servlet控制器)或@Repository(用于DAO类),并且这些类位于com.mycompany.movies包下的某个地方,Spring将找到它们并为每一个创建一个bean。这是通过两次类扫描完成的——第一次只搜索需要成为bean的类并映射需要进行的注入,第二次扫描会注入这些bean。当然,你也可以在传统的XML文件或@Configuration类中定义你的bean(或者任意三种方式的组合)。

@Autowired注释告诉Spring需要进行注入的位置。如果你将它放在一个叫做setMovieFinder的方法上,Spring就会(通过前缀set@Autowired注解)理解需要注入一个bean。在第二次扫描中,Spring会搜索类型为MovieFinder的bean,如果找到这样的bean,就会将其注入到这个方法中。如果找到两个这样的bean,就会抛出一个Exception异常。为了避免这种异常,你可以使用@Qualifier注解并告诉Spring应该注入其中的哪一个bean,方法如下:

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

如果您更喜欢在XML中声明bean,那么它应该会像这样:

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>
@Autowired声明中,您需要添加@Qualifier来告诉注入哪个颜色的豆子中的两种豆子之一:
@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

如果您不想使用两个注释(@Autowired@Qualifier),可以使用@Resource将它们合并:

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

@Resource (你可以在这个答案的第一个评论中了解更多有关它的信息) 可以替代两个注释,只使用一个注释。

我想再添加两点:

  1. 好的实践应该使用 @Inject 而不是 @Autowired,因为它不是特定于 Spring 的,并且是 JSR-330 标准的一部分
  2. 另一个好的实践是将 @Inject / @Autowired 放在构造函数上而不是方法上。如果你将它放在构造函数上,你可以验证注入的 bean 不为空,并在尝试启动应用程序时快速失败,避免在实际使用 bean 时出现空指针异常。

更新:为了完整地介绍这个问题,我创建了一个关于 @Configuration 类的新问题


7
只是为了完善你的回答:'@Resource' 是 JSR-250 标准的一部分,比简单注入多了额外的语义(正如你所说的 '@Autowired' 是 Spring 的一部分;而 '@Inject' 是 JSR-330 的一部分) :) - Ignacio Rubio
1
如果MovieFinder是一个接口,而我们有一个名为MovieFinderImpl(bean id = movieFinder)的bean,Spring会按类型还是按名称自动注入它? - JaskeyLam
@jaskey - 这取决于您是否使用@Qualifier。如果是,按名称;如果不是,则按类型。按类型仅在上下文中只有一个类型为MovieFinder的bean时才有效。超过1个将导致异常。 - Avi
1
@Avi,非常棒的回答。但是我不明白在示例2@Autowired注解如何作用于prepare方法。它正在初始化MovieRecommender,但从技术上讲,它并不是一个setter。 - Chesser
1
@KaranChadha - @Autowired也适用于构造函数。它会查找所需的依赖项并将它们注入到构造函数中。 - Avi
显示剩余4条评论

22

示例中并未说明"实现相同接口的类"。 MovieCatalog是一种类型,CustomerPreferenceDao是另一种类型。Spring可以轻松区分它们。

在Spring 2.x中,大多数bean的装配都是通过bean ID或名称进行的。这在Spring 3.x中仍然得到支持,但通常情况下,您会拥有具有特定类型的一个bean实例-大多数服务都是单例的。为这些创建名称很繁琐。因此,Spring开始支持"按类型自动装配"。

示例展示了各种方法,您可以使用这些方法将bean注入字段、方法和构造函数中。

XML已经包含了Spring需要的所有信息,因为您必须在每个bean中指定完全限定的类名。不过,您需要对接口略加小心:

以下自动装配将失败:

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

由于Java在字节码中不保存参数名,Spring无法区分这两个bean。解决方法是使用@Qualifier

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }

@AaronDigulla 非常不错。但是我想知道,您如何调用函数 prepare,将使用哪些参数来调用此函数? - Nguyen Quang Anh
@NguyenQuangAnh 我不会调用这个方法,Spring 会在创建 bean 时自动调用它。这正好发生在 @Autowired 字段注入时。然后 Spring 将看到需要参数,并使用与字段注入相同的规则来查找参数。 - Aaron Digulla

6
是的,您可以配置Spring servlet上下文xml文件来定义您的bean(即类),这样它就可以为您自动注入。但是,请注意,您必须进行其他配置才能让Spring运行起来,最好的方法是从头开始跟随教程。一旦您的Spring配置完成,您可以在Spring servlet上下文xml文件中执行以下操作以使示例1正常工作(请将com.movies 的软件包名称替换为真实软件包名称,如果这是第三方类,则请确保适当的jar文件在类路径上):
<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

如果MovieFinder类具有带原始值的构造函数,则可以像这样编写代码:

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

或者,如果MovieFinder类有一个期望另一个类的构造函数,那么你可以像这样做:
<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

...其中 'otherBeanRef' 是另一个具有对期望类的引用的bean。


4
定义XML中的所有连接线路,忽略了"@Autowired"背后的整个概念。 - Avi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接