作者:(x)Sunil Patil
译Q?a target="_new">loryliu
版权声明Q可以Q意{载,转蝲时请务必以超链接形式标明文章原始出处和作者信息及(qing)本声?br>作?
Sunil Patil;loryliu
原文地址:
http://www.onjava.com/pub/a/onjava/2004/11/10/ExtendingStruts.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43857_Struts.html
关键词:(x) extending Struts
?/span>
?
见过许多目开发者实现自׃有的MVC框架。这些开发者ƈ不是因ؓ(f)惛_C同于Struts的某些功能,而是q没有意识到怎么L展Struts。通过
开发自qMVC框架Q你可以掌控全局Q但同时q也意味着你必M出很大的代h(hun)Q在目计划很紧的情况下也许Ҏ(gu)׃可能实现?br>
Struts不但功能强大也易于扩展。你可以通过三种方式来扩展Struts:
1.PlugInQ在应用启动或关闭时L行某业务逻辑Q创Z自己的PlugInc?br>
2.RequestProcessorQ在h处理阶段一个特定点Ʋ执行某业务逻辑Q创Z自己的RequestProcessor。例如:(x)你想l承RequestProcessor来检查用L(fng)录及(qing)在执行每个请求时他是否有权限执行某个动作?br>
3.ActionServletQ在应用启动或关闭或在请求处理阶D|执行某业务逻辑Q承ActionServletcR但是必M只能在PligIn和RequestProcessor都不能满你的需求时候用?br>
本文?x)列举一个简单的Struts应用来示范如何用以上三U方式扩展Struts。在本文末尾资源区有每种方式的可下蝲样例源代码。Struts Validation 框架?Tiles 框架是最成功两个的Struts扩展例子?br>
我是假设读者已l熟(zhn)Struts框架q知道怎样使用它创建简单的应用。如想了解更多有关Struts的资料请参见资源区?br>
PlugIn
Ҏ(gu)Struts文档Q“PlugIn是一个须在应用启动和关闭旉被通知的模块定制资源或服务配置包”。这是_(d)你可以创Z个类Q它实现PlugIn的接口以便在应用启动和关闭时做你惌的事?br>
?
如创Z一个web应用Q其中用Hibernate做ؓ(f)持久化机Ӟ当应用一启动Q就需初始化HinernateQ这样在web应用接收到第一个请?
ӞHibernate已被配置完毕q待命。同时在应用关闭时要关闭Hibernate。跟着以下两步可以实现Hibernate PlugIn的需求?br>
1.创徏一个实现PlugIn接口的类Q如下:(x)
public class HibernatePlugIn implements PlugIn{
private String configFile;
// This method will be called at application shutdown time
public void destroy() {
System.out.println("Entering HibernatePlugIn.destroy()");
//Put hibernate cleanup code here
System.out.println("Exiting HibernatePlugIn.destroy()");
}
//This method will be called at application startup time
public void init(ActionServlet actionServlet, ModuleConfig config)
throws ServletException {
System.out.println("Entering HibernatePlugIn.init()");
System.out.println("Value of init parameter " +
getConfigFile());
System.out.println("Exiting HibernatePlugIn.init()");
}
public String getConfigFile() {
return name;
}
public void setConfigFile(String string) {
configFile = string;
}
}
实现PlugIn接口的类必须是实C下两个方法:(x)
init()
和destroy().。在应用启动时init()被调用,关闭destroy()被调用。Struts允许你传入初始参数给你的PlugInc;Z?
入参C必须在PlugInc里为每个参数创Z个类似JavaBean形式的setterҎ(gu)。在HibernatePlugInc里Q欲传入
configFile的名字而不是在应用里将它硬~码q去
2.在struts-condig.xml里面加入以下几行告知Strutsq个新的PlugIn
<struts-config>
...
<!-- Message Resources -->
<message-resources parameter=
"sample1.resources.ApplicationResources"/>
<!-- Declare your plugins -->
<plug-in className="com.sample.util.HibernatePlugIn">
<set-property property="configFile"
value="/hibernate.cfg.xml"/>
</plug-in>
</struts-config>
ClassName
属性是实现PlugIn接口cȝ全名。ؓ(f)每一个初始化传入PlugIncȝ初始化参数增加一?lt;set-property>元素。在q个例子
里,传入config文档的名Uͼ所以增加了一个config文档路径?lt;set-property>元素?br>
Tiles和Validator框架都是利用PlugInl初始化d配置文g。另外两个你q可以在PlugInc里做的事情是:(x)
假如应用依赖于某配置文gQ那么可以在PlugInc里(g)查其可用性,假如配置文g不可用则抛出ServletException。这导致ActionServlet不可用?br>
PlugIn接口的init()Ҏ(gu)是你改变ModuleConfigҎ(gu)的最后机?x),ModuleConfigҎ(gu)是描q基于Struts模型静态配|信息的集合。一旦PlugIn被处理完毕,Struts׃(x)ModuleCOnfigȝh?br>
h是如何被处理?/span>
ActionServlet
是Struts框架里唯一一个ServletQ它负责处理所有请求。它无论何时收到一个请求,都会(x)首先试着为现有请求找C个子应用。一旦子应用被找刎ͼ
它会(x)为其生成一个RequestProcessor对象Qƈ调用传入HttpServletRequest和HttpServletResponse为参
数的process()Ҏ(gu)?br>
大部分请处理都是在RequestProcessor.process()发生的。Process()Ҏ(gu)
是以模板Ҏ(gu)QTemplate
MethodQ的设计模式来实现的Q其中有完成request处理的每个步骤的Ҏ(gu)Q所有这些方法都从process()Ҏ(gu)序调用。例如,L当前?
求的ActionFormcd(g)查当前用h否有权限执行action
mapping都有几个单独的方法。这l我们提供了极大的弹性空间。Struts的RequestProcessorҎ(gu)个请求处理步骤都提供了默认的?
现方法。这意味着Q你可以重写你感兴趣的方法,而其余剩下的保留默认实现。例如,Struts默认调用request.isUserInRole()(g)?
用户是否有权限执行当前的ActionMappingQ但如果你需要从数据库中查找Q那么你要做的就是重写processRoles()Ҏ(gu)QƈҎ(gu)用户
角色q回true ?false?br>
首先我们看一下process()Ҏ(gu)的默认实现方式,然后我将解释RequestProcessorc里的每个默认的Ҏ(gu)Q以便你军_要修改请求处理的哪一部分?br>
public void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Wrap multipart requests with a special wrapper
request = processMultipart(request);
// Identify the path component we will
// use to select a mapping
String path = processPath(request, response);
if (path == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Processing a '" + request.getMethod() +
"' for path '" + path + "'");
}
// Select a Locale for the current user if requested
processLocale(request, response);
// Set the content type and no-caching headers
// if requested
processContent(request, response);
processNoCache(request, response);
// General purpose preprocessing hook
if (!processPreprocess(request, response)) {
return;
}
// Identify the mapping for this request
ActionMapping mapping =
processMapping(request, response, path);
if (mapping == null) {
return;
}
// Check for any role required to perform this action
if (!processRoles(request, response, mapping)) {
return;
}
// Process any ActionForm bean related to this request
ActionForm form =
processActionForm(request, response, mapping);
processPopulate(request, response, form, mapping);
if (!processValidate(request, response, form, mapping)) {
return;
}
// Process a forward or include specified by this mapping
if (!processForward(request, response, mapping)) {
return;
}
if (!processInclude(request, response, mapping)) {
return;
}
// Create or acquire the Action instance to
// process this request
Action action =
processActionCreate(request, response, mapping);
if (action == null) {
return;
}
// Call the Action instance itself
ActionForward forward =
processActionPerform(request, response,
action, form, mapping);
// Process the returned ActionForward instance
processForwardConfig(request, response, forward);
}
1、processMultipart(): ?
q个Ҏ(gu)中,Strutsdrequest以找出contentType是否为multipart/form-data。假如是Q则解析q将其打包成一
个实现HttpServletRequest的包。当你成生一个放|数据的HTML
FORMӞrequest的contentType默认是application/x-www-form-urlencoded。但是如果你的form
的inputcd是FILE-type允许用户上蝲文gQ那么你必须把form的contentType改ؓ(f)multipart/form-data。如
q样做,你永q不能通过HttpServletRequest的getParameter()来读取用h交的form|你必M
InputStream的Ş式读取requestQ然后解析它得到倹{?br>
2、processPath(): 在这个方法中QStruts读取request的URI以判断用来得到ActionMapping元素的\径?br>
3、processLocale():
在这个方法中QStruts得到当前request的LocaleQLocale假如被配|,作?
org.apache.struts.action.LOCALE属性的D存入HttpSession。这个方法的附作用是HttpSession?x)?
创徏。假如你不想此事发生Q可在struts-config.xml
文g里ControllerConfig的local属性设|ؓ(f)false,如下Q?br><controller>
<set-property property="locale" value="false"/>
</controller>
4、processContent()Q?/b>通过调用response.setContentType()讄response的contentType。这个方法首先会(x)试着的得到配|在struts-config.xml里的contentType。默认ؓ(f)text/htmlQ重写方法如下:(x)
<controller>
<set-property property="contentType" value="text/plain"/>
</controller>
5、processNoCache()Q?/b>Strutsؓ(f)每个response的设|以下三个headerQ假如已在struts 的config.xml配|ؓ(f)no-cache?br>response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 1);
假如你想讄为no-cache headerQ在struts-config.xml中加如以下几?br><controller>
<set-property property="noCache" value="true"/>
</controller>
6、processPreprocess()Q?/b>q是一个一般意义的预处理hookQ其可被子类重写。在RequestProcessor里的实现什么都没有做,Lq回true。如此方法返回false?x)中断请求处理?br>
7、processMapping():q个Ҏ(gu)?x)利用path信息扑ֈActionMapping对象。ActionMapping对象在struts-config.xml file文g里表CZؓ(f)<action>
<action path="/newcontact" type="com.sample.NewContactAction"
name="newContactForm" scope="request">
<forward name="sucess" path="/sucessPage.do"/>
<forward name="failure" path="/failurePage.do"/>
</action>
ActionMapping元素包含了如Actioncȝ名称?qing)在h中用到的ActionForm的信息,另外q有配置在当前ActionMapping的里的ActionForwards信息?br>
8、processRoles(): Struts的web 应用安全提供了一个认证机制。这是_(d)一旦用L(fng)录到容器QStruts的processRoles()Ҏ(gu)通过调用request.isUserInRole()可以(g)查他是否有权限执行给定的ActionMapping?br> <action path="/addUser" roles="administrator"/>
假如你有一个AddUserActionQ限制只有administrator权限的用h能新d用户。你所要做的就是在AddUserAction 的action元素里添加一个gؓ(f)administrator的role属性?br>
9、processActionForm()Q?/b>每个ActionMapping都有一个与它关联的ActionFormcRstruts在处理ActionMappingӞ他会(x)?lt;action>里name属性找到相关的ActionFormcȝ倹{?br><form-bean name="newContactForm"
type="org.apache.struts.action.DynaActionForm">
<form-property name="firstName"
type="java.lang.String"/>
<form-property name="lastName"
type="java.lang.String"/>
</form-bean>
在这个例子里Q首先会(x)(g)查org.apache.struts.action.DynaActionFormcȝ对象是否在request 范围内。如是,则用它Q否则创Z个新的对象ƈ在request范围内设|它?br>
10、processPopulate():Q?/b>在这个方法里QStruts匹配的request parameters值填入ActionFormcȝ实例变量中?br>
11、processValidate()Q?/b>Struts调用ActionForm的validate()Ҏ(gu)。假如validate()q回ActionErrorsQStruts用戯{到由<action>里的input属性标C的面?br>
12、processForward() and processInclude()Q?/b>在这两个Ҏ(gu)里,Struts(g)?lt;action>元素的forward和include属性的|假如有配|,则把forward和include h攑֜配置的页面内?br><action forward="/Login.jsp" path="/loginInput"/>
<action include="/Login.jsp" path="/loginInput"/>
?
可以从他们的名字看出其不同之处。processForward()调用RequestDispatcher.forward(),Q?
processInclude()调用RequestDispatcher.include()。假如你同时配置了orward 和include
属性,StrutsM(x)调用forwardQ因为forward,是首先被处理的?br>
13、processActionCreate()Q?/b>q个Ҏ(gu)?lt;action>的type属性得到ActioncdQƈ创徏q回它的实例。在q里例子中struts创Z个com.sample.NewContactActioncȝ实例?br>
14、processActionPerform()Q?/b>q个Ҏ(gu)调用Action cȝexecute()Ҏ(gu)Q其中有你写入的业务逻辑?br>
15、processForwardConfig()Q?/b>Actioncȝexecute()会(x)q回一个ActionForwardcd的对象,指出哪一面展C给用户。因此Strutsؓ(f)q个面创徏RequestDispatchetQ然后再调用RequestDispatcher.forward()Ҏ(gu)?br>
?
上列出的Ҏ(gu)解释了RequestProcessor在请求处理的每步默认实现?qing)各个步骤执行的序。正如你所见,RequestProcessor很有
Ҏ(gu),它允怽通过讄<controller>里的属性来配置它。例如,假如你的应用生成XML内容而不是HTMLQ你可以通过讄
controller的某个属性来通知Struts?br>
创徏你自qRequestProcessor
?
以上内容我们已经明白了RequestProcessor的默认实现是怎样工作的,现在我将通过创徏你自qRequestProcessor.展示一?
怎样自定义RequestProcessor的例子。ؓ(f)了演C创Z个自定义RequestProcessorQ我修改例子实C下连个业务需求:(x)
我们要创Z个ContactImageActionc,它将生成images而不是一般的HTMl面
在处理这个请求之前,通过(g)查session里的userName属性来认用户是否d。假如此属性没有被扑ֈQ则用戯{到登录页面?br>
分两步来实现以上q个业务需求?br>创徏你自qCustomRequestProcessorc,它将l承RequestProcessorc,如下Q?br>
public class CustomRequestProcessor
extends RequestProcessor {
protected boolean processPreprocess (
HttpServletRequest request,
HttpServletResponse response) {
HttpSession session = request.getSession(false);
//If user is trying to access login page
// then don't check
if( request.getServletPath().equals("/loginInput.do")
|| request.getServletPath().equals("/login.do") )
return true;
//Check if userName attribute is there is session.
//If so, it means user has allready logged in
if( session != null &&
session.getAttribute("userName") != null)
return true;
else{
try{
//If no redirect user to login Page
request.getRequestDispatcher
("/Login.jsp").forward(request,response);
}catch(Exception ex){
}
}
return false;
}
protected void processContent(HttpServletRequest request,
HttpServletResponse response) {
//Check if user is requesting ContactImageAction
// if yes then set image/gif as content type
if( request.getServletPath().equals("/contactimage.do")){
response.setContentType("image/gif");
return;
}
super.processContent(request, response);
}
}
在CustomRequestProcessor cȝprocessPreprocessҎ(gu)里,(g)查session的userName属性,假如没有扑ֈQ将用户转到d面?br>
?
于生images作ؓ(f)ContactImageActioncȝ输出Q必要重写processContentҎ(gu)。首先检查其request是否h
/contactimage路径Q如是则讄contentType为image/gifQ否则ؓ(f)text/html?br>
加入以下几行代码到sruts-config.xml文g里的<action-mapping>后面Q告知Struts QCustomRequestProcessor应该被用作RequestProcessorc?br>
<controller>
<set-property property="processorClass"
value="com.sample.util.CustomRequestProcessor"/>
</controller>
?
注意Q假如你只是很少生成contentType不是text/html输出的Actionc,重写processContent()没有问题。如不是
q种情况Q你必须创徏一个Struts子系l来处理生成image Action的请求ƈ讄contentType为image/gif
Title框架使用自己的RequestProcessor来装饰Struts生成的输出?br>
ActionServlet
假如你仔l研IStruts web应用的web.xml文gQ它看上dq样Q?br><web-app >
<servlet>
<servlet-name>action=</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<!-- All your init-params go here-->
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app >
q?
是_(d)ActionServlet负责处理所有发向Struts的请求。你可以创徏ActionServlet的一个子c,假如你想在应用启动和关闭?
或每ơ请求时做某些事情。但是你必须在承ActionServletcd创徏PlugIn ?RequestProcessor。在Servlet
1.1前,Title框架是基于承ActionServletcL装饰一个生成的response。但?.1开始,׃?
TilesRequestProcessorcR?br>
l论
开发你自己的MVC模型是一个很大的军_——你必须考虑开发和l护代码的时间和资源。Struts是一个功能强大且E_的框Ӟ你可以修改它以其满你大部分的业务需求?br>
另一斚wQ也不要L地决定扩展Struts。假如你在RequestProcessor里放入一些低效率的代码,q些代码在每次h时执行ƈ大大地降低整个应用的效率。当然L创徏你自qMVC框架比扩展Struts更好的情c(din)?br>
资源
下蝲本文源码Q[下蝲文g]
Struts主页
"Jakarta Struts框架介绍"
"学习(fn)Jakarta Struts 1.1"
Sunil Pail已从事J2EE四年Q现今与IBM实验室合作?img src ="http://www.aygfsteel.com/juhongtao/aggbug/25994.html" width = "1" height = "1" />
]]>