简介 我见过许多项目开发者实现自己专有的MVC框架。这些开发者并不是因为想实现不同于Struts的某些功能,而是还没有意识到怎么去扩展Struts。通过开发自己的MVC框架,你可以掌控全局,但同时这也意味着你必须付出很大的代价;在项目计划很紧的情况下也许根本就不可能实现。
Struts不但功能强大也易于扩展。你可以通过三种方式来扩展Struts:
1.PlugIn:在应用启动或关闭时须执行某业务逻辑,创建你自己的PlugIn类
2.RequestProcessor:在请求处理阶段一个特定点欲执行某业务逻辑,创建你自己的RequestProcessor。例如:你想继承RequestProcessor来检查用户登录及在执行每个请求时他是否有权限执行某个动作。
3.ActionServlet:在应用启动或关闭或在请求处理阶段欲执行某业务逻辑,继承ActionServlet类。但是必须且只能在PligIn和RequestProcessor都不能满足你的需求时候用。
本文会列举一个简单的Struts应用来示范如何使用以上三种方式扩展Struts。在本文末尾资源区有每种方式的可下载样例源代码。Struts Validation 框架和 Tiles 框架是最成功两个的Struts扩展例子。
我是假设读者已经熟悉Struts框架并知道怎样使用它创建简单的应用。如想了解更多有关Struts的资料请参见资源区。
PlugIn 根据Struts文档,“PlugIn是一个须在应用启动和关闭时需被通知的模块定制资源或服务配置包”。这就是说,你可以创建一个类,它实现PlugIn的接口以便在应用启动和关闭时做你想要的事。
假如创建了一个web应用,其中使用Hibernate做为持久化机制;当应用一启动,就需初始化Hinernate,这样在web应用接收到第一个请求时,Hibernate已被配置完毕并待命。同时在应用关闭时要关闭Hibernate。跟着以下两步可以实现Hibernate PlugIn的需求。
1.创建一个实现PlugIn接口的类,如下:
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接口的类必须是实现以下两个方法:
init() 和destroy().。在应用启动时init()被调用,关闭destroy()被调用。Struts允许你传入初始参数给你的PlugIn类;为了传入参数你必须在PlugIn类里为每个参数创建一个类似JavaBean形式的setter方法。在HibernatePlugIn类里,欲传入configFile的名字而不是在应用里将它硬编码进去
2.在struts-condig.xml里面加入以下几行告知Struts这个新的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接口类的全名。为每一个初始化传入PlugIn类的初始化参数增加一个<set-property>元素。在这个例子里,传入config文档的名称,所以增加了一个config文档路径的<set-property>元素。
Tiles和Validator框架都是利用PlugIn给初始化读入配置文件。另外两个你还可以在PlugIn类里做的事情是:
假如应用依赖于某配置文件,那么可以在PlugIn类里检查其可用性,假如配置文件不可用则抛出ServletException。这将导致ActionServlet不可用。
PlugIn接口的init()方法是你改变ModuleConfig方法的最后机会,ModuleConfig方法是描述基于Struts模型静态配置信息的集合。一旦PlugIn被处理完毕,Struts就会将ModuleCOnfig冻结起来。
请求是如何被处理的 ActionServlet是Struts框架里唯一一个Servlet,它负责处理所有请求。它无论何时收到一个请求,都会首先试着为现有请求找到一个子应用。一旦子应用被找到,它会为其生成一个RequestProcessor对象,并调用传入HttpServletRequest和HttpServletResponse为参数的process()方法。
大部分请处理都是在RequestProcessor.process()发生的。Process()方法是以模板方法(Template Method)的设计模式来实现的,其中有完成request处理的每个步骤的方法;所有这些方法都从process()方法顺序调用。例如,寻找当前请求的ActionForm类和检查当前用户是否有权限执行action mapping都有几个单独的方法。这给我们提供了极大的弹性空间。Struts的RequestProcessor对每个请求处理步骤都提供了默认的实现方法。这意味着,你可以重写你感兴趣的方法,而其余剩下的保留默认实现。例如,Struts默认调用request.isUserInRole()检查用户是否有权限执行当前的ActionMapping,但如果你需要从数据库中查找,那么你要做的就是重写processRoles()方法,并根据用户角色返回true 或 false。
首先我们看一下process()方法的默认实现方式,然后我将解释RequestProcessor类里的每个默认的方法,以便你决定要修改请求处理的哪一部分。
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(): 在这个方法中,Struts读取request以找出contentType是否为multipart/form-data。假如是,则解析并将其打包成一个实现HttpServletRequest的包。当你成生一个放置数据的HTML FORM时,request的contentType默认是application/x-www-form-urlencoded。但是如果你的form的input类型是FILE-type允许用户上载文件,那么你必须把form的contentType改为multipart/form-data。如这样做