struts1源码分析(三)请求处理主线
2015-07-15 16:32
267 查看
在初始化主线一文中,我们详细分析了框架的初始化过程。本文将从Struts的另外一条主线出发,分析框架的实现原理。上文的初始化主线是一个铺垫,它将框架运行时需要的数据和组件准备完毕,为请求处理主线打下基础。本文基于Struts1.2.8版本,1.3.x系列的差异性将另文说明。
[请求处理接口]
在整体概览和核心组件一文中,我们提到了Struts框架抽象出统一的业务逻辑基类,用户可以继承该类并覆盖请求处理方法,实现自己的业务逻辑。我们再来看一下请求处理方法签名:
Java代码
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
}
与原生的servlet生命周期方法service()相比,该方法增加了三个元素: ActionMapping, ActionForm和ActionForward。由于引入了新的编程元素,使得用户在业务处理上比原生的servlet更加方便。该方法也提现出框架对变化的业务处理逻辑进行抽象,框架的请求处理主线也是围绕该方法展开,包括方法参数的准备和返回值的处理。对变化的进行抽象,对不变的进行固化,这是框架的精髓所在。
[问题]
在分析请求处理主线之前,我们先来思考几个问题。
1. 请求处理过程中哪些东西需要被固化?
2. 如何构建请求处理方法中需要的参数对象(ActionMapping和ActionForm)?
3. 如何对请求处理返回值ActionForward进行处理?
[处理流程]
在整体概览和核心组件一文中,我们介绍了请求处理的核心流程,对各组件功能已经有了整体上的认识。Struts请求处理核心步骤如下:
Struts框架包含默认模块和用户自定义模块,ActionServlet在接收请求后获取当前请求对应的模块信息,进而获取模块对应的请求处理类RequestProcessor,并将请求委托给RequestProcessor处理。整个请求处理过程可以分为两段:一是ActionServlet准备委托处理对象RequestProcessor, 二是RequestProcessor对请求进行处理。下面从源码角度分别解读这两段过程。
[准备委托处理对象]
在Servlet生命周期中,请求处理由生命周期方法service()完成。对于不同的请求method(如Get/Post等),service()方法内部调用method处理方法(如doGet()/doPost()等)完成请求处理。ActionServlet重写doGet()和doPost()方法,完成对Get和Post请求处理。我们看一下doGet()方法实现:
Java代码
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
该方法比较简单,只是简单调用process()方法,doPost()方法也是如此。我们再看一下process()方法实现:
Java代码
protected void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// 1. 获取当前请求对应的Module配置信息(ModuleConfig和MessageResources),并将信息存入request作用域
ModuleUtils.getInstance().selectModule(request, getServletContext());
// 2. 获取当前请求的ModuleConfig实例
ModuleConfig config = getModuleConfig(request);
// 3. 获取模块对应的处理类RequestProcessor
RequestProcessor processor = getProcessorForModule(config);
if (processor == null) {
processor = getRequestProcessor(config);
}
// 4. 将请求委托给RequestProcessor进行处理
processor.process(request, response);
}
以上过程总共分为四步,在代码注释中已经详细说明。这里需要说明一点,每个Module都有唯一的RequestProcessor实例,该实例保存在ServletContext中,使用的key为”Globals.REQUEST_PROCESSOR_KEY + 模块前缀“。如果从ServletContext获取不到该实例,就新建一个实例并保存。因为一个模块只有唯一的RequestProcessor实例,所以RequestProcessor从实现上必须无状态,才不会出现并发问题。
[RequestProcessor请求处理]
上个阶段的目标是获取请求对应的RequestProcessor实例,真正的请求处理过程由RequestProcessor的process()方法完成。该方法实现如下:
Java代码
public void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// 1. 针对multipart请求类型,使用MultipartRequestWrapper对请求进行包装
request = processMultipart(request);
// 2. 获取当前请求的path信息
String path = processPath(request, response);
if (path == null) {
return;
}
// 此处省略log信息
// 3. 从请求中获取locale信息并放入session中
processLocale(request, response);
// 4. 对响应头ContentType和NoCache设置默认值
processContent(request, response);
processNoCache(request, response);
// 5. 增加预处理钩子,可以重写该方法,增加自定义处理逻辑
if (!processPreprocess(request, response)) {
return;
}
// 6. 清除session中的ActionMessages对象,比如错误信息
this.processCachedMessages(request, response);
// 7. 根据path获取当前路径对应的ActionMapping对象
ActionMapping mapping = processMapping(request, response, path);
if (mapping == null) {
return;
}
// 8. 验证用户角色是否在允许列表中
if (!processRoles(request, response, mapping)) {
return;
}
// 9. 获取当前请求对应的ActionForm实例
ActionForm form = processActionForm(request, response, mapping);
// 10. 根据请求参数填充ActionForm实例
processPopulate(request, response, form, mapping);
// 11. 完成Form验证
if (!processValidate(request, response, form, mapping)) {
return;
}
// 12. 完成ActionMapping中forward请求处理
if (!processForward(request, response, mapping)) {
return;
}
// 13. 完成ActionMapping中include请求处理
if (!processInclude(request, response, mapping)) {
return;
}
// 14. 获取当前请求对应的Action实例
Action action = processActionCreate(request, response, mapping);
if (action == null) {
return;
}
// 15. 调用Action实例的execute()方法,完成业务逻辑处理
ActionForward forward =
processActionPerform(request, response,
action, form, mapping);
// 16. 对执行结果ActionForward进行处理
processForwardConfig(request, response, forward);
}
该方法条理清晰,可读性高,每一步的作用已经在方法注释中说明。下面对方法中几个核心对象的使用做进一步说明。
[ActionMapping]
在第2步中,从request中提取当前请求对应的path信息。该信息将用于第7步中获取path对应的ActionMapping实例。ActionMapping实例是一个请求(路径)对应的框架配置信息载体,通过获取该实例,才能拿到请求对应的ActionForm、Action和ActionForward等配置信息。
[ActionForm]
1. 第9步中,从ActionMapping中获取当前请求的ActionForm名称,然后根据ActionForm名称查找ActionForm配置,进而获取ActionForm实例。 如果没有可重用的ActionForm实例,需要根据ActionForm的类信息通过反射方式生成实例。
2. 第10步中,获取请求的参数列表,然后用这些参数值填充ActionForm中对应的属性值,通过Apache BeanUtils完成填充工作。
3. 第11步中,调用ActionForm的validate()方法完成表单验证。如果验证失败,将错误信息放入request中,结束请求处理。
[Action]
1. 第14步中,从ActionMapping中获取该请求对应的Action名称,RequestProcessor维护了所有Action实例的HashMap,通过Action名称可以获取对应的Action实例。如果没有获取到,则根据Action类信息采用反射方式生成Action实例。由于每个Action对应的实例只有一个,所以Action本身是线程不安全的,编写业务代码时要特别注意。
2. 第15步中,processActionPerform()方法内部调用Action.execute()方法,完成业务逻辑调用。
[ActionForward]
第16步中,对执行结果ActionForward进行处理。processForwardConfig()内部根据配置文件中forward的redirect属性决定采用forward跳转方式还是redirect跳转方式。
[小结]
本文从源码角度分析了Struts1的请求处理主线。ActionServlet在请求处理过程中只负责找到请求对应的RequestProcessor,然后委托给RequestProcessor进行处理;RequestProcessor通过调用ActionMapping/ActionForm/Action/ActionForward这四个核心对象完成请求处理过程。总体上看,请求处理主线逻辑清晰,可读性高。缺陷是可扩展性差,特别是RequestProcessor的处理过程只有一处预处理钩子,不能灵活定制框架处理逻辑。下文将详细分析Struts
1.3.x系列针对请求处理的优化措施。
转载请注明出处: /content/2814233.html,谢谢!
[请求处理接口]
在整体概览和核心组件一文中,我们提到了Struts框架抽象出统一的业务逻辑基类,用户可以继承该类并覆盖请求处理方法,实现自己的业务逻辑。我们再来看一下请求处理方法签名:
Java代码
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
}
与原生的servlet生命周期方法service()相比,该方法增加了三个元素: ActionMapping, ActionForm和ActionForward。由于引入了新的编程元素,使得用户在业务处理上比原生的servlet更加方便。该方法也提现出框架对变化的业务处理逻辑进行抽象,框架的请求处理主线也是围绕该方法展开,包括方法参数的准备和返回值的处理。对变化的进行抽象,对不变的进行固化,这是框架的精髓所在。
[问题]
在分析请求处理主线之前,我们先来思考几个问题。
1. 请求处理过程中哪些东西需要被固化?
2. 如何构建请求处理方法中需要的参数对象(ActionMapping和ActionForm)?
3. 如何对请求处理返回值ActionForward进行处理?
[处理流程]
在整体概览和核心组件一文中,我们介绍了请求处理的核心流程,对各组件功能已经有了整体上的认识。Struts请求处理核心步骤如下:
Struts框架包含默认模块和用户自定义模块,ActionServlet在接收请求后获取当前请求对应的模块信息,进而获取模块对应的请求处理类RequestProcessor,并将请求委托给RequestProcessor处理。整个请求处理过程可以分为两段:一是ActionServlet准备委托处理对象RequestProcessor, 二是RequestProcessor对请求进行处理。下面从源码角度分别解读这两段过程。
[准备委托处理对象]
在Servlet生命周期中,请求处理由生命周期方法service()完成。对于不同的请求method(如Get/Post等),service()方法内部调用method处理方法(如doGet()/doPost()等)完成请求处理。ActionServlet重写doGet()和doPost()方法,完成对Get和Post请求处理。我们看一下doGet()方法实现:
Java代码
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
process(request, response);
}
该方法比较简单,只是简单调用process()方法,doPost()方法也是如此。我们再看一下process()方法实现:
Java代码
protected void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// 1. 获取当前请求对应的Module配置信息(ModuleConfig和MessageResources),并将信息存入request作用域
ModuleUtils.getInstance().selectModule(request, getServletContext());
// 2. 获取当前请求的ModuleConfig实例
ModuleConfig config = getModuleConfig(request);
// 3. 获取模块对应的处理类RequestProcessor
RequestProcessor processor = getProcessorForModule(config);
if (processor == null) {
processor = getRequestProcessor(config);
}
// 4. 将请求委托给RequestProcessor进行处理
processor.process(request, response);
}
以上过程总共分为四步,在代码注释中已经详细说明。这里需要说明一点,每个Module都有唯一的RequestProcessor实例,该实例保存在ServletContext中,使用的key为”Globals.REQUEST_PROCESSOR_KEY + 模块前缀“。如果从ServletContext获取不到该实例,就新建一个实例并保存。因为一个模块只有唯一的RequestProcessor实例,所以RequestProcessor从实现上必须无状态,才不会出现并发问题。
[RequestProcessor请求处理]
上个阶段的目标是获取请求对应的RequestProcessor实例,真正的请求处理过程由RequestProcessor的process()方法完成。该方法实现如下:
Java代码
public void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// 1. 针对multipart请求类型,使用MultipartRequestWrapper对请求进行包装
request = processMultipart(request);
// 2. 获取当前请求的path信息
String path = processPath(request, response);
if (path == null) {
return;
}
// 此处省略log信息
// 3. 从请求中获取locale信息并放入session中
processLocale(request, response);
// 4. 对响应头ContentType和NoCache设置默认值
processContent(request, response);
processNoCache(request, response);
// 5. 增加预处理钩子,可以重写该方法,增加自定义处理逻辑
if (!processPreprocess(request, response)) {
return;
}
// 6. 清除session中的ActionMessages对象,比如错误信息
this.processCachedMessages(request, response);
// 7. 根据path获取当前路径对应的ActionMapping对象
ActionMapping mapping = processMapping(request, response, path);
if (mapping == null) {
return;
}
// 8. 验证用户角色是否在允许列表中
if (!processRoles(request, response, mapping)) {
return;
}
// 9. 获取当前请求对应的ActionForm实例
ActionForm form = processActionForm(request, response, mapping);
// 10. 根据请求参数填充ActionForm实例
processPopulate(request, response, form, mapping);
// 11. 完成Form验证
if (!processValidate(request, response, form, mapping)) {
return;
}
// 12. 完成ActionMapping中forward请求处理
if (!processForward(request, response, mapping)) {
return;
}
// 13. 完成ActionMapping中include请求处理
if (!processInclude(request, response, mapping)) {
return;
}
// 14. 获取当前请求对应的Action实例
Action action = processActionCreate(request, response, mapping);
if (action == null) {
return;
}
// 15. 调用Action实例的execute()方法,完成业务逻辑处理
ActionForward forward =
processActionPerform(request, response,
action, form, mapping);
// 16. 对执行结果ActionForward进行处理
processForwardConfig(request, response, forward);
}
该方法条理清晰,可读性高,每一步的作用已经在方法注释中说明。下面对方法中几个核心对象的使用做进一步说明。
[ActionMapping]
在第2步中,从request中提取当前请求对应的path信息。该信息将用于第7步中获取path对应的ActionMapping实例。ActionMapping实例是一个请求(路径)对应的框架配置信息载体,通过获取该实例,才能拿到请求对应的ActionForm、Action和ActionForward等配置信息。
[ActionForm]
1. 第9步中,从ActionMapping中获取当前请求的ActionForm名称,然后根据ActionForm名称查找ActionForm配置,进而获取ActionForm实例。 如果没有可重用的ActionForm实例,需要根据ActionForm的类信息通过反射方式生成实例。
2. 第10步中,获取请求的参数列表,然后用这些参数值填充ActionForm中对应的属性值,通过Apache BeanUtils完成填充工作。
3. 第11步中,调用ActionForm的validate()方法完成表单验证。如果验证失败,将错误信息放入request中,结束请求处理。
[Action]
1. 第14步中,从ActionMapping中获取该请求对应的Action名称,RequestProcessor维护了所有Action实例的HashMap,通过Action名称可以获取对应的Action实例。如果没有获取到,则根据Action类信息采用反射方式生成Action实例。由于每个Action对应的实例只有一个,所以Action本身是线程不安全的,编写业务代码时要特别注意。
2. 第15步中,processActionPerform()方法内部调用Action.execute()方法,完成业务逻辑调用。
[ActionForward]
第16步中,对执行结果ActionForward进行处理。processForwardConfig()内部根据配置文件中forward的redirect属性决定采用forward跳转方式还是redirect跳转方式。
[小结]
本文从源码角度分析了Struts1的请求处理主线。ActionServlet在请求处理过程中只负责找到请求对应的RequestProcessor,然后委托给RequestProcessor进行处理;RequestProcessor通过调用ActionMapping/ActionForm/Action/ActionForward这四个核心对象完成请求处理过程。总体上看,请求处理主线逻辑清晰,可读性高。缺陷是可扩展性差,特别是RequestProcessor的处理过程只有一处预处理钩子,不能灵活定制框架处理逻辑。下文将详细分析Struts
1.3.x系列针对请求处理的优化措施。
转载请注明出处: /content/2814233.html,谢谢!
相关文章推荐
- 【转】深入理解Java的接口和抽象类
- 重学java23种设计模式-提纲
- .NET调用Java写的WebService
- struts1源码分析(二)初始化主线
- java虚拟机内存分配
- struts1源码分析(一)整体概览和核心组件
- Android开发:Eclipse中SqliteManager插件使用
- eclipse Unsupported major.minor version 51.0 错误解决方案
- Spring @Transactional配置知识梳理
- java的世界
- eclipse Formatter配置文件生成过程
- 使用POI操作Excel时new XSSFWorkbook ()报错java.lang.NoSuchMethodError解决方案
- java map
- 简单的Java多线程的使用
- Java NIO 系列教程
- java泛型
- java 小议Iterator
- java基础之 创建对象的几种方式
- java高保真高性能图片缩放编码
- java高保真高性能图片缩放编码