简介
我见过许多项目开发者实现自己专有的MVC框架这些开发者并不是因为想实现不同于Struts的某些功能而是还没有意识到怎么去扩展Struts通过开发自己的MVC框架你可以掌控全局但同时这也意味着你必须付出很大的代价在项目计划很紧的情况下也许根本就不可能实现
Struts不但功能强大也易于扩展你可以通过三种方式来扩展Struts:
PlugIn在应用启动或关闭时须执行某业务逻辑创建你自己的PlugIn类
RequestProcessor在请求处理阶段一个特定点欲执行某业务逻辑创建你自己的RequestProcessor例如你想继承RequestProcessor来检查用户登录及在执行每个请求时他是否有权限执行某个动作
ActionServlet在应用启动或关闭或在请求处理阶段欲执行某业务逻辑继承ActionServlet类但是必须且只能在PligIn和RequestProcessor都不能满足你的需求时候用
本文会列举一个简单的Struts应用来示范如何使用以上三种方式扩展Struts在本文末尾资源区有每种方式的可下载样例源代码Struts Validation 框架和 Tiles 框架是最成功两个的Struts扩展例子
我是假设读者已经熟悉Struts框架并知道怎样使用它创建简单的应用如想了解更多有关Struts的资料请参见资源区
PlugIn
根据Struts文档PlugIn是一个须在应用启动和关闭时需被通知的模块定制资源或服务配置包这就是说你可以创建一个类它实现PlugIn的接口以便在应用启动和关闭时做你想要的事
假如创建了一个web应用其中使用Hibernate做为持久化机制当应用一启动就需初始化Hinernate这样在web应用接收到第一个请求时Hibernate已被配置完毕并待命同时在应用关闭时要关闭Hibernate跟着以下两步可以实现Hibernate PlugIn的需求
创建一个实现PlugIn接口的类如下
public class HibernatePlugIn implements PlugIn{private String configFile;// This method will be called at application shutdown timepublic void destroy() {Systemoutprintln(Entering HibernatePlugIndestroy());//Put hibernate cleanup code hereSystemoutprintln(Exiting HibernatePlugIndestroy());}//This method will be called at application startup timepublic void init(ActionServlet actionServlet ModuleConfig config)throws ServletException {Systemoutprintln(Entering HibernatePlugIninit());Systemoutprintln(Value of init parameter +getConfigFile());Systemoutprintln(Exiting HibernatePlugIninit());}public String getConfigFile() {return name;}public void setConfigFile(String string) {configFile = string;}}
实现PlugIn接口的类必须是实现以下两个方法
init() 和destroy()在应用启动时init()被调用关闭destroy()被调用Struts允许你传入初始参数给你的PlugIn类为了传入参数你必须在PlugIn类里为每个参数创建一个类似JavaBean形式的setter方法在HibernatePlugIn类里欲传入configFile的名字而不是在应用里将它硬编码进去
在strutscondigxml里面加入以下几行告知Struts这个新的PlugIn
<strutsconfig><! Message Resources ><messageresources parameter=sampleresourcesApplicationResources/><! Declare your plugins ><plugin className=comsampleutilHibernatePlugIn><setproperty property=configFile value=/hibernatecfgxml/></plugin></strutsconfig>
ClassName属性是实现PlugIn接口类的全名为每一个初始化传入PlugIn类的初始化参数增加一个<setproperty>元素在这个例子里传入config文档的名称所以增加了一个config文档路径的<setproperty>元素
Tiles和Validator框架都是利用PlugIn给初始化读入配置文件另外两个你还可以在PlugIn类里做的事情是
假如应用依赖于某配置文件那么可以在PlugIn类里检查其可用性假如配置文件不可用则抛出ServletException这将导致ActionServlet不可用
PlugIn接口的init()方法是你改变ModuleConfig方法的最后机会ModuleConfig方法是描述基于Struts模型静态配置信息的集合一旦PlugIn被处理完毕Struts就会将ModuleCOnfig冻结起来
请求是如何被处理的
ActionServlet是Struts框架里唯一一个Servlet它负责处理所有请求它无论何时收到一个请求都会首先试着为现有请求找到一个子应用一旦子应用被找到它会为其生成一个RequestProcessor对象并调用传入HttpServletRequest和HttpServletResponse为参数的process()方法
大部分请处理都是在RequestProcessorprocess()发生的Process()方法是以模板方法(Template Method)的设计模式来实现的其中有完成request处理的每个步骤的方法所有这些方法都从process()方法顺序调用例如寻找当前请求的ActionForm类和检查当前用户是否有权限执行action mapping都有几个单独的方法这给我们提供了极大的弹性空间Struts的RequestProcessor对每个请求处理步骤都提供了默认的实现方法这意味着你可以重写你感兴趣的方法而其余剩下的保留默认实现例如Struts默认调用requestisUserInRole()检查用户是否有权限执行当前的ActionMapping但如果你需要从数据库中查找那么你要做的就是重写processRoles()方法并根据用户角色返回true 或 false
首先我们看一下process()方法的默认实现方式然后我将解释RequestProcessor类里的每个默认的方法以便你决定要修改请求处理的哪一部分
public void process(HttpServletRequest requestHttpServletResponse response)throws IOException ServletException {// Wrap multipart requests with a special wrapperrequest = processMultipart(request);// Identify the path component we will// use to select a mappingString path = processPath(request response);if (path == null) {return;}if (logisDebugEnabled()) {logdebug(Processing a + requestgetMethod() + for path + path + );}// Select a Locale for the current user if requestedprocessLocale(request response);// Set the content type and nocaching headers// if requestedprocessContent(request response);processNoCache(request response);// General purpose preprocessing hookif (!processPreprocess(request response)) {return; }// Identify the mapping for this requestActionMapping mapping =processMapping(request response path);if (mapping == null) {return;}// Check for any role required to perform this actionif (!processRoles(request response mapping)) {return;}// Process any ActionForm bean related to this requestActionForm 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 mappingif (!processForward(request response mapping)) {return;}if (!processInclude(request response mapping)) {return;}// Create or acquire the Action instance to// process this requestAction action =processActionCreate(request response mapping);if (action == null) {return;}// Call the Action instance itselfActionForward forward =processActionPerform(request responseaction form mapping);// Process the returned ActionForward instanceprocessForwardConfig(request response forward);}
processMultipart(): 在这个方法中Struts读取request以找出contentType是否为multipart/formdata假如是则解析并将其打包成一个实现HttpServletRequest的包当你成生一个放置数据的HTML FORM时request的contentType默认是application/xwwwformurlencoded但是如果你的form的input类型是FILEtype允许用户上载文件那么你必须把form的contentType改为multipart/formdata如这样做