3 Web 和 Servlet
3.1 快速开始
我们从一个例子开始Guice Web的开发。
首先准备我们的环境,由于是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的模块注入。
1
public
class
MyGuiceServletContextListener
extends
GuiceServletContextListener {
2
3
@Override
4
protected
Injector getInjector() {
5
return
Guice.createInjector(
new
ServletModule());
6
}
7
}
8
继续在web.xml中添加东西。
<
listener
>
<
listener-class
>
cn.imxylz.study.guice.web.MyGuiceServletContextListener
</
listener-class
>
</
listener
>
显然我们需要将某个PATH映射到一个Servlet上,于是需要在ServletModule上做点事情。
1
public
class
MyGuiceServletContextListener
extends
GuiceServletContextListener {
2
3
@Override
4
protected
Injector getInjector() {
5
return
Guice.createInjector(
new
ServletModule() {
6
protected
void
configureServlets() {
7
serve(
"
/helloworld
"
).with(HelloWorldServlet.
class
);
8
}
9
});
10
}
11
}
12
这里将/helloworld这个地址映射到HelloWorldServlet上。好吧,先写个 “HelloWorld”的Servlet吧。
1
@Singleton
2
public
class
HelloWorldServlet
extends
HttpServlet{
3
private
static
final
long
serialVersionUID
=
1L
;
4
@Override
5
protected
void
doGet(HttpServletRequest req, HttpServletResponse resp)
throws
ServletException,
6
IOException {
7
resp.getWriter().append(
"
Hello, guice!
"
+
new
Date());
8
}
9
}
注意,根据Guice的Servlet要求,每一个Servlet必须是单例的,因此这里强制加上@Singleton。
好了,我们来看看输出。

3.2 注入服务
当然了,既然是一个IOC的容器,那么在Guice中也是可以注入服务的。
首先定义一个服务。
1
@ImplementedBy(HelloWorldImpl.
class
)
2
public
interface
HelloWorld {
3
4
void
execute()
throws
IOException;
5
}
6
接着是服务的实现,注意在我们的服务中需要request和response对象,并且我们的服务假定是与request绑定的,采用@RequestScoped标签来标识。
1
@RequestScoped
2
public
class
HelloWorldImpl
implements
HelloWorld {
3
4
private
HttpServletRequest request;
5
private
HttpServletResponse response;
6
@Inject
7
public
HelloWorldImpl(HttpServletRequest request, HttpServletResponse response) {
8
super
();
9
this
.request
=
request;
10
this
.response
=
response;
11
}
12
13
public
void
execute()
throws
IOException{
14
String name
=
request.getParameter(
"
user
"
);
15
if
(name
==
null
||
name.length()
<
1
)name
=
"
Guest
"
;
16
response.getWriter().append(String.format(
"
Hello, %s. %s -> sessionId=%s,hashCode=%d
"
n
"
, name,new Date(),request.getSession().getId(),hashCode()));
17
}
18
19
}
20
然后在我们的Servlet中可以采用如下实现。
1
@Singleton
2
public
class
HelloWorldServlet
extends
HttpServlet{
3
private
static
final
long
serialVersionUID
=
1L
;
4
@Inject
5
private
Injector inj;
6
@Override
7
protected
void
doGet(HttpServletRequest req, HttpServletResponse resp)
throws
ServletException,
8
IOException {
9
inj.getInstance(HelloWorld.
class
).execute();
10
inj.getInstance(HelloWorld.
class
).execute();
11
}
12
}
这里我们自动注入Injector对象,然后通过Inject对象获取我们的服务,注意我们没有将HttpServletRequest和HttpServletResponse手动注入到我们的服务中。
好了,我们再来看看输出。可以看到我们的对象是与request绑定,同一个request使用的同一个HelloWorld服务,不同的request那么是同一个session获取的服务也是不一样的。

老实说,Guice关于WEB的东西其实大概就这么多。其它的关于多规则匹配顺序,正则表达式匹配等等其实不谈也罢,都很弱,用处不大。
3.3 整合Struts 2
Guice可以与Struts 2整合,当然了理论上可以与其它MVC框架整合,只是Guice官方提供了一个Struts 2的插件。
首先看看依赖的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只是为了记录日志方便而已。
首先定义一个服务,服务很简单输出服务器的状态。
1
public
interface
Service {
2
3
String getStatus();
4
}
5
1
public
class
ServiceImpl
implements
Service {
2
3
public
String getStatus() {
4
return
"
I'am running.
"
;
5
}
6
}
7
然后写一个Module绑定服务及其实现,当然如果偷懒可以使用@ImplementedBy,这里为了说明如果在配置文件中配置Module,所以单写一个Module。
1
public
class
ServiceModule
implements
Module {
2
@Override
3
public
void
configure(Binder binder) {
4
binder.bind(Service.
class
).to(ServiceImpl.
class
);
5
}
6
}
然后写一个SessionScope级别的对象绑定访问次数。
1
@SessionScoped
2
public
class
AccessCounter {
3
4
private
AtomicInteger count
=
new
AtomicInteger(
0
);
5
6
public
int
visit() {
7
return
count.incrementAndGet();
8
}
9
10
@Override
11
public
String toString() {
12
return
String.format(
"
AccessCounter#%s:%d
"
,
this
.hashCode(), count.get());
13
}
14
}
15
好了,我们的Servlet出场了。
1
package
cn.imxylz.study.guice.web.struts2;
2
3
import
com.google.inject.Inject;
4
5
public
class
AccessStatusAction {
6
7
final
AccessCounter counter;
8
final
Service service;
9
String message;
10
11
@Inject
12
public
AccessStatusAction(AccessCounter counter, Service service) {
13
this
.counter
=
counter;
14
this
.service
=
service;
15
}
16
17
public
String execute() {
18
return
"
success
"
;
19
}
20
21
public
int
getCount() {
22
return
counter.visit();
23
}
24
25
public
String getStatus() {
26
return
service.getStatus();
27
}
28
29
public
String getMessage() {
30
return
message;
31
}
32
33
public
void
setMessage(String message) {
34
this
.message
=
message;
35
}
36
}
37
可以看到我们很简单的服务又被Guice给入侵了,所以说Guice对我们业务逻辑的侵入是很大,估计这也是很多程序员不愿意推广Guice的一个原因吧。
要写的Java代码就这么多,剩下的就是一堆的配置了。
首先web.xml中配置struts2的filter,注意这里我们没必要再配置一个guice的listener了,因为在guice的struts2的插件中已经配置一个ServletModule了。
1
<?
xml version="1.0" encoding="UTF-8"
?>
2
<
web-app
xmlns
="http://java.sun.com/xml/ns/javaee"
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
3
xsi:schemaLocation
="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
4
version
="2.5"
>
5
6
<
display-name
>
guice
</
display-name
>
7
<
description
>
xylz study project - guice
</
description
>
8
9
<
filter
>
10
<
filter-name
>
guiceFilter
</
filter-name
>
11
<
filter-class
>
com.google.inject.servlet.GuiceFilter
</
filter-class
>
12
</
filter
>
13
<
filter-mapping
>
14
<
filter-name
>
guiceFilter
</
filter-name
>
15
<
url-pattern
>
/*
</
url-pattern
>
16
</
filter-mapping
>
17
<
filter
>
18
<
filter-name
>
struts2
</
filter-name
>
19
<
filter-class
>
org.apache.struts2.dispatcher.FilterDispatcher
20
</
filter-class
>
21
</
filter
>
22
<
filter-mapping
>
23
<
filter-name
>
struts2
</
filter-name
>
24
<
url-pattern
>
/*
</
url-pattern
>
25
</
filter-mapping
>
26
<!--
listener>
27
<listener-class>cn.imxylz.study.guice.web.MyGuiceServletContextListener</listener-class>
28
</listener
-->
29
</
web-app
>
30
下面该配置struts.xml了。
1
<!
DOCTYPE struts PUBLIC
2
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
3
"http://struts.apache.org/dtds/struts-2.0.dtd"
>
4
5
<
struts
>
6
7
<
constant
name
="guice.module"
value
="cn.imxylz.study.guice.web.struts2.ServiceModule"
/>
8
9
<
package
name
="default"
extends
="struts-default"
>
10
<
action
name
="access-status"
11
class
="cn.imxylz.study.guice.web.struts2.AccessStatusAction"
>
12
<
result
>
access-status.jsp
</
result
>
13
</
action
>
14
</
package
>
15
16
</
struts
>
17
在这里先配置我们的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页面。
1
<%
@ taglib prefix
=
"
s
"
uri
=
"
/struts-tags
"
%>
2
3
<
html
>
4
<
body
>
5
<
h1
>
Access Status
</
h1
>
6
<
h3
><
b
>
Access in this session:
</
b
>
7
<
s:property
value
="count"
/></
h3
>
8
9
<
h3
><
b
>
Status:
</
b
>
10
<
s:property
value
="status"
/></
h3
>
11
12
<
h3
><
b
>
Message:
</
b
>
13
<
s:property
value
="message"
/></
h3
>
14
<
h4
>
<%
=
"
sessionId=
"
+
session.getId()
%>
</
h4
>
15
</
body
>
16
</
html
>
17
所有的工作就完成了,我们看看浏览器中的渲染结果。

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