【转载】IDEA 9 TOMCAT 整合开发,修改类不重启 热部署 热加载

下边是个简单的工程

f7b07d49-a0f7-3ba5-aa58-b6103a86b54b

1.我们先来设置编译的目录. modules settings ->

4482d4ee-c2d4-3a60-bb86-4a06643f02e6

2.设置工程所需要的jar.
a07ddcc0-95a2-3307-88dd-2948f772d1c8

3.添加一个web faces.

a1fd516f-88a3-3ad5-80eb-21809a093969

4.设置web  faces.主要设置 web root 路径和web.xml的位置.
2d397f3e-55b6-3a23-9942-ca8fd4102de7
5.添加一个Artifacts.

324c78ae-f144-3e16-845e-3aca931d8966

6.设置Artifacts.与工程相关的配置。

b9587811-240e-32dd-a1ab-b68f7e62bd42

7.设置Artifacts.输出目录.

28e1f176-85e4-3bab-ac01-09829b177a3d

8.添加一个tomcat运行环境.

4c79fb69-80a0-39a7-85ef-93613dc295d8

9.添加一个工程相对的虚拟路径.补充一下。如果Application context 啥都不填,就说是应用的根目录,等于访问路径是http://localhost:8080/.
4be7135f-f0da-305c-a0c7-3d46ea9b9d25

10.ok配置已经好了。可以运行或者编译. 说明一下在debug模式下,修改了类后要按[下图第一个按钮]才能重新加载修改后的类,但服务器不需要重启编译的.

2f3f63bd-6fea-3404-89e5-26975dea9539

【转载】Google Guice 入门教程05 – AOP(面向切面编程)

2 AOP 面向切面编程

2.1 AOP入门

在前面的章节主要讲Guice的依赖注入,有了依赖注入的基础后我们再来看Guice的AOP。我们先从一个例子入手,深入浅出的去理解Guice的AOP的原理和实现。

首先我们定义服务Service,这个服务有一个简单的方法sayHello,当然了我们有一个服务的默认实现ServiceImpl,然后使用@ImplementedBy将服务和默认实现关联起来,同时将服务的实现标注为单例模式。

@ImplementedBy(ServiceImpl.class)
public interface Service {
    void sayHello();
}

在服务的实现ServiceImpl中,我们sayHello方法就是输出一行信息,这行信息包含服务的类名,hashCode以及方法名称和执行的时间。

@Singleton
public class ServiceImpl implements Service {

    @Override
    @Named(“log”)
    public void sayHello() {
        System.out.println(String.format(“[%s#%d] execute %s at %d”, this.getClass().getSimpleName(),hashCode(),”sayHello”,System.nanoTime()));
    }

}

接下来定义一个AOP的实现。在Aopalliance中(大家都认可的AOP联盟)实现我们的方法拦截器。这个拦截器LoggerMethodInterceptor 也没有做什么特别的事情,只是记录些执行的时间,当然了由于执行时间比较短我们用纳秒来描述(尽管不是那么精确)。

在MethodInvocation中我们一定要调用proceed()方法,这样我们的服务才能被执行。当然了如果为了做某些控制我们就能决定是否调用服务代码了。

import static java.lang.System.out;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LoggerMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        long startTime=System.nanoTime();
        out.println(String.format(“before method[%s] at %s”, methodName, startTime));
        Object ret = null;
        try {
            ret = invocation.proceed();
        } finally {
            long endTime=System.nanoTime();
            out.println(String.format(” after method[%s] at %s, cost(ns):%d”, methodName, endTime,(endTime-startTime)));
        }
        return ret;
    }
}

最后才是我们的客户端程序,注意在这里我们需要绑定一个拦截器,这个拦截器匹配任何类的带有log注解的方法。所以这就是为什么我们服务的实现方法需要用log标注的原因了。

public class AopDemo {
    @Inject
    private Service service;

    public static void main(String[] args) {
        Injector inj = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.bindInterceptor(Matchers.any(),//
                        Matchers.annotatedWith(Names.named(“log”)),//
                        new LoggerMethodInterceptor());
            }
        });
        inj.getInstance(AopDemo.class).service.sayHello();
        inj.getInstance(AopDemo.class).service.sayHello();
        inj.getInstance(AopDemo.class).service.sayHello();
    }
}

我们的程序输出了我们期望的结果。

before method[sayHello] at 7811306067456
[ServiceImpl$$EnhancerByGuice$$96717882#33353934] execute sayHello at 7811321912287
after method[sayHello] at 7811322140825, cost(ns):16073369
before method[sayHello] at 7811322315064
[ServiceImpl$$EnhancerByGuice$$96717882#33353934] execute sayHello at 7811322425280
after method[sayHello] at 7811322561835, cost(ns):246771
before method[sayHello] at 7811322710141
[ServiceImpl$$EnhancerByGuice$$96717882#33353934] execute sayHello at 7811322817521
after method[sayHello] at 7811322952455, cost(ns):242314

关于此结果有几点说明。

(1)由于使用了AOP我们的服务得到的不再是我们写的服务实现类了,而是一个继承的子类,这个子类应该是在内存中完成的。

(2)除了第一次调用比较耗时外(可能guice内部做了比较多的处理),其它调用事件为0毫秒(我们的服务本身也没做什么事)。

(3)确实完成了我们期待的AOP功能。

我们的例子暂且说到这里,来看看AOP的相关概念。

2.2 AOP相关概念

老实说AOP有一套完整的体系,光是概念就有一大堆,而且都不容易理解。这里我们结合例子和一些场景来大致了解下这些概念。

通知(Advice)

所谓通知就是我们切面需要完成的功能。比如2.1例子中通知就是记录方式执行的耗时,这个功能我们就称之为一个通知。

比如说在很多系统中我们都会将操作者的操作过程记录下来,但是这个记录过程又不想对服务侵入太多,这样就可以使用AOP来完成,而我们记录日志的这个功能就是一个通知。通知除了描述切面要完成的工作外还需要描述何时执行这个工作,比如是在方法的之前、之后、之前和之后还是只在有异常抛出时。

连接点(Joinpoint)

连接点描述的是我们的通知在程序执行中的时机,这个时机可以用一个“点”来描述,也就是瞬态。通常我们这个瞬态有以下几种:方法运行前,方法运行后,抛出异常时或者读取修改一个属性等等。总是我们的通知(功能)就是插入这些点来完成我们额外的功能或者控制我们的执行流程。比如说2.1中的例子,我们的通知(时间消耗)不仅在方法执行前记录执行时间,在方法的执行后也输出了时间的消耗,那么我们的连接点就有两个,一个是在方法运行前,还有一个是在方法运行后。

切入点(Pointcut)

切入点描述的是通知的执行范围。如果通知描述的是“什么时候”做“什么事”,连接点描述有哪些“时候”,那么切入点可以理解为“什么地方”。比如在2.1例子中我们切入点是所有Guice容器管理的服务的带有@Named(“log”)注解的方法。这样我们的通知就限制在这些地方,这些地方就是所谓的切入点。

切面(Aspect)

切面就是通知和切入点的结合。就是说切面包括通知和切入点两部分,由此可见我们所说的切面就是通知和切入点。通俗的讲就是在什么时候在什么地方做什么事。

引入(Introduction)

引入是指允许我们向现有的类添加新的方法和属性。个人觉
这个特性尽管很强大,但是大部分情况下没有多大作用,因为如果一个类需要切面来增加新的方法或者属性的话那么我们可以有很多更优美的方式绕过此问题,而是在绕不过的时候可能就不是很在乎这个功能了。

目标(Target)

目标是被通知的对象,比如我们2.1例子中的ServiceImpl 对象。

代理(Proxy)

代理是目标对象被通知引用后创建出来新的对象。比如在2.1例子中我们拿到的Service对象都不是ServiceImpl本身,而是其包装的子类ServiceImpl$$EnhancerByGuice$$96717882。

织入(Weaving)

所谓织入就是把切面应用到目标对象来创建新的代理对象的过程。通常情况下我们有几种实际来完成织入过程:

编译时:就是在Java源文件编程成class时完成织入过程。AspectJ就存在一个编译器,运行在编译时将切面的字节码编译到目标字节码中。

类加载时:切面在目标类加载到JVM虚拟机中时织入。由于是在类装载过程发生的,因此就需要一个特殊的类装载器(ClassLoader),AspectJ就支持这种特性。

运行时:切面在目标类的某个运行时刻被织入。一般情况下AOP的容器会建立一个新的代理对象来完成目标对象的功能。事实上在2.1例子中Guice就是使用的此方式。

Guice支持AOP的条件是:

  • 类必须是public或者package (default)
  • 类不能是final类型的
  • 方法必须是public,package或者protected
  • 方法不能使final类型的
  • 实例必须通过Guice的@Inject注入或者有一个无参数的构造函数

2.3 切面注入依赖

如果一个切面(拦截器)也需要注入一些依赖怎么办?没关系,Guice允许在关联切面之前将切面的依赖都注入。比如看下面的例子。

我们有一个前置服务,就是将所有调用的方法名称输出。

@ImplementedBy(BeforeServiceImpl.class)
public interface BeforeService {

    void before(MethodInvocation invocation);
}

public class BeforeServiceImpl implements BeforeService {

    @Override
    public void before(MethodInvocation invocation) {
        System.out.println(“before method “+invocation.getMethod().getName());
    }
}

然后有一个切面,这个切面依赖前置服务,然后输出一条方法调用结束语句。

public class AfterMethodInterceptor implements MethodInterceptor {
   @Inject
    private BeforeService beforeService;
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        beforeService.before(invocation);
        Object ret = null;
        try {
            ret = invocation.proceed();
        } finally {
            System.out.println(“after “+invocation.getMethod().getName());
        }
        return ret;
    }
}

在AopDemo2中演示了如何注入切面的依赖。在第9行,AfterMethodInterceptor 请求Guice注入其依赖。

public class AopDemo2 {
    @Inject
    private Service service;
    public static void main(String[] args) {
        Injector inj = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                AfterMethodInterceptor after= new AfterMethodInterceptor();
                binder.requestInjection(after);
                binder.bindInterceptor(Matchers.any(),//
                        Matchers.annotatedWith(Names.named(“log”)),//
                        after);
            }
        });
        AopDemo2 demo=inj.getInstance(AopDemo2.class);
        demo.service.sayHello();
    }
}

尽管切面允许注入其依赖,但是这里需要注意的是,如果切面依赖仍然走切面的话那么程序就陷入了死循环,很久就会堆溢出。

2.4 Matcher

Binder绑定一个切面的API是

com.google.inject.Binder.bindInterceptor(Matcher<? super Class<?>>, Matcher<? super Method>, MethodInterceptor…)

第一个参数是匹配类,第二个参数是匹配方法,第三个数组参数是方法拦截器。也就是说目前为止Guice只能拦截到方法,然后才做一些切面工作。

对于Matcher有如下API:

  • com.google.inject.matcher.Matcher.matches(T)
  • com.google.inject.matcher.Matcher.and(Matcher<? super T>)
  • com.google.inject.matcher.Matcher.or(Matcher<? super T>)

其中第2、3个方法我没有发现有什么用,好像Guice不适用它们,目前没有整明白。

对于第一个方法,如果是匹配Class那么这里T就是一个Class<?>的类型,如果是匹配Method就是一个Method对象。不好理解吧。看一个例子。

static class ServiceClassMatcher implements Matcher<Class<?>>{
    @Override
    public Matcher<Class<?>> and(Matcher<? super Class<?>> other) {
        return null;
    }
    @Override
    public boolean matches(Class<?> t) {
        return t==ServiceImpl.class;
    }
    @Override
    public Matcher<Class<?>> or(Matcher<? super Class<?>> other) {
        return null;
    }
}

在前面的例子中我们是使用的Matchers.any()对象匹配所有类而通过标注来识别方法,这里可以只匹配ServiceImpl类来控制服务运行流程。

事实上Guice里面有一个Matcher的抽象类com.google.inject.matcher.AbstractMatcher<T>,我们只需要覆盖其中的matches方法即可。

大多数情况下我们只需要使用Matchers提供的默认类即可。Matchers中有如下API:

  • com.google.inject.matcher.Matchers.any():任意类或者方法
  • com.google.inject.matcher.Matchers.not(Matcher<? super T>):不满足此条件的类或者方法
  • com.google.inject.matcher.Matchers.annotatedWith(Class<? extends Annotation>):带有此注解的类或者方法
  • com.google.inject.matcher.Matchers.annotatedWith(Annotation):带有此注解的类或者方法
  • com.google.inject.matcher.Matchers.subclassesOf(Class<?>):匹配此类的子类型(包括本身类型)
  • com.google.inject.matcher.Matchers.only(Object):与指定类型相等的类或者方法(这里是指equals方法返回true)
  • com.google.inject.matcher.Matchers.identicalTo(Object):与指定类型相同的类或者方法(这里是指同
    个对象)

  • com.google.inject.matcher.Matchers.inPackage(Package):包相同的类
  • com.google.inject.matcher.Matchers.inSubpackage(String):子包中的类(包括此包)
  • com.google.inject.matcher.Matchers.returns(Matcher<? super Class<?>>):返回值为指定类型的方法

通常只需要使用上面的方法或者组合方法就能满足我们的需求。

通过上面的学习可以看出,Guice的AOP还是很弱的,目前仅仅支持方法级别上的,另外灵活性也不是很高。

【转载】Google Guice 入门教程02 &ndash; 依赖注入

属性注入(Field Inject)

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

@ImplementedBy(ServiceImpl.class)
public interface Service {
void execute();
}

ServiceImpl.java

public class ServiceImpl implements Service {
@Override
public void execute() {
System.out.println(”This is made by imxylz (www.imxylz.info).”);
}
}

FieldInjectDemo.java

/** a demo with Field inject
* @author xylz (www.imxylz.info)
* @version $Rev: 71 $
*/
public class FieldInjectDemo {
@Inject
private Service servcie;
public Service getServcie() {
return servcie;
}
public static void main(String[] args) {
FieldInjectDemo demo = Guice.createInjector().getInstance(FieldInjectDemo.class);
demo.getServcie().execute();
}
}

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

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

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

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

/** field inject demo2
* @author xylz (www.imxylz.info)
* @version $Rev: 73 $
*/
public class FieldInjectDemo2 {
@Inject
private Service servcie;
public Service getServcie() {
return servcie;
}
public static void main(String[] args) {
FieldInjectDemo2 fd = new FieldInjectDemo2();
fd.getServcie().execute();
}
}

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

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

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

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

看下面的代码。

public class FieldInjectDemo2 {
@Inject
private static Service servcie;
public static Service getServcie() {
return servcie;
}
public static void main(String[] args) {
FieldInjectDemo2 fd = Guice.createInjector().getInstance(FieldInjectDemo2.class);
FieldInjectDemo2.getServcie().execute();
}
}

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

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

构造函数注入(Constructor Inject)

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

/**
* $Id: ConstructorInjectDemo.java 75 2009-12-23 14:22:35Z xylz $
* xylz study project (www.imxylz.info)
*/
package cn.imxylz.study.guice.inject;

import com.google.inject.Guice;
import com.google.inject.Inject;

/** a demo with constructor inject
* @author xylz (www.imxylz.info)
* @version $Rev: 75 $
*/
public class ConstructorInjectDemo {

private Service service;
@Inject
public ConstructorInjectDemo(Service service) {
this.service=service;
}
public Service getService() {
return service;
}
public static void main(String[] args) {
ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
cid.getService().execute();
}

}

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

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

public class ConstructorInjectDemo {

private Service service;
private HelloWorld helloWorld;
@Inject
public ConstructorInjectDemo(Service service,HelloWorld helloWorld) {
this.service=service;
this.helloWorld=helloWorld;
}
public Service getService() {
return service;
}
public HelloWorld getHelloWorld() {
return helloWorld;
}
public static void main(String[] args) {
ConstructorInjectDemo cid = Guice.createInjector().getInstance(ConstructorInjectDemo.class);
cid.getService().execute();
System.out.println(cid.getHelloWorld().sayHello());
}
}

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

Setter注入(Setter Method Inject)

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

public class SetterInjectDemo {

private Service service;

@Inject
public void setService(Service service) {
this.service = service;
}

public Service getService() {
return service;
}

public static void main(String[] args) {
SetterInjectDemo sid = Guice.createInjector().getInstance(SetterInjectDemo.class);
sid.getService().execute();
}

}

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

/** a demo for static field inject
* @author xylz (www.imxylz.info)
* @version $Rev: 78 $
*/
public class StaticFieldInjectDemo {

@Inject
private static Service service;

public static void main(String[] args) {
Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.requestStaticInjection(StaticFieldInjectDemo.class);
}
});
StaticFieldInjectDemo.service.execute();
}
}

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

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

public class InstanceFieldInjectDemo {

@Inject
private Service service;
public static void main(String[] args) {
final InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.requestInjection(ifid);
}
});
ifid.service.execute();
}
}

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

public class InstanceFieldInjectDemo {

@Inject
private Service service;
public static void main(String[] args) {
InstanceFieldInjectDemo ifid = new InstanceFieldInjectDemo();
Guice.createInjector().injectMembers(ifid);
ifid.service.execute();
}
}

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

【转载】Google Guice 入门教程01 &ndash; 依赖注入

【前沿】本教程基于老菜鸟叮咚的教程,原文在此http://www.family168.com/tutorial/guice/html/。特别注意,原文基于Google Guice 1.0版本的,本教程基于Google Guice 2.0版本,因此需要注意guice版本。

类依赖注入

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

HelloWorld.java

public interface HelloWorld {

String sayHello();
}

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

public class HelloWorldImpl implements HelloWorld {

@Override
public String sayHello() {
return “Hello, world!”;
}
}

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

public class HelleWorldTest {

@Test
public void testSayHello() {
Injector inj=  Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(HelloWorld.class).to(HelloWorldImpl.class);
}
});
HelloWorld hw = inj.getInstance(HelloWorld.class);
Assert.assertEquals(hw.sayHello(), “Hello, world!”);
}
}

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

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

HelloWorld hw = inj.getInstance(HelloWorld.class);
Assert.assertEquals(hw.sayHello(), “Hello, world!”);
HelloWorld hw2 = inj.getInstance(HelloWorld.class);
System.out.println(hw.hashCode()+”->”+hw2.hashCode());
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),如果绑定多个实现到同一个接口上会出现什么情况?

public class HelloWorldImplAgain implements HelloWorld {
@Override
public String sayHello() {
return “Hello world again.”;
}
}

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),可以绑定一个实现类到实现类么?

Injector inj=  Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(HelloWorldImpl.class).to(HelloWorldImpl.class);
}
});
HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
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);

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

public class HelloWorldSubImpl extends HelloWorldImpl {

@Override
public String sayHello() {
return “@HelloWorldSubImpl”;
}
}

Injector inj=  Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(HelloWorldImpl.class).to(HelloWorldSubImpl.class);
}
});
HelloWorldImpl hw = inj.getInstance(HelloWorldImpl.class);
System.out.println(hw.sayHello());

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

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

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

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

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

Injector inj=  Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(HelloWorld.class).toInstance(new HelloWorldImpl());
}
});
HelloWorld hw = inj.getInstance(HelloWorld.class);
System.out.println(hw.sayHello());

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

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

Injector inj=  Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(HelloWorld.class).toProvider(new Provider<HelloWorld>() {
@Override
public HelloWorld get() {
return new HelloWorldImpl();
}
});
}
});
HelloWorld hw = inj.getInstance(HelloWorld.class);
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提供了注解的方式完成关联。我们需要在接口上指明此接口被哪个实现类关联了。

@ImplementedBy(HelloWorldImpl.class)
public interface HelloWorld {

String sayHello();
}

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

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

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

Injector inj = Guice.createInjector(new Module() {

@Override
public void configure(Binder binder) {
binder.bind(HelloWorld.class).to(HelloWorldImplAgain.class).in(Scopes.SINGLETON);
}
});
HelloWorld hw = inj.getInstance(HelloWorld.class);
HelloWorld hw2 = inj.getInstance(HelloWorld.class);
System.out.println(hw.hashCode() + “->” + hw2.hashCode());

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

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

@Singleton
public class HelloWorldImpl implements HelloWorld {

@Override
public String sayHello() {
return “Hello, world!”;
}
}

【转载】HashMap的读写并发

大家都知道HashMap不是线程安全的,但是大家的理解可能都不是十分准确。很显然读写同一个key会导致不一致大家都能理解,但是如果读写一个不变的对象会有问题么?看看下面的代码就明白了。

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class HashMapTest2 {
static void doit() throws Exception{
final int count = 200;
final AtomicInteger checkNum = new AtomicInteger(0);
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(100);
//
final Map<Long, String> map = new HashMap<Long, String>();
map.put(0L, “www.imxylz.info”);
for (int j = 0; j < count; j++) {
newFixedThreadPool.submit(new Runnable() {
public void run() {
map.put(System.nanoTime()+new Random().nextLong(), “www.imxylz.info”);
String obj = map.get(0L);
if (obj == null) {
checkNum.incrementAndGet();
}
}
});
}
newFixedThreadPool.awaitTermination(1, TimeUnit.SECONDS);
newFixedThreadPool.shutdown();

System.out.println(checkNum.get());
}

public static void main(String[] args) throws Exception{
for(int i=0;i<10;i++) {
doit();
Thread.sleep(500L);
}
}
}

结果一定会输出0么?结果却不一定。比如某一次的结果是:

0
3
0
0
0
0
9
0
9
0

查看了源码,其实出现这个问题是因为HashMap在扩容是导致了重新进行hash计算。

在HashMap中,有下面的源码:

public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}

在indexOf中就会导致计算有偏移。

static int indexFor(int h, int length) {
return h & (length-1);
}

很显然在Map的容量(table.length,数组的大小)有变化时就会导致此处计算偏移变化。这样每次读的时候就不一定能获取到目标索引了。为了证明此猜想,我们改造下,变成以下的代码。

final Map<String, String> map = new HashMap<String, String>(10000);

执行多次结果总是输出:

0
0
0
0
0
0
0
0
0
0

当然了如果只是读,没有写肯定没有并发的问题了。改换Hashtable或者ConcurrentHashMap肯定也是没有问题了。