关于java:了解Spring @Autowired用法

Understanding Spring @Autowired usage

我正在阅读Spring3.0.x参考文档以了解SpringAutowired注释:

3.9.2@autowired和@inject

我无法理解下面的例子。我们需要在XML中做些什么才能使它工作吗?

例1

1
2
3
4
5
6
7
8
9
10
11
public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

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

    // ...
}

这两个类如何能够实现相同的接口并使用相同的类?

例子:

1
2
3
4
5
6
7
8
9
10
11
class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired
    private Color color;

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

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


DR

@autowired注释使您无需在XML文件中自己进行连接(或以任何其他方式),只需为您找到需要注入的内容,并为您完成这些操作。

完整的解释

@Autowired注释允许您跳过要注入的内容的其他配置,只需为您执行即可。假设您的包是com.mycompany.movies,那么您必须将这个标记放在XML(应用程序上下文文件)中:

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

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

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

1
2
3
4
5
6
7
8
9
@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

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

或者,如果您更愿意在XML中声明bean,它看起来应该是这样的:

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

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

@Autowired声明中,还需要添加@Qualifier来说明要注入的两种颜色的bean中的哪一种:

1
2
3
4
5
@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

如果不想使用两个注释(@Autowired@Qualifier,可以使用@Resource将这两个注释组合起来:

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

@Resource(您可以在这个答案的第一个注释中阅读一些关于它的额外数据)使您不用使用两个注释,而只使用一个注释。

我再加两条评论:

  • 好的做法是使用@Inject而不是@Autowired,因为它不是弹簧专用的,是JSR-330标准的一部分。
  • 另一个好的做法是将@Inject/@Autowired放在构造函数上,而不是方法上。如果将其放到构造函数上,则可以在尝试启动应用程序时验证注入的bean是否为空并快速失败,并在需要实际使用bean时避免使用NullPointerException
  • 更新:为了完成这张图片,我创建了一个关于@Configuration类的新问题。


    示例中没有说明"实现相同接口的类"。MovieCatalog是一种类型,CustomerPreferenceDao是另一种类型。春天很容易把它们分开。

    在Spring2.x中,bean的连接主要通过bean id或名称进行。这仍然由Spring3.x支持,但通常情况下,您会有一个具有特定类型的bean实例——大多数服务都是单例的。为那些人创建名字是乏味的。所以弹簧开始支持"自动排线"。

    示例所显示的是可以使用各种方法将bean注入到字段、方法和构造函数中。

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

    自动布线将失败:

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

    由于Java不保留字节码中的参数名,Spring不再能够区分两个bean。修复方法是使用@Qualifier

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


    是的,您可以配置SpringServlet上下文XML文件来定义bean(即类),以便它可以为您进行自动注入。但是,请注意,您必须执行其他配置才能启动和运行,最好的方法是遵循教程的基础知识。

    可能配置了Spring之后,可以在Spring Servlet上下文XML文件中执行以下操作(例如1)以工作(请将com.movies的包名称替换为真正的包名称,如果这是第三方类,请确保类路径上有适当的JAR文件):

    1
    <beans:bean id="movieFinder" class="com.movies.MovieFinder" />

    或者如果moviefinder类有一个带有基元值的构造函数,那么您可以这样做,

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

    或者如果moviefinder类有一个构造函数需要另一个类,那么您可以这样做,

    1
    2
    3
    <beans:bean id="movieFinder" class="com.movies.MovieFinder">
        <beans:constructor-arg ref="otherBeanRef" />
    </beans:bean>

    …其中"otherBeanRef"是另一个引用了预期类的bean。