• Guice入门


    参考链接:http://www.cnblogs.com/xd502djj/archive/2012/06/25/2561414.html

    http://code.google.com/p/google-guice/ 

    Google公司的Bob lee开发的轻量级IoC容器,其特点是: 

    1、速度快,号称是spring的100倍速度 
    2、无配置文件,实用JDK5.0的annotation描述组件依赖,简单,而且有编译器检查和重构支持 
    3、简单,代码量很少 

    http://code.google.com/p/google-guice/wiki/SpringComparison 

    这是Google guice和spring IoC容器的对比 

    另外xwork2.0已经集成了Google guice容器了。 

    http://docs.google.com/Doc?id=dd2fhx4z_5df5hw8 

    这是Google guice的快速入门文档,用起来挺简单的。

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

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

      下载Google Guice之后,

      有以下几个文件:

      aopalliance.jar

      guice-1.0.jar

      guice-servlet-1.0.jar

      guice-spring-1.0.jar

      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
      
    package com.test.guice;
      
    public interface HelloGuice {
      public void sayHello();
    }
      
    HelloGuiceImpl.java
      
    package com.test.guice;
      
    public class HelloGuiceImpl implements HelloGuice {
      
      public void sayHello() {
        System.out.println("Hello Guice!");
      }
    }
      
    HelloGuiceModule.java

    package com.test.guice;
      
    import com.google.inject.Binder;
    import com.google.inject.Module;
      
    public class HelloGuiceModule implements Module {
      
      public void configure(Binder binder) {
        binder.bind(HelloGuice.class).to(HelloGuiceImpl.class);
      }
      
    }
      
    TestGuice.java
    package com.test.guice;
      
    import junit.framework.TestCase;
      
    import com.google.inject.Guice;
    import com.google.inject.Injector;
      
    public class TestGuice extends TestCase {
      public void testHelloGuice() {
        Injector injector = Guice.createInjector(new HelloGuiceModule());
        
        HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
        helloGuice.sayHello();
      }
    }

      运行TestGuice,打印出:

      Hello Guice!

      范例2:使用Java Annotation

      我们也可以直接为HelloGuice加上@ImplementedBy注释,而省略掉对com.google.inject.Module的实现。

    HelloGuice.java
    package com.test.guice;
      
    import com.google.inject.ImplementedBy;
      
    @ImplementedBy(HelloGuiceImpl.class)
    public interface HelloGuice {
      public void sayHello();
    }
      
    TestGuice.java 
    package com.test.guice;
      
    import junit.framework.TestCase;
      
    import com.google.inject.Guice;
    import com.google.inject.Injector;
      
    public class TestGuice extends TestCase {
      public void testHelloGuice() {
        //Injector injector = Guice.createInjector(new HelloGuiceModule());
        
        Injector injector = Guice.createInjector();
        HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
        helloGuice.sayHello();
      }
    }

      HelloGuiceModule.java不再需要。其余的文件内容不变。

      运行TestGuice,打印出:

      Hello Guice!

    摘自 :http://hi.baidu.com/changzhiwin/blog/item/1e8251861feb553466096e36.html 

    1. 依赖注入

    1.1 类依赖注入

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

    HelloWorld.java

    1     public interface HelloWorld {

    3         String sayHello();
    4     }

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

    1     public class HelloWorldImpl implements HelloWorld {

    3         @Override
    4         public String sayHello() {
    5             return "Hello, world!";
    6         }
    7     }

    写一个测试例子看看,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 {

    3         @Override
    4         public String sayHello() {
    5             return "@HelloWorldSubImpl";
    6         }
    7     }
    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 {

    4         String sayHello();
    5     }
    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 {

    4         @Override
    5         public String sayHello() {
    6             return "Hello, world!";
    7         }
    8     }
    9

    附:【前沿】本教程的依赖注入部分基于老菜鸟叮咚的教程,原文在此http://www.family168.com/tutorial/guice/html/。原文主要基于Google Guice 1.0版本的,本文基于Google Guice 2.0版本进行学习和讨论。

    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 

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

    转自 http://www.blogjava.net/xylz/archive/2009/xylz/archive/2009/12/23/307092.html

     
     
  • 相关阅读:
    xuexi
    太厉害了!腾讯T4大牛把《数据结构与算法》讲透了,带源码笔记
    python 死循环
    redis 锁
    Opencv4.1.0交叉编译----终端摄像头内算法嵌入,海思HISI、雄迈ARM编译链使用经验
    SpringCloud之FeignClient调用跨微服接口
    Elasticsearch父子级查询及祖孙辈parent/child关联查询
    服务器ip地址与域名解析及http转https
    RestTemplate请求第三方接口添加Headers token及设置请求体
    kanzi 一些基础参数变量
  • 原文地址:https://www.cnblogs.com/ieayoio/p/5644695.html
Copyright © 2020-2023  润新知