精华内容
下载资源
问答
  • guice

    2019-03-06 11:38:00
    Guice简介 Guice 简介,本文中的内容也是参考该文档完成,如有不一致,以该文为准。 快速上手 作为示例,我们使用 BillingService,它依赖于 CreditCardProcessor 和 TransactionLog 两个接口。接下来我们看看如何...

    Guice简介
    Guice 简介,本文中的内容也是参考该文档完成,如有不一致,以该文为准。

    快速上手
    作为示例,我们使用 BillingService,它依赖于 CreditCardProcessor 和 TransactionLog 两个接口。接下来我们看看如何使用Guice:

    class BillingService {
      private final CreditCardProcessor processor;
      private final TransactionLog transactionLog;
    
      @Inject
      BillingService(CreditCardProcessor processor, 
          TransactionLog transactionLog) {
        this.processor = processor;
        this.transactionLog = transactionLog;
      }
    
      public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
        ...
      }
    }



    我们将会把 PaypalCreditCardProcessor 和 DatabaseTransactionLog 注入BillingService。 
    Guice 使用 bindings 将类型和实现对应起来。module 是特定的 bindings 的集合。

    
    public class BillingModule extends AbstractModule {
    
      @Override 
      protected void configure() {
    
          /**
           *这是告诉Guice,当遇到一个对象依赖于TransactionLog时,
           *将DatabaseTransactionLog注入
           */
          bind(TransactionLog.class).to(DatabaseTransactionLog.class);
    
          /**同上*/
          bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
      }
    }


    现在可以编写main方法了:

     

    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.
         */
        BillingService billingService = injector.getInstance(BillingService.class);
        ...
      }


    大功告成!!!

    Bindings
    injector 的职责是确定系统中需要构造哪些对象,解析依赖,装配对象(将被依赖的对象注入依赖的对象)。那么如何指定依赖的解析方式,答案是使用 bindings 配置你的 injector

    创建bindings
    继承 AbstractModule 重写 configure 方法。在该方法中调用 bind() 便创建了一个binding。完成module之后,调用 Guice.createInjector(),将module作为参数传入,便可获得一个injector对象。

    Linked Bindings
    Linked bindings 将一个实现绑定到一个类型。 
    下面例子将 DatabaseTransactionLog 绑定到接口 TransactionLog



     

    public class BillingModule extends AbstractModule {
      @Override 
      protected void configure() {
        bind(TransactionLog.class).to(DatabaseTransactionLog.class);
      }
    }


    绑定之后,当你调用 injector.getInstance(TransactionLog.class) 或当injector遇到一个对象依赖与 TransactionLog,它便会使用 DatabaseTransactionLog。链接可以建立于接口和其实现类,或者子类和父类之间。Linked bindings 可以链式使用。


     

    public class BillingModule extends AbstractModule {
      @Override 
      protected void configure() {
        bind(TransactionLog.class).to(DatabaseTransactionLog.class);
        bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
      }
    }


    在这种情况下,当一个对象依赖于 TransactionLog,injector将会返回一个 MySqlDatabaseTransactionLog.

    BindingAnnotations
    Binding Annotations
    有时候,你想要给特定的类型绑定多个实现。例如,你想给 CreditCardProcessor同时绑定 PaypalCreditCardProcessor 和 CheckoutCreditCardProcessor 两个实现. Guice 通过binding annotation满足上述需求。注解和类型共同确定了一个唯一的binding。 在这儿,注解和类型构成了Key。

    首先我们定义一个注解:

     

    import com.google.inject.BindingAnnotation;
    import java.lang.annotation.Target;
    import java.lang.annotation.Retention;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    import static java.lang.annotation.ElementType.PARAMETER;
    import static java.lang.annotation.ElementType.FIELD;
    import static java.lang.annotation.ElementType.METHOD;
    
    @BindingAnnotation 
    @Target({ FIELD, PARAMETER, METHOD }) 
    @Retention(RUNTIME)
    public @interface PayPal {}


    然后使用我们定义的注解标示需要注入的类型。

     

    public class RealBillingService implements BillingService {
    
      @Inject
      public RealBillingService(@PayPal CreditCardProcessor processor,
          TransactionLog transactionLog) {
        ...
      }


    最后我们还需要创建相应的binding。


     

    bind(CreditCardProcessor.class)
            .annotatedWith(PayPal.class)
            .to(PayPalCreditCardProcessor.class);


    @Named
    也并不是必须创建自己的注解,Guice提供了一个内建的注解@Named。用法如下:
     

    public class RealBillingService implements BillingService {
    
      @Inject
      public RealBillingService(@Named("Checkout") CreditCardProcessor processor,
          TransactionLog transactionLog) {
        ...
      }
    
    bind(CreditCardProcessor.class)
            .annotatedWith(Names.named("Checkout"))
            .to(CheckoutCreditCardProcessor.class);


    因为编译器不能对String类型进行检查,所以不建议使用@Named
     

    Instance Bindings
    你可以将一个特定类型的实例绑定到该类型。
    
    bind(String.class)
            .annotatedWith(Names.named("JDBC URL"))
            .toInstance("jdbc:mysql://localhost/pizza");
    bind(Integer.class)
            .annotatedWith(Names.named("login timeout seconds"))
            .toInstance(10);


    尽量避免给创建起来比较复杂的对象使用 .toInstance 方法,那样会导致应用启动比较慢。可以使用 @Provides 代替该方法。

    Provides Methods
    当你需要编写创建对象的代码,使用 @Provides 方法。该方法只能定义在module中。并且需要使用 @Provides 修饰。他的返回值是一个对象。当Injector需要某个类型的实例时,便会调用相应的@Provides 方法。
     

    public class BillingModule extends AbstractModule {
      @Override
      protected void configure() {
        ...
      }
    
      @Provides
      TransactionLog provideTransactionLog() {
        DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();
        transactionLog.setJdbcUrl("jdbc:mysql://localhost/pizza");
        transactionLog.setThreadPoolSize(30);
        return transactionLog;
      }
    }


    如果 @Provides 方法有binding annotation ,比如@Paypal 或者 @Named("Checkout"),Guice 也是可以的。所有的被依赖对象以参数形式传入该方法即可。


     

    @Provides @PayPal
      CreditCardProcessor providePayPalCreditCardProcessor(
          @Named("PayPal API key") String apiKey) {
        PayPalCreditCardProcessor processor = new PayPalCreditCardProcessor();
        processor.setApiKey(apiKey);
        return processor;
      }


    需要注意的是, Guice不允许 @Provides 方法抛出异常。

    Provider Bindings
    当 @Provides 方法比较复杂时,你也许会考虑将该方法转移到一个单独的类中。Provider类继承Guice的 Provider 接口。 Provider 接口定义如下:


     

    public interface Provider<T> {
      T get();
    }


    我们的Provider实现类有自己的依赖,所有的依赖是通过被@Inject 修饰的构造函数接收的。
     

    public class DatabaseTransactionLogProvider implements Provider<TransactionLog> {
      private final Connection connection;
    
      @Inject
      public DatabaseTransactionLogProvider(Connection connection) {
        this.connection = connection;
      }
    
      public TransactionLog get() {
        DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();
        transactionLog.setConnection(connection);
        return transactionLog;
      }
    }


    绑定


     

    public class BillingModule extends AbstractModule {
      @Override
      protected void configure() {
        bind(TransactionLog.class)
            .toProvider(DatabaseTransactionLogProvider.class);
      }


    Untargeted Bindings
    一些情况下,你需要创建bindings,但是却不能指定具体的实现。这个对于被@ImplementedBy 或者 @ProvidedBy 修饰的具体类或者类型特别有用。An untargetted binding informs the injector about a type, so it may prepare dependencies eagerly. Untargetted bindings have no to clause, like so:

    bind(MyConcreteClass.class);
    bind(AnotherConcreteClass.class).in(Singleton.class);

     
    当指定binding annotation时,必须加上绑定的目标。


     

    bind(MyConcreteClass.class)
            .annotatedWith(Names.named("foo"))
            .to(MyConcreteClass.class);
    bind(AnotherConcreteClass.class)
            .annotatedWith(Names.named("foo"))
            .to(AnotherConcreteClass.class)
            .in(Singleton.class);


    Constructor Bindings
    有时候, 我们需要绑定一个类型到任意的的构造函数。以下情况会有这种需求:@Inject 注解无法被应用到目标构造函数。或者该类型是一个第三方类。或者该类型中有多个构造函数参与依赖注入。 
    针对这个,Guice 有 @toConstructor() 类型的绑定方式。

     

    public class BillingModule extends AbstractModule {
      @Override 
      protected void configure() {
        try {
          bind(TransactionLog.class).toConstructor(
              DatabaseTransactionLog.class.getConstructor(DatabaseConnection.class));
        } catch (NoSuchMethodException e) {
          addError(e);
        }
      }
    }


    JustInTimeBindings
    Just-in-time Bindings
    当Injector需要一个类型的实例的时候,它需要一个binding。 如果这个binding在一个module中被创建,那么这个binding是显式binding,此时injector在每次需要该类型实例时,都使用该实例。但是如果Injector需要一个类型的实例,但是这个类型并没有对应的显式binding。此时injector会尝试创建一个Just-in-time binding。也叫JIT binding或者隐式binding。

    合格的构造函数
    Guice会使用具体类型的可注入构造函数创建binding。可注入构造函数需要是非private,无参数的或者该构造函数被 @Inject 修饰。
     

    public class PayPalCreditCardProcessor implements CreditCardProcessor {
      private final String apiKey;
    
      @Inject
      public PayPalCreditCardProcessor(@Named("PayPal API key") String apiKey) {
        this.apiKey = apiKey;
      }


    @ImplementedBy
    告诉injector,该类型的默认实现类。

     

    @ImplementedBy(PayPalCreditCardProcessor.class)
    public interface CreditCardProcessor {
      ChargeResult charge(String amount, CreditCard creditCard)
          throws UnreachableException;
    }


    上述代码和一下代码是等价的:

        bind(CreditCardProcessor.class).to(PayPalCreditCardProcessor.class);
     
    如果某个类型同时使用 bind() 和 @ImplementedBy,bind() 会生效。

    @ProvidedBy
    告诉Injector,产生该类型的Provider类


     

    @ProvidedBy(DatabaseTransactionLogProvider.class)
    public interface TransactionLog {
      void logConnectException(UnreachableException e);
      void logChargeResult(ChargeResult result);
    }


    上述代码等价于:

    bind(TransactionLog.class)
            .toProvider(DatabaseTransactionLogProvider.class);
     
    如果同时使用 @ProvidedBy 和 bind() , bind() 会生效。

    Scopes
    默认情况下,Guice 每次都会返回一个新创建的对象。不过这也是可以配置的,以便于我们重用实例。

    配置Scopes
    Guice 使用注解标示Scope。例如:

    @Singleton
    public class InMemoryTransactionLog implements TransactionLog {
      /* everything here should be threadsafe! */
    }

    @Provides @Singleton
    TransactionLog provideTransactionLog() {
      ...
    }
     
    上例中,@Singleton 标示该类型只能有一个实例。并且是线程安全的。

    Scope也可以通过代码来配置:

    bind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);
     
    如果某个类型已经被注解标注了scope,同时还使用bind() 方法配置了scope,那么后者生效。如果一个类型已经被注解配置了scope而你不想那样,你可以使用 bind() 覆盖。

    预加载的单例
    bind(TransactionLog.class).to(InMemoryTransactionLog.class).asEagerSingleton();
     
    Injections
    构造函数注入
    这种情况下,需要用 @Inject 标注构造函数。构造函数同时需要将所依赖的类型作为其参数。通常情况下,都是将传入的参数复制给类的final成员。

    public class RealBillingService implements BillingService {
      private final CreditCardProcessor processorProvider;
      private final TransactionLog transactionLogProvider;

      @Inject
      public RealBillingService(CreditCardProcessor processorProvider,
          TransactionLog transactionLogProvider) {
        this.processorProvider = processorProvider;
        this.transactionLogProvider = transactionLogProvider;
      }
     
    如果没有 @Inject 标注的构造函数,Guice 会使用共有的无参构造函数(如果存在)。

    方法注入
    这种情况下,需要使用@Inject 标注方法,该方法的参数为需要注入的类型。

    public class PayPalCreditCardProcessor implements CreditCardProcessor {

      private static final String DEFAULT_API_KEY = "development-use-only";

      private String apiKey = DEFAULT_API_KEY;

      @Inject
      public void setApiKey(@Named("PayPal API key") String apiKey) {
        this.apiKey = apiKey;
      }
     
    字段注入
    这种情况下,需要使用 @Inject 标注字段。

    public class DatabaseTransactionLogProvider implements Provider<TransactionLog> {
      @Inject Connection connection;

      public TransactionLog get() {
        return new DatabaseTransactionLog(connection);
      }
    }
     
    可选的注入
    有时候,我们的依赖项不是必须的,如果系统中存在依赖项则注入,如果不存在,也不强制要求注入。这种情况在方法注入和字段注入中都是适用的。 启用可选注入,只需要使用 @Inejct(optional=true) 标注字段或方法即可。

    public class PayPalCreditCardProcessor implements CreditCardProcessor {
      private static final String SANDBOX_API_KEY = "development-use-only";

      private String apiKey = SANDBOX_API_KEY;

      @Inject(optional=true)
      public void setApiKey(@Named("PayPal API key") String apiKey) {
        this.apiKey = apiKey;
      }
     
    不过,如果混用可选注入和Just-in-time bindings,可能会产生奇怪的接口。例如:

    @Inject(optional=true) Date launchDate;
     
    上面代码中的date总是会被成功注入即使没有为他创建对应的显示binding,因为它有无参构造函数,Guice会为他创建Just-in-time bindings。

    On-demand注入
    方法和字段注入可以用来初始化一个现存的实例。我们可以使用Injector.injectMember()API:

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(...);

        CreditCardProcessor creditCardProcessor = new PayPalCreditCardProcessor();
        injector.injectMembers(creditCardProcessor);
     
    AOP
    Guice 支持方法拦截器。这里直接看一个实例: 
    假如我们需要禁止在周末调用特定方法

    为了标注我们在周末禁止调用的方法,我们定义一个注解类型:

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
    @interface NotOnWeekends {}
     
    然后使用该注解标注我们方法

    public class RealBillingService implements BillingService {

      @NotOnWeekends
      public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
        ...
      }
    }
     现在我们定义我们的拦截器,我们的拦截器需要实现org.aopalliance.intercept.MethodInterceptor 接口。

    public class WeekendBlocker implements MethodInterceptor {
      public Object invoke(MethodInvocation invocation) throws Throwable {
        Calendar today = new GregorianCalendar();
        if (today.getDisplayName(DAY_OF_WEEK, LONG, ENGLISH).startsWith("S")) {
          throw new IllegalStateException(
              invocation.getMethod().getName() + " not allowed on weekends!");
        }
        return invocation.proceed();
      }
    }
     
    然后配置我们的拦截器

    public class NotOnWeekendsModule extends AbstractModule {
      protected void configure() {
        /**在这里,我们匹配所有的类,但是只匹配类中有NotOnWeekends的方法*/
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class), 
            new WeekendBlocker());
      }
    }
     
    所有工作就完成了。

    注入拦截器
    如果需要注入拦截器,使用 `requestInjection` API

    public class NotOnWeekendsModule extends AbstractModule {
      protected void configure() {
        WeekendBlocker weekendBlocker = new WeekendBlocker();
        requestInjection(weekendBlocker);
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class), 
           weekendBlocker);
      }
    }
     
    另外一种方式是使用 `Binder.getProvider`,将依赖的内容传入拦截器的构造函数。

    public class NotOnWeekendsModule extends AbstractModule {
      protected void configure() {
        bindInterceptor(any(),
                        annotatedWith(NotOnWeekends.class),
                        new WeekendBlocker(getProvider(Calendar.class)));
      }
    }
     
    END
     

    展开全文
  • Guice

    2019-01-09 16:05:23
    Guice 的工作原理: Guice:整个框架的门面 Injector:一个依赖的管理上下文 Binder:一个接口和实现的绑定 Module:一组 Binder Provider:bean 的提供者 Key:Binder 中对应一个 Provider Scope:Provider 的作用...

    Guice 的工作原理:

    1. Guice:整个框架的门面
    2. Injector:一个依赖的管理上下文
    3. Binder:一个接口和实现的绑定
    4. Module:一组 Binder
    5. Provider:bean 的提供者
    6. Key:Binder 中对应一个 Provider
    7. Scope:Provider 的作用域
    展开全文
  • GUICE

    2015-07-27 09:45:00
    本文通过范例简单地介绍Google Guice的使用,通过下面的范例我们可以知道,Google Guice的使用非常简单。 Google Guice需要使用JDK1.5以上java环境。 下载Google Guice之后,有以下几个文件: Java代码 ...

    http://blog.chinaunix.net/uid-20749563-id-718418.html

    本文通过范例简单地介绍Google Guice的使用,通过下面的范例我们可以知道,Google Guice的使用非常简单。

    Google Guice需要使用JDK1.5以上java环境。

    下载Google Guice之后,有以下几个文件:

    Java代码
    1. aopalliance.jar   
    2. guice-1.0.jar   
    3. guice-servlet-1.0.jar   
    4. guice-spring-1.0.jar   
    5. guice-struts2-plugin-1.0.jar  

    本例只使用到guice-1.0.jar文件,将其加入到class path中。

    下面简单地介绍范例:

    范例1:使用com.google.inject.Module接口实现类
     

     

    文件名
    说明
    HelloGuice.java
    业务逻辑接口定义文件
    HelloGuiceImpl.java
    业务逻辑接口实现文件
    HelloGuiceModule.java
    该文件必须实现com.google.inject.Module接口
    TestGuice.java
    测试文件

     

    HelloGuice.java

    Java代码
    1. package cn.jcourse.guice;   
    2.   
    3. /**  
    4.  * @author zhangtao  
    5.  * HelloGuice接口,用于表达问候  
    6.  */  
    7. public interface HelloGuice {   
    8.     public void sayHello();   
    9. }   

    上面接口中,我们定义了一个方法,sayHello,用于向用户问候。这里我只是做演示用,在实际的业务中,业务逻辑很可能不是这么简单。

    HelloGuiceImpl.java
     

    Java代码
    1. package cn.jcourse.guice.impl;   
    2.   
    3. import cn.jcourse.guice.HelloGuice;   
    4.   
    5. /**  
    6.  * @author zhangtao  
    7.  * HellGuice实现类  
    8.  */  
    9. public class HelloGuiceImpl implements HelloGuice {   
    10.   
    11.     /* (non-Javadoc)  
    12.      * @see cn.jcourse.guice.HelloGuice#sayHello()  
    13.      */  
    14.     public void sayHello() {   
    15.         System.out.println("Hello Guice!");   
    16.     }   
    17. }  

    该类是HelloGuice接口的实现类,这里我们仅仅是向控制台输出了一个Hello Guice!字符串。

    HelloGuiceModule.java
     

    Java代码
    1. package cn.jcourse.guice;   
    2.   
    3. import cn.jcourse.guice.impl.HelloGuiceImpl;   
    4.   
    5. import com.google.inject.Binder;   
    6. import com.google.inject.Module;   
    7. /**  
    8.  * @author zhangtao  
    9.  * HelloGuice模块  
    10.  */  
    11. public class HelloGuiceModule implements Module {   
    12.   
    13.     /*  
    14.      * (non-Javadoc)  
    15.      *   
    16.      * @see com.google.inject.Module#configure(com.google.inject.Binder)  
    17.      */  
    18.     public void configure(Binder binder) {   
    19.         binder.bind(HelloGuice.class).to(HelloGuiceImpl.class);   
    20.     }   
    21. }  

    上面的代码用于告知Guice将接口和实现类绑定。

    TestGuice.java
     

    Java代码
    1. package cn.jcourse.guice.test;   
    2.   
    3. import cn.jcourse.guice.HelloGuice;   
    4. import cn.jcourse.guice.HelloGuiceModule;   
    5.   
    6. import com.google.inject.Guice;   
    7. import com.google.inject.Injector;   
    8.   
    9. import junit.framework.TestCase;   
    10.   
    11. /**  
    12.  * @author zhangtao   
    13.  * 测试Guice  
    14.  */  
    15. public class TestGuice extends TestCase {   
    16.     public void testHelloGuice() {   
    17.         Injector injector = Guice.createInjector(new HelloGuiceModule());   
    18.         HelloGuice helloGuice = injector.getInstance(HelloGuice.class);   
    19.         helloGuice.sayHello();   
    20.     }   
    21. }  

    上面的代码我们使用JUnit来进行单元测试,这里的代码也相对比较简单。

    在编写完上述代码后,我们运行TestGuice类,将会发现它向控制台输出了Hello Guice!。

    范例2:使用Java Annotation

    范例1中,我们自己手工的去配置了绑定关系,当然我们也可以不用那么做。我们可以直接为HelloGuice加上@ImplementedBy注释,而省略掉对com.google.inject.Module的实现。

    HelloGuice.java
     

    Java代码
    1. package cn.jcourse.guice;   
    2.   
    3. import cn.jcourse.guice.impl.HelloGuiceImpl;   
    4.   
    5. import com.google.inject.ImplementedBy;   
    6.   
    7. /**  
    8.  * @author zhangtao  
    9.  * HelloGuice接口,用于表达问候  
    10.  */  
    11. @ImplementedBy(HelloGuiceImpl.class)   
    12. public interface HelloGuice {   
    13.     public void sayHello();   
    14. }   

    这里我们使用了Guice提供的注解,ImplementedBy,表示该接口由HelloGuiceImpl类实现。这样我们就可以不手动的去配置依赖关系。再看看TestGuice.java。

    TestGuice.java
     

    Java代码
    1. package cn.jcourse.guice.test;   
    2.   
    3. import junit.framework.TestCase;   
    4. import cn.jcourse.guice.HelloGuice;   
    5.   
    6. import com.google.inject.Guice;   
    7. import com.google.inject.Injector;   
    8.   
    9. /**  
    10.  * @author zhangtao   
    11.  * 测试Guice  
    12.  */  
    13. public class TestGuice extends TestCase {   
    14.     public void testHelloGuice() {   
    15.         //Injector injector = Guice.createInjector(new HelloGuiceModule());   
    16.            
    17.         Injector injector = Guice.createInjector();   
    18.            
    19.         HelloGuice helloGuice = injector.getInstance(HelloGuice.class);   
    20.         helloGuice.sayHello();   
    21.     }   
    22. }  

    可以看出,我们不需要自己去new一个Module了,Guice会根据我们提供的注解自己来配置依赖关系。

    我们运行例子的时候可以看出,它也输出了Hello Guice!到控制台

    通过 Guice 进行依赖项注入(2)

    2009-01-12 作者: 
    zhangtaolv

    其他注入形式

    到目前为止,我展示了 @Inject 应用于构造函数的用法。当 Guice 找到注释时,它会挑选构造函数参数,并试图为每个参数找到一个配置绑定。这称为 构造函数注入。根据 Guice 的最佳实践指南,构造函数注入是询问依赖项的首选方式。但这不是唯一的方式。清单 9 显示了配置 FrogMan 类的另一种方式:

    清单 9. 方法注入

    Java代码
    1. public class FrogMan{   
    2.   private Vehicle vehicle;   
    3.   
    4.   @Inject  
    5.   public void setVehicle(Vehicle vehicle) {   
    6.     this.vehicle = vehicle;   
    7.   }   
    8. //etc. ...  

    注意,我没有使用注入的构造函数,而是改用一个带有 @Inject 标记的方法。Guice 会在构造好 hero 之后立即调用此方法。Spring 框架的忠实用户可以将此方法视为 “setter 注入”。不过,Guice 只关心 @Inject;您可以任意命名这个方法,它可以带有多个参数。此方法可以是包保护的,也可以是私有方法。

    如果您认为 Guice 访问私有方法不是很好,可以参见清单 10,其中 FrogMan 使用了字段注入:

    清单 10. 字段注入

    Java代码
    1. public class FrogMan {   
    2.   @Inject private Vehicle vehicle;   
    3.   public FrogMan(){}   
    4. //etc. ...  

    同样,所有 Guice 都只关心 @Inject 注释。字段注入查找注释的所有字段并试图注入相应的依赖项。

    哪种方法最好

    三个 FrogMan 版本都展示了相同的行为:Guice 在构建时注入相应的 Vehicle。不过,像 Guice 的作者一样,我更喜欢构造函数注入。下面简单分析这三种方式:

    • 构造函数注入 很简单。因为 Java 技术能保证构造函数调用,您不用担心出现未初始化的对象 — 不管是不是由 Guice 创建的。您还可以将字段标记为 final。
    • 字段注入 会影响可测试性,特别是将字段标记为 private 时。这破坏了使用 DI 的主要目的。应该尽量少使用字段注入。
    • 方法注入 在您不控制类的实例化时很有用。如果您有一个需要某些依赖项的超类,也可以使用方法注入(构造函数注入会使这种情况变得很复杂)。

    选择实现

    现在,假设应用程序中有多个 Vehicle。一样英勇的 Weasel Girl 无法驾驭 FrogMobile!同时,您不想在 WeaselCopter 上硬编码依赖项。清单 11 显示了 Weasel Girl 请求一种更快的传输模式:

    清单 11. 使用注释请求某种特定的实现

    Java代码
    1. @Inject  
    2. public WeaselGirl(@Fast Vehicle vehicle) {   
    3.   this.vehicle = vehicle;   
    4. }  

    在清单 12 中,HeroModule 使用绑定函数告诉 Guice WeaselCopter 是 “很快” 的:

    清单 12. 告诉 Guice Module 中的相关注释

    Java代码
    1. public class HeroModule implements Module {   
    2.  public void configure(Binder binder) {   
    3.     binder.bind(Vehicle.class).to(FrogMobile.class);   
    4.     binder.bind(Vehicle.class).annotatedWith(Fast.class).to(WeaselCopter.class);   
    5.   }   
    6. }  

    注意,我选择了一个注释,描述我想以抽象形式描述的工具种类(@Fast),而不是与实现太接近的注释(@WeaselCopter)。如果您使用的注释将想要的实现描述得太精确,就让读者觉得创建一个隐式依赖项。如果使用 @WeaselCopter,而且 Weasel Girl 借用了 Wombat Rocket,就会对程序员阅读和调试代码造成混淆。

    要创建 @Fast 注释,需要复制清单 13 中的模板:

    清单 13. 复制粘贴这段代码以创建一个绑定注释

    Java代码
    1. @Retention(RetentionPolicy.RUNTIME)   
    2. @Target({ElementType.FIELD, ElementType.PARAMETER})   
    3. @BindingAnnotation  
    4. public @interface Fast {}  

    如果您编写了大量 BindingAnnotations,就会得到许多这样的小文件,每个文件只是注释名称不同。如果您觉得这很繁琐,或者需要执行快速的原型设计,可以考虑 Guice 的内置 @Named 注释,它接受一个字符串属性。清单 14 展示了这种替代方法:

    清单 14. 使用 @Named 代替自定义注释

    Java代码
    1. // in WeaselGirl   
    2. @Inject  
    3. public WeaselGirl(@Named("Fast") Vehicle vehicle) {   
    4.   //...   
    5. }   
    6.   
    7. // in HeroModule   
    8. binder.bind(Vehicle.class)   
    9.   .annotatedWith(Names.named("Fast")).to(WeaselCopter.class);  

    这种方法是可行的,但由于名称只在字符串内有效,所以这不能利用编译时检查和自动补齐。总的来说,我更愿意自己编写注释。

    如果您根本不想使用注释,怎么办?即使添加 @Fast 或 @Named("Fast") 都会使类在某种程度上影响配置本身。如果想知道如何解决这个问题,请接着阅读。

    provider 方法

    如果每次探险都派遣 Frog Man,您可能会厌烦。您喜欢在每个场景中出现的 hero 是随机的。但是,Guice 的默认绑定程序 API 不允许出现 “每次调用时将 Hero 类绑定到一个不同的实现” 这样的调用。不过,您可以 告诉 Guice 使用一种特殊的方法来创建每个新的 Hero。清单 15 显示了将一个新方法添加到 HeroModule 中,并用特殊的 @Provides 注释进行注释:

    清单 15. 使用 provider 编写自定义创建逻辑

    Java代码
    1. @Provides  
    2. private Hero provideHero(FrogMan frogMan, WeaselGirl weaselGirl) {   
    3.   if (Math.random() > .5) {   
    4.     return frogMan;   
    5.   }   
    6.   return weaselGirl;   
    7. }  

    Guice 会自动发现具有 @Provides 注释的 Module 中的所有方法。根据 Hero 的返回类型,在您请求某个 hero 时,Guice 会进行计算,它应该调用 provider 方法来提供 hero。您可以为 provider 方法添加逻辑以构建对象并在缓存中查询它,或者通过其他方式获得它。provider 方法是将其他库集成到 Guice 模块中的很好方式。它们也是从 Guice 2.0 开始提供的新方法(Guice 1.0 中只编写自定义 provider 类,这比较乏味,而且更加繁琐。如果您已经决定使用 Guice 1.0,用户指南中有这种旧方法的文档,而且在本文随附的 示例代码 中,您可以找到一个自定义 provider)。

    在清单 15 中,Guice 自动使用正确的参数注入 provider 方法。这意味着 Guice 将从它的绑定列表中找到 WeaselGirl 和 FrogMan,您无需在 provider 方法中手动构建它们。这演示了 “海龟背地球” 原则(海龟背地球,哪海龟下面是什么呢?是由另一只海龟背着,如此反复)。您依靠 Guice 来提供依赖项,即使是配置 Guice 模块本身。

    请求一个 Provider 而不是一个依赖项

    假设一个故事(Saga)中要有多个 hero。如果要求 Guice 注入一个 Hero,只会得到一个 hero。但如果您请求一个 “hero provider”,就可以根据需要创建任意多的 hero,如清单 17 所示:

    清单 17. 注入 provider 来控制实例化

    Java代码
    1. public class Saga {   
    2.   private final Provider heroProvider;   
    3.   
    4.   @Inject  
    5.   public Saga(Provider heroProvider) {   
    6.     this.heroProvider = heroProvider;   
    7.   }   
    8.   
    9.   public void start() throws IOException {   
    10.     for (int i = 0; i < 3; i++) {   
    11.       Hero hero = heroProvider.get();   
    12.       hero.fightCrime();   
    13.     }   
    14.   }   
    15. }  

    提供者也可以推迟英雄的出场时间,直到传奇真正开始。如果英雄依赖于时间敏感或上下文敏感的数据,这就会很方便。

    Provider 接口有一个方法:get。要访问提供的对象,调用这个方法即可。每次有没有获取新对象以及对象如何配置取决于 Guice 是如何配置的(参阅下面的 作用域 部分,了解单实例对象和其他长生命周期对象的详细信息)。在本例中,Guice 使用 @Provides 方法,因为它是构建新 Hero 的注册方式。这意味着该传奇应该由任意三位英雄混合而成。

    不要把提供者与 provider 方法弄混淆了(在 Guice 1.0,这两者更难区分开来)。尽管该 Saga 是从自定义 @Provides 方法中获得它的英雄,但您可以请求任意 Guice 实例化依赖项的一个 Provider。如果需要,可以根据清单 18 重新编写 FrogMan 的构造函数:

    清单 18. 请求 Provider 而不是依赖项

    Java代码
    1. @Inject  
    2. public FrogMan(Provider vehicleProvider) {   
    3.   this.vehicle = vehicleProvider.get();   
    4. }  

    (注意您完全不用更改这个模块代码)。重新编写没有任何作用;只是说明您总是可以请求 Provider,而不用直接请求依赖项。

    作用域

    默认情况下,Guice 为每个请求的依赖项创建一个新实例。如果对象是轻量级的,这个策略可以很好地工作。但是,如果有一个创建开销很大的依赖项,就可能需要在几台客户机之间共享实例。在清单 19 中,HeroModule 将 HeavyWaterRefinery 作为一个单实例对象绑定:

    清单 19. 将 HeavyWaterRefinery 绑定为一个单实例对象

    Java代码
    1. public class HeroModule implements Module {   
    2.   public void configure(Binder binder) {   
    3.     //...   
    4.     binder.bind(FuelSource.class)   
    5.       .to(HeavyWaterRefinery.class).in(Scopes.SINGLETON);   
    6.   }   
    7. }  

    这意味着 Guice 会一直保持 “提炼厂” 可用,只要另一个实例需要燃料源,Guice 就会注入相同 的 “提炼厂”。这避免了在应用程序中启动多个 “提炼厂”。

    在选择作用域时,Guice 提供了一个选项。可以使用绑定程序配置它们,或者直接注释依赖项,如清单 20 所示:

    清单 20. 改用注释选择作用域

    Java代码
    1. @Singleton  
    2. public class HeavyWaterRefinery implements FuelSource {...}  

    Guice 提供了超出范围的 Singleton 作用域,但它允许您定义自己的作用域(如果您愿意)。例如,Guice servlet 包提供了两个其他作用域:Request 和 Session,它们为 servlet 请求和 servlet 会话提供类的一个独特实例。

    常量绑定和模块配置

    HeavyWaterRefinery 需要一个许可密钥才能启动。Guice 可以绑定常量值和新实例。请查看清单 21:

    清单 21. 在模块中绑定常量值

    Java代码
    1. public class HeavyWaterRefinery implements FuelSource {   
    2.   @Inject  
    3.   public HeavyWaterRefinery(@Named("LicenseKey") String key) {...}   
    4. }   
    5.   
    6. // in HeroModule:   
    7. binder.bind(String.class)   
    8.   .annotatedWith(Names.named("LicenseKey")).toInstance("QWERTY");  

    这里有必要使用绑定注释,否则 Guice 将不能区分不同的 String。

    注意,尽管前面不推荐使用,我还是选择使用 @Named 注释。因为我想显示清单 22 所示的代码:

    清单 22. 使用属性文件配置模块

    Java代码
    1. //In HeroModule:   
    2. private void loadProperties(Binder binder) {   
    3.   InputStream stream =   
    4.     HeroModule.class.getResourceAsStream("/app.properties");   
    5.   Properties appProperties = new Properties();   
    6.   try {   
    7.     appProperties.load(stream);   
    8.     Names.bindProperties(binder, appProperties);   
    9.   } catch (IOException e) {   
    10.     // This is the preferred way to tell Guice something went wrong   
    11.     binder.addError(e);   
    12.   }   
    13. }   
    14.   
    15. //In the file app.properties:   
    16. LicenseKey=QWERTY1234  

    这段代码使用 Guice Names.bindProperties 实用函数,通过恰当的 @Named 注释将 app.properties 文件中的每个属性与一个常量绑定。这本身就很好,而且还显示了您可以使模块代码更复杂。如果喜欢,可以从数据库或 XML 文件加载绑定信息。模块是纯 Java 代码,这提供了很大的 灵活性。

    结束语

    Guice 主要概念小结:

    • 使用 @Inject 请求依赖项。
    • 将依赖项与 Module 中的实现绑定。
    • 使用 Injector 引导应用程序。
    • 使用 @Provides 方法增加灵活性。

    需要了解的 Guice 知识还很多,但您应该先掌握这篇文章中讨论的内容。我建议下载它,以及本文的 示例代码。当然,您也可以创建自己的示例应用程序,这就更好了。通过示例深入了解概念但又不用考虑生产代码是很有意思的。如果要了解更多 Guice 高级功能(比如面向方面编程支持),建议您访问 参考资料 中的一些链接。

    说到生产代码,DI 的一个缺点是它可能感染病毒。注入一个类后,它会导致注入下一个类,依此类推。不过这很好,因为 DI 使代码更好。另一方面,这需要大量重构现有代码。为了使工作易于管理,可以将 Guice Injector 存储在某处并直接调用它。应该将这当作一根临时需要的拐杖,但最后一定可以摆脱它。

    Guice 2.0 即将推出。有一些功能我还没有讨论,它可以使模块的配置更简单,而且能支持更大、更复杂的配置方案。

    我希望您会考虑将 Guice 添加到工具包中。根据我的经验,DI 对于实现灵活的可测试代码库特别有用。Guice 使 DI 变得简单而有趣。还有什么比容易编写的、灵活的、可测试的代码更好呢?

    转载于:https://www.cnblogs.com/ss561/p/4679331.html

    展开全文
  • Guice support

    2020-12-09 06:51:28
    <div><p>Currently this module and your vertx-mod-guice module can only be used mutually exclusive. It would be nice to have Guice support or just an example how to integrate it, by using vertx-mod-...
  • Guice refactoring

    2020-12-04 16:11:16
    <div><p>Refactor to not use guice injector directly Decouple RoutingMap and SinkManager from JSON configuration Bridge all Jackson inject directly into Guice</p><p>该提问来源于开源项目:Netflix/...
  • Guice Grapher

    2020-12-26 05:59:18
    <div><p>This adds the Guice configuration grapher from my dissertation as a <code>ControlerListener</code> that puts the live configuration graph into the output directory. <p>Can be viewed with ...
  • <div><p>Would it be possible to enable Guice JIT in the Guice Bridge. I am currently doing this using this adapter: <p>https://github.com/t-tang/guice-bridge-jit-injector</p><p>该提问来源于开源项目&#...
  • Separate guice

    2021-01-08 12:22:14
    <div><p>This separates the schema logic from the guice modules. SchemaBundle could get a bunch of utility methods to make it easy to programmatically define a schema. A few more tests are needed ...
  • mybatis-guice

    2019-04-27 22:20:47
    这个是项目主要是演示mybatis集成google的guice的案例,在网上guice的例子真是太少了
  • ratpack-guice-config 对ratpack-guice的可注射配置支持
  • Google Guice

    2018-10-16 11:32:15
    Guice is a lightweight dependency injection framework for Java . 即 Guice 是轻量级依赖注入的 Java 框架。

    Guice

    Guice is a lightweight dependency injection framework for Java .
    即 Guice 是轻量级依赖注入的 Java 框架。

    特点

    1. 取消 Bean 概念,使用 Java 代码描述绑定规则
    2. 泛型支持
    3. 专注于 Dependency Injection

    依赖

    Maven

        <!-- Guice -->
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>4.2.1</version>
        </dependency>
    
        <!-- Guice扩展插件:多值绑定 -->
        <dependency>
            <groupId>com.google.inject.extensions</groupId>
            <artifactId>guice-multibindings</artifactId>
            <version>4.2.1</version>
        </dependency>
    

    Gradle

        // Guice
        compile 'com.google.inject:guice:4.2.1'
    
        // Guice扩展插件:多值绑定
        compile 'com.google.inject.extensions:guice-multibindings:4.2.1'
    

    注入 @Inject

    1.构造器注入(推荐)

    public class BookManager {
    
        private final BookService bookService;
        private final PriceService priceService;
    
        @Inject
        public BookManager(BookService bookService, PriceService priceService) {
            this.bookService = bookService;
            this.priceService = priceService;
        }
        ...
    }
    

    2.设值注入

    public class BookManager {
    
        @Inject
        private BookService bookService;
    
        @Inject
        private PriceService priceService;
        ...
    }
    

    绑定 bind(…)

    1.类名绑定

        bind(BookService.class).to(BookServiceImpl.class);
    

    2.实例绑定

        bind(BookDao.class).toInstance(new BookDaoImpl()));
    

    3.链式绑定

        bind(PriceService.class).to(PriceServiceImpl.class);
        bind(PriceServiceImpl.class).toInstance(new PriceServiceImpl() {
            @Override
            public BigDecimal getPrice() {
                return BigDecimal.ONE;
            }
        });
    

    4.Provider 绑定

        bind(CurrencyService.class).toProvider(CurrencyProvider.class);
    
        @Provides
        List<String> getSupportedCurrencies(CurrencyService currencyService) {
            return currencyService.getSupportedCurrency();
        }
    

    5.命名绑定

        @Provides
        @Named("supportedCurrencies")
        List<String> getSupportedCurrencies(CurrencyService currencyService) {
            return currencyService.getSupportedCurrency();
        }
    

    6.泛型绑定

        bind(new TypeLiteral<List<String>>() {})
                .annotatedWith(Names.named("supportedCurrencies"))
                .toInstance(Arrays.asList("CNY", "JPY"));
    

    7.集合绑定

        // 集合Set绑定
        Multibinder<String> currencyBinder = Multibinder.newSetBinder(binder(), String.class);
        currencyBinder.addBinding().toInstance("ZH");
        currencyBinder.addBinding().toInstance("EN");
        currencyBinder.addBinding().toInstance("DE");
    
        // 集合Map绑定
        MapBinder<String, Integer> authorBinder = MapBinder.newMapBinder(binder(),String.class,Integer.class);
        authorBinder.addBinding("Leif").toInstance(20);
        authorBinder.addBinding("Chen").toInstance(18);
    

    Module 的相互关系

    1. 并列
    2. 嵌套
    3. 覆盖

    作用域

    1. 默认:一般实例,构造速度快
    2. 单例 singleton :构造速度慢的实例,必须线程安全,如数据库连接
    3. Session Scope :含有 session/request 信息的实例

    Guice 与 Spring 的依赖注入代码比较

    一、Spring 依赖注入

    • Dao
    public interface UserDao {
    
        void save();
    }
    
    public class UserDaoImpl implements UserDao {
    
        @Override
        public void save() {
            System.out.println("Spring BookDao method: save().");
        }
    }
    
    • Service
    public interface UserService {
    
        void save();
    }
    
    public class UserServiceImpl implements UserService {
    
        private final UserDao userDao;
    
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void save() {
            System.out.println("Spring BookService method: save().");
            userDao.save();
        }
    }
    
    • Manager
    public class UserManager {
    
        private final UserService userService;
    
        public UserManager(UserService userService) {
            this.userService = userService;
        }
    
        public void test() {
            userService.save();
        }
    }
    
    • 配置文件 applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="userDao" class="com.chen.spring.dao.impl.UserDaoImpl"/>
        <bean id="userService" class="com.chen.spring.service.impl.UserServiceImpl">
            <constructor-arg ref="userDao"/>
        </bean>
        <bean id="userManager" class="com.chen.spring.manager.UserManager">
            <constructor-arg ref="userService"/>
        </bean>
    
    </beans>
    
    • 主程序
    public class SpringApp {
    
        public static void main(String[] args) {
            BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserManager userManager = (UserManager) beanFactory.getBean("userManager");
            userManager.test();
        }
    }
    

    二、Guice 依赖注入

    • Dao
    public interface BookDao {
    
        void save();
    }
    
    public class BookDaoImpl implements BookDao {
    
        @Override
        public void save() {
            System.out.println("Guice BookDao method: save().");
        }
    }
    
    • Service
    public interface BookService {
    
        void save();
    }
    
    
    public class BookServiceImpl implements BookService {
    
        private final BookDao bookDao;
    
        @Inject
        public BookServiceImpl(BookDao bookDao) {
            this.bookDao = bookDao;
        }
    
        @Override
        public void save() {
            System.out.println("Guice BookService method: save().");
            bookDao.save();
        }
    }
    
    • Manager
    public class BookManager {
    
        private final BookService bookService;
    
        @Inject
        public BookManager(BookService bookService) {
            this.bookService = bookService;
        }
    
        public void test() {
            bookService.save();
        }
    }
    
    • Module
    public class BookModule extends AbstractModule {
    
        @Override
        protected void configure() {
            bind(BookDao.class).toInstance(new BookDaoImpl());
            bind(BookService.class).to(BookServiceImpl.class);
        }
    }
    
    • 主程序
    public class GuiceApp {
    
        public static void main(String[] args) {
            Guice.createInjector(new BookModule()).getInstance(BookManager.class).test();
        }
    }
    

    参考

    [1] 慕课网:使用Google Guice实现依赖注入
    [2] 代码

    展开全文
  • 为何guice

    2018-11-01 17:56:00
    Guice是谷歌推出的一个轻量级依赖注入框架,帮助我们解决Java项目中的依赖注入问题。如果只想在项目中使用依赖注入,这时候我们可以考虑使用Guice,不需要使用Spring那个庞然大物。详细信息可以直接查看Guice文档。 ...
  • Guice依赖注入(一)

    万次阅读 2020-10-10 11:47:28
    本教程主要详细讲解Guice的一些基本注入方式,通过该简单教程让我们可以快速使用Guice进行简单系统化开发,后续我们会更深入讲解更多模块,如果还不了解Guice大家可以先去网上自行了解一下. 基础环境 技术 版本 ...
  • Dropwizard-Guice 一个简单的DropWizard扩展,用于通过捆绑包集成Guice。 它有选择地使用Reflections项目提供的类路径扫描来发现资源,并在服务启动时将更多资源安装到dropwizard环境中。 笔记: 该库已由取代,...
  • Learning Google Guice

    2018-03-09 11:11:23
    谷歌Guice开发英文文档,很详细,对开发很有帮助,可当成工具书使用!
  • Guice框架

    千次阅读 2013-07-14 18:33:23
    一、下载和配置Guice 1、下载Guice 1.1、下载Guice,首先访问下载Guice框架的官方网站(http://code.google.com/p/google-guice/),如下图所示: 1.2、点击“Download”,下载Guice 3.0版本,如下图所示: ...
  • shiro-guice

    2018-06-29 11:24:43
    <groupId>org.apache.shiro <artifactId>shiro-guice <version>1.3.2 </dependency>
  • Hello Guice

    2016-09-22 15:03:01
    HelloGuicepackage cn.jiahaixin.guice.hello;public interface HelloGuice {public void sayHello(); }HelloGuiceImplpackage cn.jiahaixin.guice.hello;public class HelloGuiceImpl implements HelloGuice { @O
  • Guice入门

    2019-03-05 18:40:58
    Guice 官方资料:https://github.com/google/guice 解决的痛点问题 代码中的factory(解耦client和实现类) 过多new的使用 Unit Test难 依赖不明显,隐藏在代码中 优势 减少了factory,采用bind方式 修改方便 :...
  • guice.jar/guice.jar

    2008-12-30 08:42:39
    guice.jar guice.jar guice.jar guice.jar guice.jar guice.jar guice.jar

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,141
精华内容 1,256
关键字:

guice