Java7新特性(三)DI

本文主要根据《Java程序员修炼之道》整理的代码笔记片段

1.好处

松耦合、易测性、更强的内聚性、可重用组件、更轻盈的代码

2.容器

Guice、Spring、PicoContainer

3.进化

工厂模式、服务定位器模式
public class AgentFinderFactory {

  private static AgentFinderFactory singleton;

  private AgentFinderFactory() {
  }

  public static AgentFinderFactory getInstance() {
    if (singleton == null) {
      singleton = new AgentFinderFactory();
    }
    return singleton;
  }

  public AgentFinder getAgentFinder(String agentType) {
    AgentFinder finder = null;
    switch (agentType) {
    case "spreadsheet":
      finder = new SpreadsheetAgentFinder();
      break;
    case "webservice":
      finder = new WebServiceAgentFinder();
      break;
    default:
      finder = new WebServiceAgentFinder();
      break;
    }
    return finder;
  }
}
DI
public class HollywoodServiceWithDI {

  public static List<Agent> getFriendlyAgents(AgentFinder finder) {
    List<Agent> agents = finder.findAllAgents();
    List<Agent> friendlyAgents = filterAgents(agents, "Java Developers");
    return friendlyAgents;
  }

  public static List<Agent> filterAgents(List<Agent> agents, String agentType) {
    List<Agent> filteredAgents = new ArrayList<>();
    for (Agent agent : agents) {
      if (agent.getType().equals("Java Developers")) {
        filteredAgents.add(agent);
      }
    }
    return filteredAgents;
  }
}
JSR-330
public class HollywoodServiceJSR330 {

  @Inject
  public static List<Agent> getFriendlyAgents(AgentFinder finder) {
    List<Agent> agents = finder.findAllAgents();
    List<Agent> friendlyAgents = filterAgents(agents, "Java Developers");
    return friendlyAgents;
  }

  public static List<Agent> filterAgents(List<Agent> agents, String agentType) {
    List<Agent> filteredAgents = new ArrayList<>();
    for (Agent agent : agents) {
      if (agent.getType().equals("Java Developers")) {
        filteredAgents.add(agent);
      }
    }
    return filteredAgents;
  }
}

4.JSR-330 主角

@Inject 注入
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}
@Qualifier 限定
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Qualifier {}
@Named 
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}
使用:
// @Inject
// @Named("eddie")
Provider接口
public interface Provider<T> {

    /**
     * Provides a fully-constructed and injected instance of {@code T}.
     *
     * @throws RuntimeException if the injector encounters an error while
     *  providing an instance. For example, if an injectable member on
     *  {@code T} throws an exception, the injector may wrap the exception
     *  and throw it to the caller of {@code get()}. Callers should not try
     *  to handle such exceptions as the behavior may vary across injector
     *  implementations and even different configurations of the same injector.
     */
    T get();
}
使用(谷歌Guice框架实例  延迟加载 条件加载
import com.google.inject.Inject;
import com.google.inject.Provider;

/**
 * Code for listing 3_6
 */
class MurmurMessage {
  boolean someGlobalCondition = true;

  @Inject
  MurmurMessage(Provider<Message> messageProvider) {
    Message msg1 = messageProvider.get();
    if (someGlobalCondition) {
      Message copyOfMsg1 = messageProvider.get();
    }
    // Do stuff with msg1 and copyOfMsg1
  }

  private class Message {
  }
}
@Scope
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Scope {}
@Singleton
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

5.参考实现

谷歌Guice框架  最新是guice-4.0-beta.zip
Maven 依赖  
    <dependency>
      <groupId>com.google.inject</groupId>
      <artifactId>guice</artifactId>
      <version>3.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
      <scope>compile</scope>
    </dependency>

Getting Started

With dependency injection, objects accept dependencies in their constructors. To construct an object, you first build its dependencies. But to build each dependency, you need its dependencies, and so on. So when you build an object, you really need to build an object graph.

Building object graphs by hand is labour intensive, error prone, and makes testing difficult. Instead, Guice can build the object graph for you. But first, Guice needs to be configured to build the graph exactly as you want it.

To illustrate, we'll start the RealBillingService class that accepts its dependent interfaces CreditCardProcessor and TransactionLog in its constructor. To make it explicit that the RealBillingService constructor is invoked by Guice, we add the @Inject annotation:

class RealBillingService implements BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;

  @Inject
  RealBillingService(CreditCardProcessor processor, 
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }

  @Override
  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    ...
  }
}

We want to build a RealBillingService using PaypalCreditCardProcessor and DatabaseTransactionLog. Guice uses bindings to map types to their implementations. A module is a collection of bindings specified using fluent, English-like method calls:

public class BillingModule extends AbstractModule {
  @Override 
  protected void configure() {

     /*
      * This tells Guice that whenever it sees a dependency on a TransactionLog,
      * it should satisfy the dependency using a DatabaseTransactionLog.
      */
    bind(TransactionLog.class).to(DatabaseTransactionLog.class);

     /*
      * Similarly, this binding tells Guice that when CreditCardProcessor is used in
      * a dependency, that should be satisfied with a PaypalCreditCardProcessor.
      */
    bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
  }
}

The modules are the building blocks of an injector, which is Guice's object-graph builder. First we create the injector, and then we can use that to build the RealBillingService:

 public static void main(String[] args) {
    /*
     * Guice.createInjector() takes your Modules, and returns a new Injector
     * instance. Most applications will call this method exactly once, in their
     * main() method.
     */
    Injector injector = Guice.createInjector(new BillingModule());

    /*
     * Now that we've got the injector, we can build objects.
     */
    RealBillingService billingService = injector.getInstance(RealBillingService.class);
    ...
  }

By building the billingService, we've constructed a small object graph using Guice. The graph contains the billing service and its dependent credit card processor and transaction log.


你可能感兴趣的:(DI,java7,Guice,JSR-330)