Spring之依赖

来源:互联网 发布:网络骚扰电话打不停 编辑:程序博客网 时间:2024/06/03 14:49

一个典型的企业应用不是由一个单一对象组成(在Spring语法中称为bean)。甚至是最简单的应用程序也有几个对象一起工作来展现出作为一个连贯应用程序终端用户所见的。

This next section explains how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal


5.4.1 依赖注入


依赖注入是一个方法,靠它对象定义了它们的依赖。即,它们一起工作的对象,仅通过构造器参数,工厂方法的参数,或者属性,在被构造或者从工厂方法返回的的对象实例上设置。容器稍后在创建这个bean时容器注入那些依赖。这个过程根本上是反转的,因此命名为IoC,bean自身控制了实例化或者其自己的依赖的部署信息,这可以通过使用类直接构造器,或者Server Locator 模式。


使用依赖注入代码会更加清晰,并且当使用其依赖提供对象时,去耦也会更加有效。对象不会查找其依赖,并且不知道依赖的类或者部署信息。同样地,你的类更加容易测试,尤其是当依赖是在接口或者抽象基类时,允许存根或者模拟实现用于单元测试。


依赖注入有两个变异,基于构造器的依赖注入和基于setter的依赖注入。


基于构造器的依赖注入


基于构造器的依赖注入通过调用带有大量参数的构造器完成,每个参数代表一个依赖。调用带有指定参数的static工厂方法构造bean几乎等价。关于构造器参数和static工厂方法的参数的讨论是相似的。下面的例子显示了一个类只可以通过构造器注入实现依赖注入。注意关于这个类没有什么特别的,其是一个POJO,即在容器指定接口,基类或者注解上没有依赖。


public class SimpleMovieLister {    // the SimpleMovieLister has a dependency on a MovieFinder    private MovieFinder movieFinder;    // a constructor so that the Spring container can inject a MovieFinder    public SimpleMovieLister(MovieFinder movieFinder) {        this.movieFinder = movieFinder;    }    // business logic that actually uses the injected MovieFinder is omitted...}


构造器参数分辨

构造器参数分辨使用参数类型匹配。如果在一个bean定义的构造器参数内没有潜在的歧义,在bean定义中一个构造器参数定义的顺序就是那些参数提供给合适构造器的顺序,当bean被实例化时。考虑下面的类:


package x.y;public class Foo {    public Foo(Bar bar, Baz baz) {        // ...    }}

没有潜在歧义存在,假设Bar和Baz类没有继承上的关联。那样的话 下面配置就工作的很好,并且你不需要指定参数类型下标并且/或者在<constructor-arg/>元素中明确指定类型。


<beans>    <bean id="foo" class="x.y.Foo">        <constructor-arg ref="bar"/>        <constructor-arg ref="baz"/>    </bean>    <bean id="bar" class="x.y.Bar"/>    <bean id="baz" class="x.y.Baz"/></beans>

当引用另一个bean时,类型已知,并且可以匹配(如同上面的例子)。当使用一个简单类型时,比如<value>true</value>,Spring不能决定值的类型,并且没有帮助的话不能匹配类型的。考虑下面的类:


package examples;public class ExampleBean {    // Number of years to calculate the Ultimate Answer    private int years;    // The Answer to Life, the Universe, and Everything    private String ultimateAnswer;    public ExampleBean(int years, String ultimateAnswer) {        this.years = years;        this.ultimateAnswer = ultimateAnswer;    }}

在前面的案例中,容器可以使用类型匹配简单类型,如果你使用type属性明确指定构造器参数类型。例如:


<bean id="exampleBean" class="examples.ExampleBean">    <constructor-arg type="int" value="7500000"/>    <constructor-arg type="java.lang.String" value="42"/></bean>

使用index属性明确指定构造器参数索引。例如:


<bean id="exampleBean" class="examples.ExampleBean">    <constructor-arg index="0" value="7500000"/>    <constructor-arg index="1" value="42"/></bean>


为了解决多个简单值的歧义,即指定一个索引解决歧义构造器内有同一类型的两个参数。注意索引是从0开始。


你也可以使用构造器参数名来消除歧义


<bean id="exampleBean" class="examples.ExampleBean">    <constructor-arg name="years" value="7500000"/>    <constructor-arg name="ultimateAnswer" value="42"/></bean>

记住为了使这个工作,你的代码必须使用debug标识进行编译,这样Spring可以从构造器中查找参数名。如果你使用debug标识(或者不想)不能编译你的代码,你可以使用@ConstructorPropertiesJDK注解来明确命名你的构造器参数。简单的例子如下:


package examples;public class ExampleBean {    // Fields omitted    @ConstructorProperties({"years", "ultimateAnswer"})    public ExampleBean(int years, String ultimateAnswer) {        this.years = years;        this.ultimateAnswer = ultimateAnswer;    }}

基于setter的依赖注入


基于setter的DI是通过容器调用你的beans的setter方法完成的,当然了要在调用一个非参数的构造器或者非参数的static工厂方法实例化你的bean之后。


下面的例子显示了一个类仅仅可以使用setter注入完成依赖注入。这个类是符合约定的Java。它是一个POJO,在容器指定的接口,基类或者注解上没有依赖。


public class SimpleMovieLister {    // the SimpleMovieLister has a dependency on the MovieFinder    private MovieFinder movieFinder;    // a setter method so that the Spring container can inject a MovieFinder    public void setMovieFinder(MovieFinder movieFinder) {        this.movieFinder = movieFinder;    }    // business logic that actually uses the injected MovieFinder is omitted...}

ApplicationContext支持其管理的bean基于构造器和基于setter的依赖注入。也支持基于构造器注入后再基于setter的依赖注入。你在BeanDefinition的表单中配置依赖,你使用它并结合PropertyEditor实例将属性从一种格式转换为另一种。然而,大多数Spring使用者不直接使用这些类来工作,(即。。。编程式的)而是使用XML的bean定义,注解的组件(例如,使用@Component,@Controller注解的类),或者基于Java的@Configuration类的@Bean方法。这些资源之后在内部转换为BeanDefinition的实例,并且用于加载一个完整的Spring IoC容器实例。


基于构造器或者基于setter的依赖注入?


由于你可以混合搭配使用上面的两种依赖注入,这是一种好的规则,对于强制性的依赖使用基于构造器的,对于可选的依赖使用setter方法或者配置方法。注意@Required注解使用在一个setter方法上可以使那个属性成为一个必须的依赖。


Spring团队一般提倡构造器注入,因为它能够实现应用程序组件作为不可变对象并且确保必须的依赖不为null。更多的构造器注入组件也总是返回给客户端(调用)代码,在完全初始化状态下。作为附注,大量的构造器参数是一个坏的编码习惯,意味着这个类可能有很多职责并且应该进行代码重构以更好地达到关注点分离。



Setter注入主要用于可选依赖,可以在class内指定合理的默认的值。否则,not-null查询必须在使用这个依赖的地方执行。setter注入一个好处是setter方法使得类的对象可以延迟重新配置或者重新注入。通过JMX MBeans管理是一个使用setter注入引人注目的案例



针对一个特别的类使用DI方式是很有意义的。有时候,当处理第三方类没有源码的类时,就是你做出选择的时候了。例如,如果第三方类没有暴露任何setter方法,那么结构器注入可能是DI的唯一方式。




0 0
原创粉丝点击