控制反转 的种类


之前对控制反转的定义和解释都不是很清晰。最近翻书发现在《Pro Spring 5》(免费电子版在文章最后)有一段非常不错的解释。记录一下,有道翻译贴出来方便查看。如有请直接跳过中文,看后面的原文。


控制反转的类型

控制反转的类型您可能想知道为什么有两种类型的IoC,以及为什么这些类型被进一步划分为不同的实现。这个问题似乎没有明确的答案;当然,不同的类型提供了一定程度的灵活性,但对我们来说,IoC更像是新旧思想的混合体。IoC的两种类型代表了这一点。依赖查找是一种更为传统的方法,乍一看,Java程序员似乎更熟悉它。依赖项注入虽然一开始看起来与直觉相反,但实际上比依赖项查找更灵活、更有用。使用依赖查找样式的IoC,组件必须获得对依赖项的引用,而使用依赖项注入时,依赖项由IoC容器注入到组件中。依赖项查找有两种类型:依赖项拉取和上下文化依赖项查找(CDL)。依赖项注入还有两种常见的特性:构造函数和setter依赖项注入。

依赖项拉取(Dependency Pull)

对于Java开发人员来说,依赖项拉取是最常见的IoC类型。在依赖项拉取中,根据需要从注册表中提取依赖项。任何编写过访问EJB(2.1或以前的版本)的代码的人都使用了依赖项拉取(即通过JNDI API查找EJB组件)。图3-1显示了通过查找机制提取依赖项的场景。

图3 - 1。通过JNDI查找进行依赖。

Spring还提供了依赖查找机制,用于检索框架管理的组件;你在第二章看到了这一点。下面的代码示例展示了基于spring的应用程序中典型的依赖查找:

package com.apress.prospring5.ch3;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DependencyPull {
    public static void main(String... args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/app-context.xml");
        MessageRenderer mr = ctx.getBean("renderer", MessageRenderer.class);
        mr.render();
    }
}

这种IoC不仅流行于基于jee的应用程序(使用EJB 2.1或以前的版本),它广泛使用JNDI查找来从注册中心获得依赖关系,而且在许多环境中与Spring一起工作也很关键。

上下文化依赖项查找(Contextualized Dependency Lookup)

上下文化依赖项查找(CDL)在某些方面与依赖项拉取相似,但是在CDL中,查找是针对管理资源的容器执行的,而不是来自某个中心注册中心,并且通常在某个设置点执行。图3-2显示了CDL机制。
图3 - 2。上下文化依赖项查找
CDL的工作方式是让组件实现一个类似于以下代码片段中的接口:
package com.apress.prospring5.ch3;

public interface ManagedComponent {
    void performLookup(Container container);
}

通过实现这个接口,一个组件向容器发出信号,它希望获得一个依赖项。容器通常由底层应用程序服务器或框架(例如,Tomcat或JBoss)或框架(例如Spring)提供。下面的代码片段显示了一个简单的容器接口,该接口提供一个依赖项查找服务:

package com.apress.prospring5.ch3;

public interface Container {
    Object getDependency(String key);
}

当容器准备将依赖项传递给组件时,它将依次调用每个组件上的performLookup()。然后,该组件可以使用容器接口查找其依赖项,如下面的代码片段所示:

package com.apress.prospring5.ch3;

public class ContextualizedDependencyLookup implements ManagedComponent {
    private Dependency dependency;

    @Override
    public void performLookup(Container container) {
        this.dependency = (Dependency) container.getDependency("myDependency");
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

构造函数依赖注入(Constructor Dependency Injection)

当组件的依赖项在其构造函数(或构造函数)中提供给它时,就会发生构造函数依赖项注入。组件声明一个构造函数或一组构造函数,将其依赖项作为参数,IoC容器在实例化发生时将依赖项传递给组件,如下面的代码片段所示:
package com.apress.prospring5.ch3;

public class ConstructorInjection {
    private Dependency dependency;

    public ConstructorInjection(Dependency dependency) {
        this.dependency = dependency;
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

使用构造函数注入的一个明显后果是,如果没有对象的依赖关系,就不能创建对象;因此,他们是强制性的。

Setter依赖注入(Setter Dependency Injection)

在setter依赖项注入中,IoC容器通过javabean样式的setter方法注入组件的依赖项。 组件的setter公开了IoC容器可以管理的依赖项。 下面的代码示例显示了典型的基于setter依赖项注入的组件:
package com.apress.prospring5.ch3;

public class SetterInjection {
    private Dependency dependency;

    public void setDependency(Dependency dependency) {
        this.dependency = dependency;
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

使用setter注入的一个明显后果是可以在没有依赖项的情况下创建对象,并且可以稍后通过调用setter来提供这些对象。

在容器中,setDependency()方法公开的依赖项需求由javabeans风格的名称dependency引用。在实践中,setter注入是最广泛使用的注入机制,它是实现的最简单的IoC机制之一。

  • 注入与查找(Injection vs. Lookup)

    选择使用注入或查找的IoC类型通常不是一个困难的决定。 在许多情况下,您使用的容器的类型是由您使用的容器强制执行的。 例如,如果您使用的是EJB 2.1或以前的版本,您必须使用查找样式的IoC(通过JNDI)从JEE容器中获得EJB。 在Spring中,除了初始bean查找之外,您的组件和它们的依赖项总是使用注入式的IoC连接在一起。
  • 真正的问题是:如果可以选择,应该使用哪种方法,注入还是查找? 答案肯定是注射。 如果查看前面代码示例中的代码,您可以清楚地看到,使用注入对组件的代码没有任何影响。 另一方面,依赖项拉代码必须积极地获取对注册表的引用,并与它交互以获得依赖项,并且使用CDL需要您的类实现一个特定的接口,并手动查找所有依赖项。 当您使用注入时,您的类最多只能使用构造函数或setter来注入依赖项。
  • 使用注入,您可以自由地使用完全与IoC容器分离的类,IoC容器手工向协作者提供依赖对象,而对于查找,类总是依赖于容器定义的类和接口。 查找的另一个缺点是很难在与容器隔离的情况下测试类。 使用注入,测试组件是非常简单的,因为您可以通过使用适当的构造函数或setter自己提供依赖项。
  • 基于查找的解决方案必然比基于注入的解决方案更复杂。 尽管复杂性并不是什么可怕的,但我们对将不必要的复杂性添加到与依赖关系管理一样对应用程序至关重要的流程的有效性提出了质疑。
  • 抛开所有这些原因,选择注入而不是查找的最大原因是它使您的生活更容易。 当您使用注入时,您编写的代码要少得多,并且您所编写的代码很简单,而且通常可以通过一个好的IDE实现自动化。 您将注意到注入示例中的所有代码都是被动的,因为它不会主动尝试完成任务。 您在注入代码中看到的最令人兴奋的事情是对象仅存储在字段中; 没有其他代码涉及从任何注册表或容器中提取依赖项。 因此,代码更简单,更不容易出错。 被动代码的维护比主动代码简单得多,因为很少会出错。 考虑以下来自CDL示例的代码:
  • public void performLookup(Container container) {
        this.dependency = (Dependency) container.getDependency("myDependency");
    }
    在这段代码中,很多地方都可能出错:依赖项键可能会改变,容器实例可能为null,或者返回的依赖项可能是不正确的类型。我们把这段代码称作有很多移动部件,因为很多东西都可能损坏。使用依赖项查找可能会分离应用程序的组件,但它增加了将这些组件重新连接在一起以执行任何有用任务所需的额外代码的复杂性。
  • Setter注入与构造函数注入(Setter Injection vs. Constructor Injection)

    现在我们已经确定了IoC的哪种方法更可取,您仍然需要选择是否使用setter注入或构造函数注入。当您在使用组件之前必须拥有依赖类的实例时,构造函数注入特别有用。包括Spring在内的许多容器提供了一种机制,用于确保在使用setter注入时定义所有依赖项,但是通过使用构造函数注入,您可以以容器不可知的方式断言依赖项的需求。构造函数注入还有助于实现不可变对象的使用。
  • Setter注入在许多情况下都很有用。如果组件将其依赖项公开给容器,但乐于提供自己的默认值,则setter注入通常是实现这一点的最佳方式。setter注入的另一个好处是,它允许在接口上声明依赖关系,尽管这并不像您可能首先想到的那样有用。考虑使用一个业务方法defineMeaningOfLife()的典型业务接口。如果除了这个方法之外,您还为注入定义了一个setter,比如setEncylopedia(),那么您就要求所有实现都必须使用,或者至少要知道全部的依赖关系。但是,您不需要在业务接口中定义setEncylopedia()。相反,您可以在实现业务接口的类中定义方法。通过这种方式编程,所有近代的IoC容器(包括Spring)都可以在业务接口方面与组件一起工作,但仍然提供实现类的依赖关系。这方面的一个例子可以稍微澄清这个问题。考虑下面代码片段中的业务接口:
package com.apress.prospring5.ch3;

public interface Oracle {
    String defineMeaningOfLife();
}

注意,业务接口没有为依赖项注入定义任何setter。这个接口可以实现如下代码片段所示:

package com.apress.prospring5.ch3;

public class BookwormOracle implements Oracle {
    private Encyclopedia encyclopedia;

    public void setEncyclopedia(Encyclopedia encyclopedia) {
        this.encyclopedia = encyclopedia;
    }
    @Override
    public String defineMeaningOfLife() {
        return "Encyclopedias are a waste of money - go see the world instead";
    }
}

如您所见,BookwormOracle类不仅实现了Oracle接口,而且还定义了依赖注入的setter。在处理这样的结构时,Spring是非常舒适的。完全不需要定义业务接口上的依赖项。使用接口来定义依赖关系的能力是setter注入的一个经常被吹捧的优点,但是实际上,您应该努力使setter只用于接口之外的注入。除非您绝对确定特定业务接口的所有实现都需要特定的依赖项,否则请让每个实现类定义自己的依赖项并为业务方法保留业务接口。

尽管您不应该总是在业务接口中设置依赖项,但在业务接口中为配置参数设置setter和getter是一个好主意,并使setter注入成为一个有价值的工具。我们认为配置参数是依赖项的特殊情况。当然,您的组件依赖于配置数据,但是配置数据与您到目前为止看到的依赖类型有很大的不同。稍后我们将讨论这些差异,但是现在,请考虑如下代码片段所示的业务接口:

package com.apress.prospring5.ch3;

public interface NewsletterSender {
    void setSmtpServer(String smtpServer);
    String getSmtpServer();
    void setFromAddress(String fromAddress);
    String getFromAddress();
    void send();
}

通过电子邮件发送一组时事通讯的类实现了时事通讯发送器接口。send()方法是唯一的业务方法,但是注意到我们在接口上定义了两个JavaBean属性。当我们刚刚说不应该在业务接口中定义依赖关系时,我们为什么要这么做呢?原因是这些值,SMTP服务器地址和电子邮件发送的地址,在实际意义上不是依赖;相反,这些配置细节会影响到NewsletterSender 接口的所有实现功能。这里的问题是:配置参数和任何其他类型的依赖之间的区别是什么?在大多数情况下,您可以清楚地看到一个依赖项是否应该被归类为配置参数,但是如果您不确定,请查看以下三个指向配置参数的特征:

  • 配置参数是被动的。在前面代码片段中描述的NewsletterSender示例中,SMTP服务器参数是一个被动依赖的示例。被动依赖关系不直接用于执行操作;相反,它们是在内部使用或由另一个依赖项来执行它们的操作。在第2章的MessageRenderer示例中,MessageProvider依赖项不是被动的;它执行了MessageRenderer完成其任务所需的函数。

  • 配置参数通常是信息,而不是其他组件。我们的意思是,配置参数通常是组件完成工作所需的一些信息。显然,SMTP服务器是时事通讯发送者所需的一段信息,但是MessageProvider实际上是MessageRenderer需要正确运行的另一个组件。

  • 配置参数通常是简单值或简单值的集合。这实际上是前两点的副产品,但是配置参数通常是简单的值。在Java中,这意味着它们是一个原语(或相应的包装类)或这些值的字符串或集合。简单的值通常是被动的。这意味着,除了操作字符串所代表的数据之外,您不能对字符串做太多事情;您几乎总是将这些值用于信息目的,例如,表示网络套接字应该监听的端口号的int值或表示电子邮件程序应该通过其发送消息的SMTP服务器的字符串。

在考虑是否在业务接口中定义配置选项时,还要考虑配置参数是否适用于业务接口的所有实现或仅适用于一个实现。例如,对于时事通讯发送器的实现,显然所有实现都需要知道发送电子邮件时使用哪个SMTP服务器。但是,我们可能会选择保留配置选项,标记是否从业务接口发送安全的电子邮件,因为并不是所有的电子邮件api都能做到这一点,并且假定许多实现根本不考虑安全性是正确的。

Setter注入还允许您动态地为不同的实现交换依赖项,而无需创建父组件的新实例。Spring的JMX支持使这成为可能。setter注入的最大好处可能是它对注入机制的干扰最小。

Setter注入还允许您动态地为不同的实现交换依赖项,而无需创建父组件的新实例。通常,您应该根据您的用例选择注入类型。基于setterbased注入允许在不创建新对象的情况下交换依赖项,还允许类选择适当的默认值,而不需要显式地注入对象。构造函数注入是一个很好的选择,当您希望确保依赖项被传递给组件时,当您设计不可变对象时。请记住,虽然构造函数注入确保向组件提供所有依赖项,但是大多数容器也提供了一种机制来确保这一点,但是可能会导致将代码与框架耦合的成本。Spring的JMX支持使这成为可能。setter注入的最大好处可能是它对注入机制的干扰最小。


原文:

《Pro Spring 5》

CHAPTER 3

Types of Inversion of Control

You may be wondering why there are two types of IoC and why these types are split further into different
implementations. There seems to be no clear answer to this question; certainly the different types provide a
level of flexibility, but to us, it seems that IoC is more of a mixture of old and new ideas. The two types of IoC
represent this. Dependency lookup is a much more traditional approach, and at first glance, it seems more
familiar to Java programmers. Dependency injection, although it appears counterintuitive at first, is actually
much more flexible and usable than dependency lookup. With dependency lookup–style IoC, a component
must acquire a reference to a dependency, whereas with dependency injection, the dependencies are
injected into the component by the IoC container. Dependency lookup comes in two types: dependency
pull and contextualized dependency lookup (CDL). Dependency injection also has two common flavors:
constructor and setter dependency injection.

Dependency Pull

To a Java developer, dependency pull is the most familiar types of IoC. In dependency pull, dependencies
are pulled from a registry as required. Anyone who has ever written code to access an EJB (2.1 or prior
versions) has used dependency pull (that is, via the JNDI API to look up an EJB component). Figure 3-1
shows the scenario of dependency pull via the lookup mechanism.

Figure 3-1. Dependency pull via JNDI lookup

Spring also offers dependency pull as a mechanism for retrieving the components that the framework
manages; you saw this in action in Chapter 2. The following code sample shows a typical dependency pull
lookup in a Spring-based application:

package com.apress.prospring5.ch3;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DependencyPull {
    public static void main(String... args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/app-context.xml");
        MessageRenderer mr = ctx.getBean("renderer", MessageRenderer.class);
        mr.render();
    }
}

This kind of IoC is not only prevalent in JEE-based applications (using EJB 2.1 or prior versions), which
make extensive use of JNDI lookups to obtain dependencies from a registry, but also pivotal to working with
Spring in many environments.

Contextualized Dependency Lookup

Contextualized dependency lookup (CDL) is similar, in some respects, to dependency pull, but in CDL,
lookup is performed against the container that is managing the resource, not from some central registry, and
it is usually performed at some set point. Figure 3-2 shows the CDL mechanism.

Figure 3-2. Contextualized dependency lookup

CDL works by having the component implement an interface similar to that in the following code snippet:

package com.apress.prospring5.ch3;

public interface ManagedComponent {
    void performLookup(Container container);
}

By implementing this interface, a component is signaling to the container that it wants to obtain a
dependency. The container is usually provided by the underlying application server or framework (for
example, Tomcat or JBoss) or framework (for example, Spring). The following code snippet shows a simple
Container interface that provides a dependency lookup service:

package com.apress.prospring5.ch3;

public interface Container {
    Object getDependency(String key);
}

When the container is ready to pass dependencies to a component, it calls performLookup() on each
component in turn. The component can then look up its dependencies by using the Container interface, as
shown in the following code snippet:

package com.apress.prospring5.ch3;

public class ContextualizedDependencyLookup implements ManagedComponent {
    private Dependency dependency;

    @Override
    public void performLookup(Container container) {
        this.dependency = (Dependency) container.getDependency("myDependency");
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

Constructor Dependency Injection

Constructor dependency injection occurs when a component’s dependencies are provided to it in its
constructor (or constructors). The component declares a constructor or a set of constructors, taking as
arguments its dependencies, and the IoC container passes the dependencies to the component when
instantiation occurs, as shown in the following code snippet:

package com.apress.prospring5.ch3;

public class ConstructorInjection {
    private Dependency dependency;

    public ConstructorInjection(Dependency dependency) {
        this.dependency = dependency;
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

An obvious consequence of using constructor injection is that an object cannot be created without its
dependencies; thus, they are mandatory.

Setter Dependency Injection

In setter dependency injection, the IoC container injects a component’s dependencies via JavaBean-style
setter methods. A component’s setters expose the dependencies the IoC container can manage. The
following code sample shows a typical setter dependency injection–based component:

package com.apress.prospring5.ch3;

public class SetterInjection {
    private Dependency dependency;

    public void setDependency(Dependency dependency) {
        this.dependency = dependency;
    }

    @Override
    public String toString() {
        return dependency.toString();
    }
}

An obvious consequence of using setter injection is that an object can be created without its
dependencies, and they can be provided later by calling the setter.

Within the container, the dependency requirement exposed by the setDependency() method is referred
to by the JavaBeans-style name, dependency. In practice, setter injection is the most widely used injection
mechanism, and it is one of the simplest IoC mechanisms to implement.

Injection vs. Lookup

Choosing which style of IoC to use—injection or lookup—is not usually a difficult decision. In many cases,
the type of IoC you use is mandated by the container you are using. For instance, if you are using EJB 2.1 or
prior versions, you must use lookup-style IoC (via JNDI) to obtain an EJB from the JEE container. In Spring,
aside from initial bean lookups, your components and their dependencies are always wired together using
injection-style IoC.

The real question is this: given the choice, which method should you use, injection or lookup? The
answer is most definitely injection. If you look at the code in the previous code samples, you can clearly see
that using injection has zero impact on your components’ code. The dependency pull code, on the other
hand, must actively obtain a reference to the registry and interact with it to obtain the dependencies, and
using CDL requires your classes to implement a specific interface and look up all dependencies manually.
When you are using injection, the most your classes have to do is allow dependencies to be injected by using
either constructors or setters.

Using injection, you are free to use your classes completely decoupled from the IoC container that
is supplying dependent objects with their collaborators manually, whereas with lookup, your classes are
always dependent on the classes and interfaces defined by the container. Another drawback with lookup
is that it becomes difficult to test your classes in isolation from the container. Using injection, testing your
components is trivial because you can simply provide the dependencies yourself by using the appropriate
constructor or setter.

Lookup-based solutions are, by necessity, more complex than injection-based ones. Although
complexity is nothing to be afraid of, we question the validity of adding unneeded complexity to a process as
central to your application as dependency management.

All of these reasons aside, the biggest reason to choose injection over lookup is that it makes your life
easier. You write substantially less code when you are using injection, and the code that you do write is
simple and can, in general, be automated by a good IDE. You will notice that all the code in the injection
samples is passive, in that it doesn’t actively try to accomplish a task. The most exciting thing you see in
injection code is that objects get stored in a field only; no other code is involved in pulling the dependency
from any registry or container. Therefore, the code is much simpler and less error prone. Passive code is
much simpler to maintain than active code because there is very little that can go wrong. Consider the
following code taken from the CDL example:

public void performLookup(Container container) {
    this.dependency = (Dependency) container.getDependency("myDependency");
}

In this code, plenty could go wrong: the dependency key could change, the container instance could be
null, or the returned dependency might be the incorrect type. We refer to this code as having a lot of moving
parts, because plenty of things can break. Using dependency lookup might decouple the components of
your application, but it adds complexity in the additional code required to couple these components back
together in order to perform any useful tasks.

Setter Injection vs. Constructor Injection

Now that we have established which method of IoC is preferable, you still need to choose whether to use
setter injection or constructor injection. Constructor injection is particularly useful when you absolutely
must have an instance of the dependency class before your component is used. Many containers, Spring
included, provide a mechanism for ensuring that all dependencies are defined when you use setter
injection, but by using constructor injection, you assert the requirement for the dependency in a containeragnostic
manner. Constructor injection also helps achieve the use of immutable objects.

Setter injection is useful in a variety of cases. If the component is exposing its dependencies to the
container but is happy to provide its own defaults, setter injection is usually the best way to accomplish
this. Another benefit of setter injection is that it allows dependencies to be declared on an interface,although this is not as useful as you might first think. Consider a typical business interface with one business
method, defineMeaningOfLife(). If, in addition to this method, you define a setter for injection such
as setEncylopedia(), you are mandating that all implementations must use or at least be aware of the
encyclopedia dependency. However, you don’t need to define setEncylopedia() in the business interface.
Instead, you can define the method in the classes implementing the business interface. While programming
in this way, all recent IoC containers, Spring included, can work with the component in terms of the business
interface but still provide the dependencies of the implementing class. An example of this may clarify this
matter slightly. Consider the business interface in the following code snippet:

package com.apress.prospring5.ch3;

public interface Oracle {
    String defineMeaningOfLife();
}

Notice that the business interface does not define any setters for dependency injection. This interface
could be implemented as shown in the following code snippet:

package com.apress.prospring5.ch3;

public class BookwormOracle implements Oracle {
    private Encyclopedia encyclopedia;

    public void setEncyclopedia(Encyclopedia encyclopedia) {
        this.encyclopedia = encyclopedia;
    }

    @Override
    public String defineMeaningOfLife() {
        return "Encyclopedias are a waste of money - go see the world instead";
    }
}

As you can see, the BookwormOracle class not only implements the Oracle interface but also defines
the setter for dependency injection. Spring is more than comfortable dealing with a structure like this.
There is absolutely no need to define the dependencies on the business interface. The ability to use
interfaces to define dependencies is an often-touted benefit of setter injection, but in actuality, you
should strive to keep setters used solely for injection out of your interfaces. Unless you are absolutely
sure that all implementations of a particular business interface require a particular dependency, let each
implementation class define its own dependencies and keep the business interface for business methods.

Although you shouldn’t always place setters for dependencies in a business interface, placing setters
and getters for configuration parameters in the business interface is a good idea and makes setter injection
a valuable tool. We consider configuration parameters to be a special case for dependencies. Certainly your
components depend on the configuration data, but configuration data is significantly different from the
types of dependency you have seen so far. We will discuss the differences shortly, but for now, consider the
business interface shown in the following code snippet:

package com.apress.prospring5.ch3;
public interface NewsletterSender {
    void setSmtpServer(String smtpServer);
    String getSmtpServer();
    void setFromAddress(String fromAddress);
    String getFromAddress();
    void send();
}

Classes that send a set of newsletters via e-mail implement the NewsletterSender interface. The
send() method is the only business method, but notice that we have defined two JavaBean properties on the
interface. Why are we doing this when we just said that you shouldn’t define dependencies in the business
interface? The reason is that these values, the SMTP server address and the address the e-mails are sent
from, are not dependencies in the practical sense; rather, they are configuration details that affect how all
implementations of the NewsletterSender interface function. The question here then is this: what is the
difference between a configuration parameter and any other kind of dependency? In most cases, you can
clearly see whether a dependency should be classified as a configuration parameter, but if you are not sure,
look for the following three characteristics that point to a configuration parameter:

  • Configuration parameters are passive. In the NewsletterSender example depicted in the previous code snippet, the SMTP server parameter is an example of a passive dependency. Passive dependencies are not used directly to perform an action; instead, they are used internally or by another dependency to perform their actions. In the MessageRenderer example from Chapter 2, the MessageProvider dependency was not passive; it performed a function that was necessary for the MessageRenderer to complete its task.

  • Configuration parameters are usually information, not other components. By this we mean that a configuration parameter is usually some piece of information that a component needs to complete its work. Clearly, the SMTP server is a piece of information required by the NewsletterSender, but the MessageProvider is really another component that the MessageRenderer needs to function correctly.

  • Configuration parameters are usually simple values or collections of simple values. This is really a by-product of the previous two points, but configuration parameters are usually simple values. In Java this means they are a primitive (or the corresponding wrapper class) or a String or collections of these values. Simple values are generally passive. This means you can’t do much with a String other than manipulate the data it represents; and you almost always use these values for information purposes, for example, an int value that represents the port number that a network socket should listen on or a String that represents the SMTP server through which an e-mail program should send messages.

When considering whether to define configuration options in the business interface, also consider
whether the configuration parameter is applicable to all implementations of the business interface
or just one. For instance, in the case of implementations of NewsletterSender, it is obvious that all
implementations need to know which SMTP server to use when sending e-mails. However, we would
probably choose to leave the configuration option that flags whether to send secure e-mail off the
business interface because not all e-mail APIs are capable of this, and it is correct to assume that many
implementations will not take security into consideration at all.

Setter injection also allows you to swap dependencies for a different implementation on the fly without
creating a new instance of the parent component. Spring’s JMX support makes this possible. Perhaps the
biggest benefit of setter injection is that it is the least intrusive of the injection mechanisms.

In general, you should choose an injection type based on your use case. Setter-based injection allows
dependencies to be swapped out without creating new objects and also lets your class choose appropriate
defaults without the need to explicitly inject an object. Constructor injection is a good choice when you
want to ensure that dependencies are being passed to a component and when designing for immutable
objects. Do keep in mind that while constructor injection ensures that all dependencies are provided to a
component, most containers provide a mechanism to ensure this as well but may incur a cost of coupling
your code to the framework.


这本的的资源 https://pan.baidu.com/s/1R4r587qTRn1mrRfT1zIUYQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值