Geowebcache切片个数与占用空间估计

=== Estimating the number of tiles and size on disk ===
Please download one of the attached spreadsheets (Open Document or Excel 97).

The green areas in the document are numbers you should change to match your data. When you specify the bounds, use the extent of your actual data, what is known as “dataBounds” in GWC 1.1.x or as gridSubset in GWC 1.2.x. Using the world bounds represents the worst case scenario.

Note that the numbers are for each zoomlevel, but the error margins are so large that this is not significant.

=== Preview ===
To cover the entire planet is quite resource intensive. Assumptions:
  * Bounding box: -180,-90,180,90  (EPSG:4326)
  * Average tile size: 20 kbyte (typical for PNG with some antialiasing, or JPEG of raster)
  * Response time from WMS for 768×768 GetMap: 0.5
  * Number of cores on WMS server: 2
  * Tile dimensions: 256×256 pixels
Please note that these numbers below in many ways represent the worst case scenario. Speak to an expert before using the attached spreadsheet as a decision tool.

|| Zoomlevel || Number of tiles || Disk space (megabytes) || Time to render (hours) || Note ||
|| 0 ||  2 ||  0 ||  0|| ||
|| 1 ||  8 ||  0 ||  0|| ||
|| 2 ||  32 ||  1 ||  0|| ||
|| 3 ||  128 ||  3 ||  0|| ||
|| 4 ||  512 ||  10 ||  0|| ||
|| 5 ||  2,048 ||  40 ||  0|| ||
|| 6 ||  8,192 ||  160 ||  0|| ||
|| 7 ||  32,768 ||  640 ||  0|| ||
|| 8 ||  131,072 ||  2,560 ||  1|| ||
|| 9 ||  524,288 ||  10,240 ||  4|| ||
|| 10 ||  2,097,152 ||  40,960 ||  16|| ||
|| 11 ||  8,388,608 ||  163,840 ||  65|| ||
|| 12 ||  33,554,432 ||  655,360 ||  259|| This is 11 days of rendering ||
|| 13 ||  134,217,728 ||  2,621,440 ||  1,036|| This is 2 terabytes ||
|| 14 ||  536,870,912 ||  10,485,760 ||  4,143|| ||
|| 15 ||  2,147,483,648 ||  41,943,040 ||  16,570|| This is two years of rendering ||
|| 16 ||  8,589,934,592 ||  167,772,160 ||  66,280|| ||
|| 17 ||  34,359,738,368 ||  671,088,640 ||  265,121|| ||
|| 18 ||  137,438,953,472 ||  2,684,354,560 ||  1,060,486 || This is 2.7 petabytes ||
|| 19 ||  549,755,813,888 ||  10,737,418,240 ||  4,241,943 || ||
|| 20 ||  2,199,023,255,552 ||  42,949,672,960 ||  16,967,772 || ||

【转载】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 入门教程06 – Web 和 Servlet

3 Web 和 Servlet

3.1 快速开始

我们从一个例子开始Guice Web的开发。

image

首先准备我们的环境,由于是web开发,因此我们需要guice-servlet的jar包。log4j不是必须的,只是为了方便日志记录而已(Guice内部是使用jdk内部的logging包来完成日志记录的)。

必可避免的要在web.xml中都一些手脚,这里先配置一个filter吧。

<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

GuiceFilter中并没有帮我们完成Guice的初始化工作,因此我们必须手动完成Guice的模块注入。

public class MyGuiceServletContextListener extends GuiceServletContextListener {

@Override
protected Injector getInjector() {
return Guice.createInjector(new ServletModule());
}
}

继续在web.xml中添加东西。

<listener>
<listener-class>cn.imxylz.study.guice.web.MyGuiceServletContextListener</listener-class>
</listener>

显然我们需要将某个PATH映射到一个Servlet上,于是需要在ServletModule上做点事情。

public class MyGuiceServletContextListener extends GuiceServletContextListener {

@Override
protected Injector getInjector() {
return Guice.createInjector(new ServletModule() {
protected void configureServlets() {
serve(”/helloworld”).with(HelloWorldServlet.class);
}
});
}
}

这里将/helloworld这个地址映射到HelloWorldServlet上。好吧,先写个 “HelloWorld”的Servlet吧。

@Singleton
public class HelloWorldServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
resp.getWriter().append(”Hello, guice! “+new Date());
}
}

注意,根据Guice的Servlet要求,每一个Servlet必须是单例的,因此这里强制加上@Singleton。

好了,我们来看看输出。

image

3.2 注入服务

当然了,既然是一个IOC的容器,那么在Guice中也是可以注入服务的。

首先定义一个服务。

@ImplementedBy(HelloWorldImpl.class)
public interface HelloWorld {

void execute() throws IOException;
}

接着是服务的实现,注意在我们的服务中需要request和response对象,并且我们的服务假定是与request绑定的,采用@RequestScoped标签来标识。

@RequestScoped
public class HelloWorldImpl implements HelloWorld {

private HttpServletRequest request;
private HttpServletResponse response;
@Inject
public HelloWorldImpl(HttpServletRequest request, HttpServletResponse response) {
super();
this.request = request;
this.response = response;
}

public void execute() throws IOException{
String name=request.getParameter(”user”);
if(name==null||name.length()<1)name=”Guest”;
response.getWriter().append(String.format(”Hello, %s. %s -> sessionId=%s,hashCode=%d \n”, name,new Date(),request.getSession().getId(),hashCode()));
}

}

然后在我们的Servlet中可以采用如下实现。

@Singleton
public class HelloWorldServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
@Inject
private Injector inj;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
inj.getInstance(HelloWorld.class).execute();
inj.getInstance(HelloWorld.class).execute();
}
}

这里我们自动注入Injector对象,然后通过Inject对象获取我们的服务,注意我们没有将HttpServletRequest和HttpServletResponse手动注入到我们的服务中。

好了,我们再来看看输出。可以看到我们的对象是与request绑定,同一个request使用的同一个HelloWorld服务,不同的request那么是同一个session获取的服务也是不一样的。

image_thumb4

老实说,Guice关于WEB的东西其实大概就这么多。其它的关于多规则匹配顺序,正则表达式匹配等等其实不谈也罢,都很弱,用处不大。

3.3 整合Struts 2

Guice可以与Struts 2整合,当然了理论上可以与其它MVC框架整合,只是Guice官方提供了一个Struts 2的插件。

image_thumb5

首先看看依赖的jar包,我们尽可能的少用jar包。

aopalliance-1.0.jar是guice-servlet依赖的,因此guice需要aopalliance/guice/guice-servlet/guice-struts2-plugin等包,struts2启动依赖commons-logging/freemarker/ognl/struts2-core/xwork等jar包。lo4j只是为了记录日志方便而已。

首先定义一个服务,服务很简单输出服务器的状态。

public interface Service {

String getStatus();
}

public class ServiceImpl implements Service {

public String getStatus() {
return “I’am running.”;
}
}

然后写一个Module绑定服务及其实现,当然如果偷懒可以使用@ImplementedBy,这里为了说明如果在配置文件中配置Module,所以单写一个Module。

public class ServiceModule implements Module {
@Override
public void configure(Binder binder) {
binder.bind(Service.class).to(ServiceImpl.class);
}
}

然后写一个SessionScope级别的对象绑定访问次数。

@SessionScoped
public class AccessCounter {

private AtomicInteger count = new AtomicInteger(0);

public int visit() {
return count.incrementAndGet();
}

@Override
public String toString() {
return String.format(”AccessCounter#%s:%d”, this.hashCode(), count.get());
}
}

好了,我们的Servlet出场了。

package cn.imxylz.study.guice.web.struts2;

import com.google.inject.Inject;

public class AccessStatusAction {

final AccessCounter counter;
final Service service;
String message;

@Inject
public AccessStatusAction(AccessCounter counter, Service service) {
this.counter = counter;
this.service = service;
}

public String execute() {
return “success”;
}

public int getCount() {
return counter.visit();
}

public String getStatus() {
return service.getStatus();
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

可以看到我们很简单的服务又被Guice给入侵了,所以说Guice对我们业务逻辑的侵入是很大,估计这也是很多程序员不愿意推广Guice的一个原因吧。

要写的Java代码就这么多,剩下的就是一堆的配置了。

首先web.xml中配置struts2的fi
lter,注意这里我们没必要再配置一个guice的listener了,因为在guice的struts2的插件中已经配置一个ServletModule了。

<?xml version=”1.0″ encoding=”UTF-8″?>
<web-app xmlns=”http://java.sun.com/xml/ns/javaee”xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd”
version=”2.5″>

<display-name>guice</display-name>
<description>xylz study project – guice</description>

<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!– listener>
<listener-class>cn.imxylz.study.guice.web.MyGuiceServletContextListener</listener-class>
</listener –>
</web-app>

下面该配置struts.xml了。

<!DOCTYPE struts PUBLIC
“-//Apache Software Foundation//DTD Struts Configuration 2.0//EN”
http://struts.apache.org/dtds/struts-2.0.dtd”>

<struts>

<constant name=”guice.module” value=”cn.imxylz.study.guice.web.struts2.ServiceModule”/>

<package name=”default” extends=”struts-default”>
<action name=”access-status”
class=”cn.imxylz.study.guice.web.struts2.AccessStatusAction”>
<result>access-status.jsp</result>
</action>
</package>

</struts>

在这里先配置我们的Module,我们的Module就是完成Guice的注入过程。在guice的Struts2插件中类com.google.inject.struts2.GuiceObjectFactory有以下逻辑:

@Inject(value = “guice.module”, required = false)
void setModule(String moduleClassName) {
try {
// Instantiate user’s module.
@SuppressWarnings({”unchecked”})
Class<? extends Module> moduleClass =
(Class<? extends Module>) Class.forName(moduleClassName);
this.module = moduleClass.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

这段逻辑就是完成我们Module的注入过程。

当然了我们需要配置一个Struts2的action类cn.imxylz.study.guice.web.struts2.AccessStatusAction,访问地址access-status.action正确的时候渲染access-status.jsp页面。

<%@ taglib prefix=”s” uri=”/struts-tags” %>

<html>
<body>
<h1>Access Status</h1>
<h3><b>Access in this session:</b>
<s:property value=”count”/></h3>

<h3><b>Status:</b>
<s:property value=”status”/></h3>

<h3><b>Message:</b>
<s:property value=”message”/></h3>
<h4><%=”sessionId=”+session.getId()%></h4>
</body>
</html>

所有的工作就完成了,我们看看浏览器中的渲染结果。

image_thumb6

即使如此,Guice整合Struts 2还是比较弱的。

【转载】Google Guice 入门教程05 &ndash; 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 入门教程04 &ndash; 依赖注入

本章节继续讨论依赖注入的其他话题,包括作用域(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.info)
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.info)
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服务了。

到此为止,Guice依赖注入的基本教程就学习完了,下面的章节我们进入经典的AOP教程学习。

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

1.3 更多话题

1.3.1 接口多实现

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

public interface Service {

void execute();
}

public class HomeService implements Service {
@Override
public void execute() {
System.out.println(”home.imxylz.info”);
}
}

public class WwwService implements Service {
@Override
public void execute() {
System.out.println(”www.imxylz.info”);
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD,PARAMETER})
@BindingAnnotation
public @interface Home {
}

@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD,PARAMETER})
@BindingAnnotation
public @interface Www {
}

上面的代码描述的是一个Service服务,有WwwService和HomeService两个实现,同时有Www和Home两个注解(如果对注解各个参数不明白的需要单独去学习JAVA 5注解)。好了下面请出我们的主角。

/**
* $Id: MultiInterfaceServiceDemo.java 82 2009-12-24 06:55:16Z xylz $
* xylz study project (www.imxylz.info)
*/
package cn.imxylz.study.guice.inject.more;

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

/** a demo with multi interfaces
* @author xylz (www.imxylz.info)
* @version $Rev: 82 $
*/
public class MultiInterfaceServiceDemo {
@Inject
@Www
private Service wwwService;
@Inject
@Home
private Service homeService;
public static void main(String[] args) {
MultiInterfaceServiceDemo misd = Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(Service.class).annotatedWith(Www.class).to(WwwService.class);
binder.bind(Service.class).annotatedWith(Home.class).to(HomeService.class);
}
}).getInstance(MultiInterfaceServiceDemo.class);
misd.homeService.execute();
misd.wwwService.execute();
}
}

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

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

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

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

public class StaticMultiInterfaceServiceDemo {
@Inject
@Www
private static Service wwwService;
@Inject
@Home
private static Service homeService;
public static void main(String[] args) {
Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(Service.class).annotatedWith(Www.class).to(WwwService.class);
binder.bind(Service.class).annotatedWith(Home.class).to(HomeService.class);
binder.requestStaticInjection(StaticMultiInterfaceServiceDemo.class);
}
});
StaticMultiInterfaceServiceDemo.homeService.execute();
StaticMultiInterfaceServiceDemo.wwwService.execute();
}
}

问题(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的模板来生成注解。看下面的例子。

public class NoAnnotationMultiInterfaceServiceDemo {
@Inject
@Named(”Www”)
private static Service wwwService;
@Inject
@Named(”Home”)
private static Service homeService;
public static void main(String[] args) {
Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(Service.class).annotatedWith(Names.named(”Www”)).to(WwwService.class);
binder.bind(Service.class).annotatedWith(Names.named(”Home”)).to(HomeService.class);
binder.requestStaticInjection(NoAnnotationMultiInterfaceServiceDemo.class);
}
});
NoAnnotationMultiInterfaceServiceDemo.homeService.execute();
NoAnnotationMultiInterfaceServiceDemo.wwwService.execute();
}
}

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

1.3.2 Provider注入

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

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

public class WwwServiceProvider implements Provider<Service> {

@Override
public Service get() {
return new WwwService();
}
}

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

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

public class ProviderServiceDemo {

@Inject
private Service service;

public static void main(String[] args) {
Injector inj=  Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(Service.class).toProvider(WwwServiceProvider.class);
}
});
ProviderServiceDemo psd = inj.getInstance(ProviderServiceDemo.class);
psd.service.execute();
}

}

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

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

@ProvidedBy(WwwServiceProvider.class)
public interface Service {

void execute();
}

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

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

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

public class ProviderServiceDemo {

@Inject
private Provider<Service> provider;

public static void main(String[] args) {
ProviderServiceDemo psd = Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bind(Service.class).toProvider(WwwServiceProvider.class);
}
}).getInstance(ProviderServiceDemo.class);
psd.provider.get().execute();
}
}

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

1.3.3 绑定常量

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

public class ConstantInjectDemo {

@Inject
@Named(”v”)
private int v;
public static void main(String[] args) {

ConstantInjectDemo cid = Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
binder.bindConstant().annotatedWith(Names.named(”v”)).to(12);
}
}).getInstance(ConstantInjectDemo.class);
System.out.println(cid.v);
}
}

当然,既然可以使用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>。

@Inject
@Named(”web”)
private String web;

public static void main(String[] args) {

ConstantInjectDemo cid = Guice.createInjector(new Module() {
@Override
public void configure(Binder binder) {
Properties properties= new Properties();
properties.setProperty(”web”, “www.imxylz.info”);
Names.bindProperties(binder, properties);
}
}).getInstance(ConstantInjectDemo.class);
System.out.println(cid.web);
}

【转载】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肯定也是没有问题了。

【转载】单例模式完全解析

本文将探讨单例模式的各种情况,并给出相应的建议。 单例模式应该是设计模式中比较简单的一个,但是在多线程并发的环境下使用却是不那么简单了。
首先看最原始的单例模式。

1 package xylz.study.singleton;
2
3 public class Singleton {
4
5 private static Singleton instance = null;
6
7 private Singleton() {
8 }
9
10 public static Singleton getInstance() {
11 if (instance == null) {
12 instance = new Singleton();
13 }
14 return instance;
15 }
16 }
17

显然这个写法在单线程环境下非常好,但是多线程会导致多个实例出现,这个大家都能理解。
最简单的改造方式是添加一个同步锁。

1 package xylz.study.singleton;
2
3 public class SynchronizedSingleton {
4
5 private static SynchronizedSingleton instance = null;
6
7 private SynchronizedSingleton() {
8 }
9
10 public static synchronized SynchronizedSingleton getInstance() {
11 if (instance == null) {
12 instance = new SynchronizedSingleton();
13 }
14 return instance;
15 }
16 }
17

显然上面的方法避免了并发的问题,但是由于我们只是在第一次构造对象的时候才需要同步,以后就不再需要同步,所以这里不可避免的有性能开销。于是将锁去掉采用静态的属性来解决同步锁的问题。

1 package xylz.study.singleton;
2
3 public class StaticSingleton {
4
5 private static StaticSingleton instance = new StaticSingleton();
6
7 private StaticSingleton() {
8 }
9
10 public static StaticSingleton getInstance() {
11 return instance;
12 }
13 }
14

上面的方法既没有锁又解决了性能问题,看起来已经满足需求了。但是追求“完美”的程序员想延时加载对象,希望在第一次获取的时候才构造对象,于是大家非常聪明的进行改造,也即非常出名的双重检查锁机制出来了。

1 package xylz.study.singleton;
2
3 public class DoubleLockSingleton {
4
5 private static DoubleLockSingleton instance = null;
6
7 private DoubleLockSingleton() {
8 }
9
10 public static DoubleLockSingleton getInstance() {
11 if (instance == null) {
12 synchronized (DoubleLockSingleton.class) {
13 if (instance == null) {
14 instance = new DoubleLockSingleton();
15 }
16 }
17 }
18 return instance;
19 }
20 }
21

双重锁机制看起来非常巧妙的避免了上面的问题。但是真的是这样的吗?文章《双重检查锁定及单例模式》中谈到了非常多演变的双重检查锁机制带来的问题,包括比较难以理解的指令重排序机制等。总之就是双重锁机制仍然对导致错误问题而不是性能问题。
于是继续改造,某个牛人利用JVM的特性来解决上述问题,具体哪个牛人我忘记了,但是不是下面文章的作者。
(1)《Java theory and practice: Fixing the Java Memory Model, Part 2
(2)《Initialize-On-Demand Holder Class and Singletons

1 package xylz.study.singleton;
2
3 public class HolderSingleton {
4
5 private static class HolderSingletonHolder {
6
7 static HolderSingleton instance = new HolderSingleton();
8 }
9
10 private HolderSingleton() {
11 //maybe throw an Exception when doing something
12 }
13
14 public static HolderSingleton getInstance() {
15 return HolderSingletonHolder.instance;
16 }
17 }
18

上述代码看起来解决了上面单例模式遇到的所有问题,而且实际上工作的很好,没有什么问题。但是却有一个致命的问题,如果第11行抛出了一个异常,也就是第一次构造函数失败将导致永远无法再次得到构建对象的机会。
使用下面的代码测试下。

1 package xylz.study.singleton;
2
3 public class HolderSingletonTest {
4
5 private static class HolderSingletonHolder {
6
7 static HolderSingletonTest instance = new HolderSingletonTest();
8 }
9
10 private static boolean init = false;
11
12 private HolderSingletonTest() {
13 //maybe throw an Exception when doing something
14 if(!init) {
15 init=true;
16 throw new RuntimeException(“fail“);
17 }
18 }
19
20 public static HolderSingletonTest getInstance() {
21 return HolderSingletonHolder.instance;
22 }
23 public static void main(String[] args) {
24 for(int i=0;i<3;i++) {
25 try {
26 System.out.println(HolderSingletonTest.getInstance());
27 } catch (Exception e) {
28 System.err.println(“one->“+i);
29 e.printStackTrace();
30 }catch(ExceptionInInitializerError err) {
31 System.err.println(“two->“+i);
32 err.printStackTrace();
33 }catch(Throwable t) {
34 System.err.println(“three->“+i);
35 t.printStackTrace();
36 }
37 }
38 }
39 }
40

很不幸将得到以下输出:

two->0
java.lang.ExceptionInInitializerError
at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
Caused by: java.lang.RuntimeException: fail
at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:16)
at xylz.study.singleton.HolderSingletonTest.<init>(HolderSingletonTest.java:12)
at xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder.<clinit>(HolderSingletonTest.java:7)
… 2 more
three->1
java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)
three->2
java.lang.NoClassDefFoundError: Could not initialize class xylz.study.singleton.HolderSingletonTest$HolderSingletonHolder
at xylz.study.singleton.HolderSingletonTest.getInstance(HolderSingletonTest.java:21)
at xylz.study.singleton.HolderSingletonTest.main(HolderSingletonTest.java:26)

很显然我们想着第一次加载失败第二次能够加载成功,非常不幸,JVM一旦加载某个类失败将认为此类的定义有问题,将来不再加载,这样就导致我们没有机会再 加载。目前看起来没有办法避免此问题。如果要使用JVM的类加载特性就必须保证类加载一定正确,否则此问题将比并发和性能更严重。如果我们的类需要初始话 那么就需要想其它办法避免在构造函数中完成。看起来像是又回到了老地方,难道不是么?

总之,结论是目前没有一个十全十美的单例模式,而大多数情况下我们只需要满足我们的需求就行,没必有特意追求最“完美”解决方案。