zoukankan      html  css  js  c++  java
  • Google Guice 入门教程

    01 - 依赖注入


    1. 依赖注入

    1.1 类依赖注入

    所谓的绑定就是将一个接口绑定到具体的类中,这样客户端不用关心具体的实现,而只需要获取相应的接口完成其服务即可。

    HelloWorld.java

    1     public interface HelloWorld {
    2
    3         String sayHello();
    4     }
    5

    然后是具体的实现,HelloWorldImpl.java

    1     public class HelloWorldImpl implements HelloWorld {
    2
    3         @Override
    4         public String sayHello() {
    5             return "Hello, world!";
    6         }
    7     }
    8

    写一个测试例子看看,HelleWorldTest.java

    1     public class HelleWorldTest {
    2
    3         @Test
    4         public void testSayHello() {
    5           Injector inj=  Guice.createInjector(new Module() {
    6                 @Override
    7                 public void configure(Binder binder) {
    8                     binder.bind(HelloWorld.class).to(HelloWorldImpl.class);
    9                 }
    10             });
    11           HelloWorld hw = inj.getInstance(HelloWorld.class);
    12           Assert.assertEquals(hw.sayHello(), "Hello, world!");
    13         }
    14     }
    15

    这个例子非常简单,通俗的将就是将一个HelloWorldImpl的实例与HelloWorld关联起来,当想Guice获取一个 HelloWorld实例的时候,Guice就返回一个HelloWorldImpl的实例,然后我们就可以调用HelloWorld服务的方法了。

    问题(1)HelloWorld是单例的么?测试下。

    1 HelloWorld hw = inj.getInstance(HelloWorld.class);
    2 Assert.assertEquals(hw.sayHello(), "Hello, world!");
    3 HelloWorld hw2 = inj.getInstance(HelloWorld.class);
    4 System.out.println(hw.hashCode()+"->"+hw2.hashCode());
    5 Assert.assertEquals(hw.hashCode(), hw2.hashCode());

    解答(1)测试结果告诉我们,HelloWorld不是单例的,每次都会返回一个新的实例。

    问题(2)HelloWorld的实例是HelloWorldImpl么?可以强制转型么?

        HelloWorld hw = inj.getInstance(HelloWorld.class);
        System.out.println(hw.getClass().getName());

         

    解答(2),结果输出cn.imxylz.study.guice.helloworld.HelloWorldImpl,看来确实只是返回了一个正常的实例,并没有做过多的转换和代理。

    问题(3),如果绑定多个实现到同一个接口上会出现什么情况?

    1 public class HelloWorldImplAgain implements HelloWorld {
    2     @Override
    3     public String sayHello() {
    4         return "Hello world again.";
    5     }
    6 }

    binder.bind(HelloWorld.class).to(HelloWorldImpl.class);
    binder.bind(HelloWorld.class).to(HelloWorldImplAgain.class);

    解答(3),很不幸,Guice目前看起来不允许多个实例绑定到同一个接口上了。

            com.google.inject.CreationException: Guice creation errors:

            1) A binding to cn.imxylz.study.guice.helloworld.HelloWorld was already configured at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:28).
              at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:29)

    问题(4),可以绑定一个实现类到实现类么?

        1 Injector inj=  Guice.createInjector(new Module() {
        2       @Override
        3       public void configure(Binder binder) {
        4           binder.bind(HelloWorldImpl.class).to(HelloWorldImpl.class);
        5       }
        6   });
        7 HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
        8 System.out.println(hw.sayHello());

         

    非常不幸,不可以自己绑定到自己。

        1) Binding points to itself.
          at cn.imxylz.study.guice.helloworld.HelleWorldTest$1.configure(HelleWorldTest.java:28)

    我们来看看bind的语法。
    <T> AnnotatedBindingBuilder<T> bind(Class<T> type);


    ScopedBindingBuilder to(Class<? extends T> implementation);

    也就是说只能绑定一个类的子类到其本身。改造下,改用子类替代。

    1     public class HelloWorldSubImpl extends HelloWorldImpl {
    2
    3         @Override
    4         public String sayHello() {
    5             return "@HelloWorldSubImpl";
    6         }
    7     }
    8

    1 Injector inj=  Guice.createInjector(new Module() {
    2             @Override
    3             public void configure(Binder binder) {
    4                 binder.bind(HelloWorldImpl.class).to(HelloWorldSubImpl.class);
    5             }
    6         });
    7       HelloWorldImpl hw = inj.getInstance(HelloWorldImpl.class);
    8       System.out.println(hw.sayHello());

    太好了,支持子类绑定,这样即使我们将一个实现类发布出去了(尽管不推荐这么做),我们在后期仍然有办法替换实现类。

    使用bind有一个好处,由于JAVA 5以上的泛型在编译器就确定了,所以可以帮我们检测出绑定错误的问题,而这个在配置文件中是无法检测出来的。

    这样看起来Module像是一个Map,根据一个Key获取其Value,非常简单的逻辑。

    问题(5),可以绑定到我们自己构造出来的实例么?

    解答(5)当然可以!看下面的例子。

    1 Injector inj=  Guice.createInjector(new Module() {
    2             @Override
    3             public void configure(Binder binder) {
    4                 binder.bind(HelloWorld.class).toInstance(new HelloWorldImpl());
    5             }
    6         });
    7       HelloWorld hw = inj.getInstance(HelloWorld.class);
    8       System.out.println(hw.sayHello());

    问题(6),我不想自己提供逻辑来构造一个对象可以么?

    解答(6),可以Guice提供了一个方式(Provider<T>),允许自己提供构造对象的方式。

    1 Injector inj=  Guice.createInjector(new Module() {
    2       @Override
    3       public void configure(Binder binder) {
    4           binder.bind(HelloWorld.class).toProvider(new Provider<HelloWorld>() {
    5               @Override
    6               public HelloWorld get() {
    7                   return new HelloWorldImpl();
    8               }
    9           });
    10       }
    11   });
    12 HelloWorld hw = inj.getInstance(HelloWorld.class);
    13 System.out.println(hw.sayHello());

    问题(7),实现类可以不经过绑定就获取么?比如我想获取HelloWorldImpl的实例而不通过Module绑定么?

    解答(7),可以,实际上Guice能够自动寻找实现类。

    Injector inj=  Guice.createInjector();
    HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
    System.out.println(hw.sayHello());

    问题(8),可以使用注解方式完成注入么?不想手动关联实现类。

    解答(8),好,Guice提供了注解的方式完成关联。我们需要在接口上指明此接口被哪个实现类关联了。

    1     @ImplementedBy(HelloWorldImpl.class)
    2     public interface HelloWorld {
    3
    4         String sayHello();
    5     }
    6

    Injector inj=  Guice.createInjector();
    HelloWorld hw = inj.getInstance(HelloWorld.class);
    System.out.println(hw.sayHello());


    事实上对于一个已经被注解的接口我们仍然可以使用Module来关联,这样获取的实例将是Module关联的实例,而不是@ImplementedBy注解关联的实例。这样仍然遵循一个原则,手动优于自动。

    问题(9)再回头看问题(1)怎么绑定一个单例?

         1     Injector inj = Guice.createInjector(new Module() {
         2
         3         @Override
         4         public void configure(Binder binder) {
         5             binder.bind(HelloWorld.class).to(HelloWorldImplAgain.class).in(Scopes.SINGLETON);
         6         }
         7     });
         8     HelloWorld hw = inj.getInstance(HelloWorld.class);
         9     HelloWorld hw2 = inj.getInstance(HelloWorld.class);
        10     System.out.println(hw.hashCode() + "->" + hw2.hashCode());
        11

    可以看到现在获取的实例已经是单例的,不再每次请求生成一个新的实例。事实上Guice提供两种 Scope,com.google.inject.Scopes.SINGLETON和 com.google.inject.Scopes.NO_SCOPE,所谓没有scope即是每次生成一个新的实例。

    对于自动注入就非常简单了,只需要在实现类加一个Singleton注解即可。

        1     @Singleton
        2     public class HelloWorldImpl implements HelloWorld {
        3
        4         @Override
        5         public String sayHello() {
        6             return "Hello, world!";
        7         }
        8     }
        9

    肥鱼 发表于 2009-12-28 15:20:44

    Google Guice 入门教程02 - 依赖注入

    1.2 属性注入(Field Inject)

    1.2.1 基本属性注入

    首先来看一个例子。Service.java

    1 @ImplementedBy(ServiceImpl.class)
    2 public interface Service {
    3     void execute();
    4 }

    ServiceImpl.java

    1 public class ServiceImpl implements Service {
    2     @Override
    3     public void execute() {
    4         System.out.println("This is made by imxylz (www.imxylz.cn).");
    5     }
    6 }

    FieldInjectDemo.java

    1 /** a demo with Field inject
    2  * @author xylz (www.imxylz.cn)
    3  * @version $Rev: 71 $
    4  */
    5 public class FieldInjectDemo {
    6     @Inject
    7     private Service servcie;
    8     public Service getServcie() {
    9         return servcie;
    10     }
    11     public static void main(String[] args) {
    12         FieldInjectDemo demo = Guice.createInjector().getInstance(FieldInjectDemo.class);
    13         demo.getServcie().execute();
    14     }
    15 }

    这个例子比较简单。具体来说就是将接口Service通过@Inject注解注入到FieldInjectDemo类中,然后再 FieldInjectDemo类中使用此服务而已。当然Service服务已经通过@ImplementedBy注解关联到ServiceImpl 类中,每次生成一个新的实例(非单例)。注意,这里FieldInjectDemo类没有通过Module等关联到Guice中,具体可以查看《》。

    意料之中得到了我们期待的结果。

    同样,我们通过问答的方式来加深理解(注意,入门教程我们只是强调怎么使用,至于原理和底层的思想我们放到高级教程中再谈)。

    问题(1):可以自己构造FieldInjectDemo 对象而不通过Guice么?

    1 /** field inject demo2
    2  * @author xylz (www.imxylz.cn)
    3  * @version $Rev: 73 $
    4  */
    5 public class FieldInjectDemo2 {
    6     @Inject
    7     private Service servcie;
    8     public Service getServcie() {
    9         return servcie;
    10     }
    11     public static void main(String[] args) {
    12         FieldInjectDemo2 fd = new FieldInjectDemo2();
    13         fd.getServcie().execute();
    14     }
    15 }

    就像上面的例子中一样,然后运行下看看?非常不幸,我们得到了一个谁都不喜欢的结果。

    Exception in thread "main" java.lang.NullPointerException
        at cn.imxylz.study.guice.inject.FieldInjectDemo2.main(FieldInjectDemo2.java:22)

    很显然,由于FieldInjectDemo2不属于Guice容器(暂且称为容器吧)托管,这样Service服务没有机会被注入到FieldInjectDemo2类中。

    问题(2):可以注入静态属性么?

    看下面的代码。

    1 public class FieldInjectDemo2 {
    2     @Inject
    3     private static Service servcie;
    4     public static Service getServcie() {
    5         return servcie;
    6     }
    7     public static void main(String[] args) {
    8         FieldInjectDemo2 fd = Guice.createInjector().getInstance(FieldInjectDemo2.class);
    9         FieldInjectDemo2.getServcie().execute();
    10     }
    11 }

    很不幸!运行结果告诉我们Guice看起来还不支持静态字段注入。

    好了,上面两个问题我们暂且放下,我们继续学习其它注入功能。

    1.2.2 构造函数注入(Constructor Inject)

    继续看例子。例子是说明问题的很好方式。

    1     /**
    2      * $Id: ConstructorInjectDemo.java 75 2009-12-23 14:22:35Z xylz $
    3      * xylz study project (www.imxylz.cn)
    4      */
    5     package cn.imxylz.study.guice.inject;
    6
    7     import com.google.inject.Guice;
    8     import com.google.inject.Inject;
    9
    10     /** a demo with constructor inject
    11      * @author xylz (www.imxylz.cn)
    12      * @version $Rev: 75 $
    13      */
    14     public class ConstructorInjectDemo {
    15
    16         private Service service;
    17         @Inject
    18         public ConstructorInjectDemo(Service service) {
    19             this.service=service;
    20         }
    21         public Service getService() {
    22             return service;
    23         }
    24         public static void main(String[] args) {
    25             ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
    26             cid.getService().execute();
    27         }
    28
    29     }
    30
    31

    我们在构造函数上添加@Inject来达到自动注入的目的。构造函数注入的好处是可以保证只有一个地方来完成属性注入,这样可以确保在构造函数中完成一些初始化工作(尽管不推荐这么做)。当然构造函数注入的缺点是类的实例化与参数绑定了,限制了实例化类的方式。

    问题(3):构造函数中可以自动注入多个参数么?

    1     public class ConstructorInjectDemo {
    2
    3         private Service service;
    4         private HelloWorld helloWorld;
    5         @Inject
    6         public ConstructorInjectDemo(Service service,HelloWorld helloWorld) {
    7             this.service=service;
    8             this.helloWorld=helloWorld;
    9         }
    10         public Service getService() {
    11             return service;
    12         }
    13         public HelloWorld getHelloWorld() {
    14             return helloWorld;
    15         }
    16         public static void main(String[] args) {
    17             ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
    18             cid.getService().execute();
    19             System.out.println(cid.getHelloWorld().sayHello());
    20         }
    21     }
    22
    23

    非常完美的支持了多参数构造函数注入。当然了没有必要写多个@Inject,而且写了的话不能通过编译。

    1.2.3 Setter注入(Setter Method Inject)

    有了上面的基础我们再来看Setter注入就非常简单了,只不过在setter方法上增加一个@Inject注解而已。

    1     public class SetterInjectDemo {
    2
    3         private Service service;
    4
    5         @Inject
    6         public void setService(Service service) {
    7             this.service = service;
    8         }
    9
    10         public Service getService() {
    11             return service;
    12         }
    13
    14         public static void main(String[] args) {
    15             SetterInjectDemo sid = Guice.createInjector().getInstance(SetterInjectDemo.class);
    16             sid.getService().execute();
    17         }
    18
    19     }
    20
    21

    好了我们再回头看问题2的静态注入(static inject)。下面的例子演示了如何注入一个静态的字段。

    1     /** a demo for static field inject
    2      * @author xylz (www.imxylz.cn)
    3      * @version $Rev: 78 $
    4      */
    5     public class StaticFieldInjectDemo {
    6
    7         @Inject
    8         private static Service service;
    9
    10         public static void main(String[] args) {
    11             Guice.createInjector(new Module() {
    12                 @Override
    13                 public void configure(Binder binder) {
    14                     binder.requestStaticInjection(StaticFieldInjectDemo.class);
    15                 }
    16             });
    17             StaticFieldInjectDemo.service.execute();
    18         }
    19     }
    20
    21

    非常棒!上面我们并没有使用Guice获取一个StaticFieldInjectDemo实例(废话),实际上static字段(属性)是类相关的,因 此我们需要请求静态注入服务。但是一个好处是在外面看起来我们的服务没有Guice绑定,甚至client不知道(或者不关心)服务的注入过程。

    再回到问题(1),参考上面静态注入的过程,我们可以使用下面的方式来注入实例变量的属性。

    1     public class InstanceFieldInjectDemo {
    2
    3         @Inject
    4         private Service service;
    5         public static void main(String[] args) {
    6            final InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
    7             Guice.createInjector(new Module() {
    8                 @Override
    9                 public void configure(Binder binder) {
    10                     binder.requestInjection(ifid);
    11                 }
    12             });
    13             ifid.service.execute();
    14         }
    15     }
    16
    17

    实际上这里有一种简便的方法来注入字段,实际上此方法也支持Setter注入。

    1     public class InstanceFieldInjectDemo {
    2
    3         @Inject
    4         private Service service;
    5         public static void main(String[] args) {
    6             InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
    7             Guice.createInjector().injectMembers(ifid);
    8             ifid.service.execute();
    9         }
    10     }
    11
    12

    好了既然是入门教程,我们就不讨论更深层次的东西了。

    肥鱼 发表于 2009-12-28 15:21:05

    Google Guice 入门教程03 - 依赖注入

    1.3 更多话题

    1.3.1 接口多实现

    如果一个接口有多个实现,这样通过@Inject和Module都难以直接实现,但是这种现象确实是存在的,于是Guice提供了其它注入方式来解决此问题。比如下面的自定义注解。

    1     public interface Service {
    2
    3         void execute();
    4     }
    5
    6

    1 public class HomeService implements Service {
    2     @Override
    3     public void execute() {
    4         System.out.println("home.imxylz.cn");
    5     }
    6 }

    1 public class WwwService implements Service {
    2     @Override
    3     public void execute() {
    4         System.out.println("www.imxylz.cn");
    5     }
    6 }

    1 @Retention(RetentionPolicy.RUNTIME)
    2 @Target({FIELD,PARAMETER})
    3 @BindingAnnotation
    4 public @interface Home {
    5 }

    1 @Retention(RetentionPolicy.RUNTIME)
    2 @Target({FIELD,PARAMETER})
    3 @BindingAnnotation
    4 public @interface Www {
    5 }

    上面的代码描述的是一个Service服务,有WwwService和HomeService两个实现,同时有Www和Home两个注解(如果对注解各个参数不明白的需要单独去学习JAVA 5注解)。好了下面请出我们的主角。
    1     /**
    2      * $Id: MultiInterfaceServiceDemo.java 82 2009-12-24 06:55:16Z xylz $
    3      * xylz study project (www.imxylz.cn)
    4      */
    5     package cn.imxylz.study.guice.inject.more;
    6
    7     import com.google.inject.Binder;
    8     import com.google.inject.Guice;
    9     import com.google.inject.Inject;
    10     import com.google.inject.Module;
    11
    12     /** a demo with multi interfaces
    13      * @author xylz (www.imxylz.cn)
    14      * @version $Rev: 82 $
    15      */
    16     public class MultiInterfaceServiceDemo {
    17         @Inject
    18         @Www
    19         private Service wwwService;
    20         @Inject
    21         @Home
    22         private Service homeService;
    23         public static void main(String[] args) {
    24             MultiInterfaceServiceDemo misd = Guice.createInjector(new Module() {
    25                 @Override
    26                 public void configure(Binder binder) {
    27                     binder.bind(Service.class).annotatedWith(Www.class).to(WwwService.class);
    28                     binder.bind(Service.class).annotatedWith(Home.class).to(HomeService.class);
    29                 }
    30             }).getInstance(MultiInterfaceServiceDemo.class);
    31             misd.homeService.execute();
    32             misd.wwwService.execute();
    33         }
    34     }
    35
    36

    此类的结构是注入两个Service服务,其中wwwService是注入@Www注解关联的WwwService服务,而homeService是注入@Home注解关联的HomeService服务。

    同样关于此结构我们要问几个问题。

    问题(1)静态注入多个服务怎么写?

    其实,参照教程02,我们可以使用下面的例子。

    1 public class StaticMultiInterfaceServiceDemo {
    2     @Inject
    3     @Www
    4     private static Service wwwService;
    5     @Inject
    6     @Home
    7     private static Service homeService;
    8     public static void main(String[] args) {
    9        Guice.createInjector(new Module() {
    10             @Override
    11             public void configure(Binder binder) {
    12                 binder.bind(Service.class).annotatedWith(Www.class).to(WwwService.class);
    13                 binder.bind(Service.class).annotatedWith(Home.class).to(HomeService.class);
    14                 binder.requestStaticInjection(StaticMultiInterfaceServiceDemo.class);
    15             }
    16         });
    17         StaticMultiInterfaceServiceDemo.homeService.execute();
    18         StaticMultiInterfaceServiceDemo.wwwService.execute();
    19     }
    20 }

    问题(2):如果不小心一个属性绑定了多个接口怎么办?

    非常不幸,你将得到类似一下的错误,也就是说不可以绑定多个服务。

    1) cn.imxylz.study.guice.inject.more.StaticMultiInterfaceServiceDemo.wwwService has more than one annotation annotated with @BindingAnnotation: cn.imxylz.study.guice.inject.more.Www and cn.imxylz.study.guice.inject.more.Home
      at cn.imxylz.study.guice.inject.more.StaticMultiInterfaceServiceDemo.wwwService(StaticMultiInterfaceServiceDemo.java:17)

    问题(3):我太懒了不想写注解来区分多个服务,怎么办?

    程序员都是懒惰的,于是Google帮我们提供了一个Names的模板来生成注解。看下面的例子。

    1 public class NoAnnotationMultiInterfaceServiceDemo {
    2     @Inject
    3     @Named("Www")
    4     private static Service wwwService;
    5     @Inject
    6     @Named("Home")
    7     private static Service homeService;
    8     public static void main(String[] args) {
    9        Guice.createInjector(new Module() {
    10             @Override
    11             public void configure(Binder binder) {
    12                 binder.bind(Service.class).annotatedWith(Names.named("Www")).to(WwwService.class);
    13                 binder.bind(Service.class).annotatedWith(Names.named("Home")).to(HomeService.class);
    14                 binder.requestStaticInjection(NoAnnotationMultiInterfaceServiceDemo.class);
    15             }
    16         });
    17         NoAnnotationMultiInterfaceServiceDemo.homeService.execute();
    18         NoAnnotationMultiInterfaceServiceDemo.wwwService.execute();
    19     }
    20 }

    上面的例子中我们使用Named来标注我们的服务应该使用什么样的注解,当然前提是我们已经将相应的服务与注解关联起来了。

    1.3.2 Provider注入

    在教程第一篇中我们提到了可以通过Provider注入一个服务,这里详细说说这种模式。

    首先我们需要构造一个Provider<T>出来。

    1     public class WwwServiceProvider implements Provider<Service> {
    2
    3         @Override
    4         public Service get() {
    5             return new WwwService();
    6         }
    7     }
    8
    9

    上面的Provider的意思很简单,每次新建一个新的WwwService对象出来。

    注入的过程看下面的代码。

    1     public class ProviderServiceDemo {
    2
    3         @Inject
    4         private Service service;
    5
    6         public static void main(String[] args) {
    7             Injector inj=  Guice.createInjector(new Module() {
    8                 @Override
    9                 public void configure(Binder binder) {
    10                     binder.bind(Service.class).toProvider(WwwServiceProvider.class);
    11                 }
    12             });
    13             ProviderServiceDemo psd = inj.getInstance(ProviderServiceDemo.class);
    14             psd.service.execute();
    15         }
    16
    17     }
    18
    19

    很显然如果这东西和线程绑定就非常好了,比如我们可以使用ThreadLocal来做线程的对象交换。

    当然如果想自动注入(不使用Module手动关联)服务的话,可以使用@ProviderBy注解。

    1     @ProvidedBy(WwwServiceProvider.class)
    2     public interface Service {
    3
    4         void execute();
    5     }
    6
    7

    这样我们就不必使用Module将Provider绑定到Service上,获取服务就很简单了。

    ProviderServiceDemo psd = Guice.createInjector().getInstance(ProviderServiceDemo.class);
    psd.service.execute();

    除了上述两种方式我们还可以注入Provider,而不是注入服务,比如下面的例子例子中,属性不再是Service,而是一个Provider<Service>。

    1     public class ProviderServiceDemo {
    2
    3         @Inject
    4         private Provider<Service> provider;
    5
    6         public static void main(String[] args) {
    7             ProviderServiceDemo psd = Guice.createInjector(new Module() {
    8                 @Override
    9                 public void configure(Binder binder) {
    10                     binder.bind(Service.class).toProvider(WwwServiceProvider.class);
    11                 }
    12             }).getInstance(ProviderServiceDemo.class);
    13             psd.provider.get().execute();
    14         }
    15     }
    16
    17

    当然了,由于我们WwwServiceProvider每次都是构造一个新的服务出来,因此在类ProviderServiceDemo中的provider每次获取的服务也是不一样的。

    1.3.3 绑定常量

    看看下面的例子,演示了一个绑定整数值到实例的例子。

    1     public class ConstantInjectDemo {
    2
    3         @Inject
    4         @Named("v")
    5         private int v;
    6         public static void main(String[] args) {
    7
    8             ConstantInjectDemo cid = Guice.createInjector(new Module() {
    9                 @Override
    10                 public void configure(Binder binder) {
    11                     binder.bindConstant().annotatedWith(Names.named("v")).to(12);
    12                 }
    13             }).getInstance(ConstantInjectDemo.class);
    14             System.out.println(cid.v);
    15         }
    16     }
    17
    18

    当然,既然可以使用Named,也就可以使用自己写注解了。但是看起来好像没有多大作用。除了上述写法,也可以用下面的方式实现。

    binder.bind(int.class).annotatedWith(Names.named("v")).toInstance(12);

    除了可以绑定int外,在ConstantBindingBuilder类中还可以绑定其它的基本类型。
    com.google.inject.binder.ConstantBindingBuilder.to(String)
    com.google.inject.binder.ConstantBindingBuilder.to(long)
    com.google.inject.binder.ConstantBindingBuilder.to(boolean)
    com.google.inject.binder.ConstantBindingBuilder.to(double)
    com.google.inject.binder.ConstantBindingBuilder.to(float)
    com.google.inject.binder.ConstantBindingBuilder.to(short)
    com.google.inject.binder.ConstantBindingBuilder.to(char)



    1.3.4 绑定Properties

    除了可以绑定基本类型外,还可以绑定一个Properties到Guice中,当然了,由于Properties本质上时一个Map<String,String>,因此Guice也允许绑定一个Map<String,String>。

    1     @Inject
    2     @Named("web")
    3     private String web;
    4
    5     public static void main(String[] args) {
    6
    7         ConstantInjectDemo cid = Guice.createInjector(new Module() {
    8             @Override
    9             public void configure(Binder binder) {
    10                 Properties properties= new Properties();
    11                 properties.setProperty("web", "www.imxylz.cn");
    12                 Names.bindProperties(binder, properties);
    13             }
    14         }).getInstance(ConstantInjectDemo.class);
    15         System.out.println(cid.web);
    16     }
    17
    18

    肥鱼 发表于 2009-12-28 15:21:20

    Google Guice 入门教程04 - 依赖注入

    本章节继续讨论依赖注入的其他话题,包括作用域(scope,这里有一个与线程绑定的作用域例子)、立即初始化(Eagerly Loading Bindings)、运行阶段(Stage)、选项注入(Optional Injection)等等。

    1.3.5 Scope(作用域)

    在1.1章节中我们初步了解了对象的单例模式,在Guice中提供了一些常见的作用域,比如对于单例模式有下面两个作用域。

        com.google.inject.Scopes.SINGLETON

        com.google.inject.Scopes.NO_SCOPE

    在使用上,可以使用Module的bind来实现,看下面的例子。

    1     public class ScopeDemo {
    2         public static void main(String[] args) {
    3
    4             Service service = Guice.createInjector(new Module() {
    5                 @Override
    6                 public void configure(Binder binder) {
    7                     binder.bind(Service.class).to(WwwService.class).in(Scopes.SINGLETON);
    8                 }
    9             }).getInstance(Service.class);
    10             service.execute();
    11         }
    12     }
    13
    14

    当然单例模式还可以似乎用@Singleton注解。

    在com.google.inject.binder.ScopedBindingBuilder.in(Scope)方法中,一个Scope除了可以使 上面的SINGLETION和NO_SCOPE外,还可以是自己定义的Scope。下面的例子演示了一个与线程绑定的Scope例子。
    1 /**
    2  * $Id: ThreadScopeDemo.java 90 2009-12-25 08:12:21Z xylz $
    3  * xylz study project (www.imxylz.cn)
    4  */
    5 package cn.imxylz.study.guice.inject.more;
    6
    7 import com.google.inject.Binder;
    8 import com.google.inject.Guice;
    9 import com.google.inject.Injector;
    10 import com.google.inject.Key;
    11 import com.google.inject.Module;
    12 import com.google.inject.Provider;
    13 import com.google.inject.Scope;
    14
    15 /** a demo with thread-scope
    16  * @author xylz (www.imxylz.cn)
    17  * @version $Rev: 90 $
    18  */
    19 public class ThreadScopeDemo {
    20
    21     static class ThreadServiceScope implements Scope {
    22
    23         static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
    24
    25         @Override
    26         public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
    27             return new Provider<T>() {
    28                 @Override
    29                 public T get() {
    30                     T instance = (T) threadLocal.get();
    31                     if (instance == null) {
    32                         instance = unscoped.get();
    33                         threadLocal.set(instance);
    34                     }
    35                     return instance;
    36                 }
    37             };
    38         }
    39
    40         @Override
    41         public String toString() {
    42             return "Scopes.ThreadServiceScope";
    43         }
    44     }
    45     
    46     public static void main(String[] args) {
    47         final Injector inj=Guice.createInjector(new Module() {
    48             @Override
    49             public void configure(Binder binder) {
    50                 binder.bind(Service.class).to(WwwService.class).in(new ThreadServiceScope());
    51             }
    52         });
    53         for(int i=0;i<3;i++) {
    54             new Thread("Thread-"+i) {
    55                 public void run() {
    56                     for(int m=0;m<3;m++) {
    57                         System.out.println(String.format("%s-%d:%d",//
    58                                 getName()//
    59                                 ,m//
    60                                 ,inj.getInstance(Service.class).hashCode()));
    61                         try {
    62                             Thread.sleep(50L);
    63                         } catch (Exception e) {
    64                         }
    65                     }
    66                 }
    67             }.start();
    68         }
    69     }
    70 }
    71

    注意,这里用到了《Google Guice 入门教程03 - 依赖注入》的中的两个类Service和WwwService。在本例中ThreadServiceScope 类是一个与线程绑定的作用域(利用ThreadLocal特性),当当前线程中没有构造一个对象的时候先构造一个出来,然后放入线程上下文中,以后每次都 从线程中获取对象。第50行是将WwwService服务以ThreadServiceScope的作用域绑定到Service服务上。第57-60行输 出当前对象的hashCode,如果此类是同一对象的话就应该输出相同的hashCode。为了看到效果,我们使用3个线程,每个线程输出三次来看结果。
    Thread-0-0:18303751
    Thread-1-0:23473608
    Thread-2-0:21480956
    Thread-1-1:23473608
    Thread-0-1:18303751
    Thread-2-1:21480956
    Thread-1-2:23473608
    Thread-2-2:21480956
    Thread-0-2:18303751

    我们看到对于同一个线程(比如说Thread-0)的三次都输出了相同的对象(hashCode为18303751),而与线程2和线程3的hashCode不同。

    (特别说明:如果两个线程输出了同一个hashCode不必惊慌,那是因为可能前一个线程生成的对象的地址空间被GC释放了,结果下一个线程使用了上一个线程的相同空间,所以这里使用Thread.sleep来降低这种可能性)

    事实上在guice-servlet-2.0.jar中有与request和session绑定的scope。
    com.google.inject.servlet.ServletScopes.REQUEST
    com.google.inject.servlet.ServletScopes.SESSION

    1.3.6 Eagerly Loading Bindings (立即初始化)

    除了可以绑定scope外,对象默认在第一次调用时被创建,也即所谓的延时加载,Guice也允许对象在注入到Guice容器中时就被创建出来(显然这是针对单例模式才有效)。
    1 public class EagerSingletonDemo {
    2
    3     public EagerSingletonDemo() {
    4         System.out.println(" constuctor:"+System.nanoTime());
    5     }
    6     void doit() {
    7         System.out.println("       doit:"+System.nanoTime());
    8     }
    9     public static void main(String[] args) throws Exception{
    10         Injector inj = Guice.createInjector(new Module() {
    11             @Override
    12             public void configure(Binder binder) {
    13                 binder.bind(EagerSingletonDemo.class).asEagerSingleton();
    14             }
    15         });
    16         System.out.println("before call:"+System.nanoTime());
    17         Thread.sleep(100L);
    18         inj.getInstance(EagerSingletonDemo.class).doit();
    19     }
    20 }

    结果输出如下:
    constuctor:26996967388652
    before call:26996967713635
           doit:26997069993702

    可以看到我们的对象在调用getInstance之前就已经被构造出来了。

    1.3.7 Stages (运行阶段)

    Guice还有一个特效,可以指定Guice运行模式来控制Guice的加载速度。在com.google.inject.Stage枚举中提供了TOOL,DEVELOPMENT,PRODUCTION三种模式。

    TOOL描述的是带有IDE等插件的运行模式;DEVELOPMENT是指在开发阶段只加载自己需要的功能(对于非立即初始化单例对象采用延后加载),这 样来降低加载不需要功能的时间;而PRODUCTION模式是指完全加载所有功能(对于单例对象采用立即加载方式),这样可以更早的发现问题,免得等需要 某些功能的时候才发现问题(要知道我们某些功能可能需要特定的条件才能触发)。

    其实只有比较多的单例对象,并且单例对象构造比较耗时的情况下才能有用。大部分情况下这点性能可能都忽略不计了。

    默认情况下Guice采用DEVELOPMENT模式。


    1.3.8 Optional Injection (选项注入 )

    选项注入描述的是如果不能从Guice容器中注入一个对象,那么可以使用一个默认的对象。看下面的例子。
    1 public class OptionalInjectionDemo {
    2     @Inject(optional=true)
    3     Service service = new WwwService();
    4     public static void main(String[] args) {
    5         Guice.createInjector(new Module() {
    6             public void configure(Binder binder) {
    7                 //binder.bind(Service.class).to(HomeService.class);
    8             }
    9         }).getInstance(OptionalInjectionDemo.class).service.execute();
    10     }
    11 }

    上述例子中第2行描述的是选项注入,如果不能从Guice容器中获取一个Service服务那么就使用默认的WwwService,否则就是用获取的服务。如果将第7行注释去掉我们就可以看到实际上调用的是HomeService服务了。

  • 相关阅读:
    解决:insert Vodafone sim card,open the mms read report,when receive the read report,cann&#39;t download..
    为什么不记录慢速查询?
    [TypeScript] Understand lookup types in TypeScript
    [Angular] How to styling ng-content
    [Http] Understand what an HTTP Request is
    [AngularJS] Directive for top scroll bar
    [SCSS] Write Custom Functions with the SCSS @function Directive
    [SCSS] Loop Over Data with the SCSS @each Control Directive
    [GraphQL] Deploy a GraphQL dev playground with graphql-up
    [Javascript] Different ways to create an new array/object based on existing array/object
  • 原文地址:https://www.cnblogs.com/wxmarr/p/4565448.html
Copyright © 2011-2022 走看看