了解Spring @Autowired用法


309

我正在阅读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(); 
    } 
}

将调用哪种设计方法?如何确定Red类的设计方法将被调用而不是Blue?

Answers:


542

TL; DR

@Autowired注释使您无需亲自在XML文件中(或通过任何其他方式)进行布线,而只是为您找到需要注入的内容,然后为您完成。

完整说明

@Autowired注释允许你对什么注入其他地方跳过配置,只是它给你的。假设您的软件包是com.mycompany.movies您必须将此标记放入XML(应用程序上下文文件)中:

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

该标签将进行自动扫描。假设必须成为Bean的每个类都使用正确的注释@Component(例如(对于简单bean)或@Controller(对于servlet控件)或@Repository(对于DAO类))进行注释,并且这些类位于包的某个位置com.mycompany.movies,Spring将查找所有这些并创建每个豆一个。这是通过对类的2次扫描完成的-第一次是它仅搜索需要成为bean的类并映射它需要进行的注入,而在第二次扫描中,它将注入bean。当然,您可以在更传统的XML文件中或使用@Configuration类(或这三种的任意组合)来定义bean 。

@Autowired注解告诉Spring去哪里注射需要发生。如果将其放在方法上,setMovieFinder它将理解(通过前缀set+ @Autowired注释)需要注入bean。在第二次扫描中,Spring搜索类型为的bean MovieFinder,如果找到该bean,则将其注入此方法。如果它找到两个这样的豆,您将得到一个Exception。为了避免Exception,您可以使用@Qualifier批注并以以下方式告诉它要注入的两个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以指示要注入的两种颜色的bean:

@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不为null,并且在尝试启动应用程序时会快速失败,并NullPointerException在需要实际使用Bean时避免出现。

更新:为了完成图片,我创建了一个关于班级的新问题@Configuration


6
只是为了完成您的出色回答:“ @ Resource”是JSR-250标准的一部分,除简单注入外,还具有额外的语义(正如您所说的“ @Autowired”来自Spring;“ @ Inject”是Java注入的一部分) JSR-330):)
Ignacio Rubio

如果MovieFinder是Interface,并且有一个bean MovieFinderImpl(bean id = movieFinder),Spring将按类型或名称自动注入它吗?
Jaskey

@jaskey-取决于您是否使用@Qualifier。如果您这样做-按名称,如果不是,则按类型。仅当您MovieFinder的上下文中只有一个类型的bean时,按类型才会起作用。大于1会导致异常。
2015年

@Avi,很棒的答案。但是我不理解@Autowired注释如何prepare示例2中的方法上起作用。它正在初始化,MovieRecommender但从技术上讲,它不是设置器。
卡兰·查达

@KaranChadha-这@Autowired也适用于构造函数。它找到所需的依赖项,并将其注入到构造函数中。
Avi

21

该示例中没有任何内容说“实现相同接口的类”。MovieCatalog是一种类型,CustomerPreferenceDao是另一种类型。春天可以轻松区分它们。

在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我没有调用该方法,在创建bean时Spring会这样做。正是在@Autowired注入字段时才发生这种情况。然后,Spring将看到需要参数,并且它将使用用于字段注入的相同规则来查找参数。
亚伦·迪古拉

5

是的,您可以配置Spring servlet上下文xml文件来定义您的bean(即类),以便它可以为您执行自动注入。但是,请注意,要启动并运行Spring,您还必须进行其他配置,而做到这一点的最佳方法是遵循本教程。

一旦配置好Spring,就可以在上面的示例1的Spring servlet上下文xml文件中执行以下操作(请将com.movi​​es的程序包名称替换为真实的程序包名称,如果这是第三方,则为真实名称)。类,然后确保相应的jar文件在classpath上):

<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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.