用struts1也有不短的日子了,对于它的功能也有了一定的理解。基于此,抱着学习的态度,我计划在空闲时间来系统的看下struts1的源码。之所以说系统,是因为之前断断续续的也看过一些,但限于当时对struts1的了解,体会得可能还不深入,所以总是容易忘记看过的东西。但现在来读 struts1的源码,应该会更加合适一些,有几个方面的因素,而这几个因素,我觉得也可以用在阅读其他项目的源码上:
1、首先,我对struts1的功能有了一定的了解,虽然还不够精通,但对其整体的框架和体系是有一定认知的;
2、其次,能够区分struts1的各功能模块,这样在阅读源码时就会有的放矢,各个击破了。
struts1工作流程
先让我们来看看struts1的工作流程,从宏观上对struts1的功能有个认知。
从网上找了两张图:
struts1的体系结构图:
struts1的工作流程图:
1、初始化:struts框架的总控制器ActionServlet是一个Servlet,它在web.xml中配置成自动启动的
Servlet,在启动时总控制器会读取配置文件(struts-config.xml)的配置信息,为struts
中不同的模块初始化相应的对象。
2、发送请求:用户提交表单或通过URL向WEB服务器提交请求,请求的数据用HTTP协议传给web服务器。
3、form填充:struts的总控制器ActionServlet在用户提交请求时将数据放到对应的form对象中的成员
变量中。
4、派发请求:控制器根据配置信息对象ActionConfig将请求派发到具体的Action,对应的formBean一并
传给这个Action中的excute()方法。
5、处理业务:Action一般只包含一个excute()方法,它负责执行相应的业务逻辑(调用其它的业务模块)
完毕后返回一个ActionForward对象。服务器通过ActionForward对象进行转发工作。
6、返回响应:Action将业务处理的不同结果返回一个目标响应对象给总控制器。
7、查找响应:总控制器根据Action处理业务返回的目标响应对象,找到对应的资源对象,一般情况下
为jsp页面。
8、响应用户:目标响应对象将结果传递给资源对象,将结果展现给用户。
struts1初始化
下面开始我们的源码阅读之旅,首先,对struts1的初始化源码进行阅读。
web容器在初始化或者初始化完成后,会在适当的时候初始化servlet。struts1中的ActionServlet类就是一个servlet,其会被web容器进行调用。
首先会调用init()方法,该方法会执行如下动作:
1、initInternal()
初始化struts内部使用的消息资源。
2、initOther()
2.1 读取初始化参数config,如果在web.xml中对该参数进行了设置,则将ActionServlet类中的config字段设置上对应的值。该值记录的是默认情况下,struts配置文件的路径。
2.2 读取初始化参数convertNull,如果设置为true(可为true、yes、on、y、1),则将原始类型的封装类默认值设置为null。
3、initServlet()
利用Digester组件解析web.xml,将当前servlet对应的<servlet-mapping>内容加载到ActionServlet中。关键代码如下:
- protected void initServlet() throws ServletException {
- // Remember our servlet name
- this.servletName = getServletConfig().getServletName();
- // Prepare a Digester to scan the web application deployment descriptor
- Digester digester = new Digester();
- digester.push(this);
- digester.setNamespaceAware(true);
- digester.setValidating(false);
- // Register our local copy of the DTDs that we can find
- for (int i = 0; i < registrations.length; i += 2) {
- URL url = this.getClass().getResource(registrations[i+1]);
- if (url != null) {
- digester.register(registrations[i], url.toString());
- }
- }
- // Configure the processing rules that we need
- digester.addCallMethod("web-app/servlet-mapping",
- "addServletMapping", 2);
- digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
- digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
- InputStream input =
- getServletContext().getResourceAsStream("/WEB-INF/web.xml");
- try {
- digester.parse(input);
- } catch (IOException e) {
- log.error(internal.getMessage("configWebXml"), e);
- throw new ServletException(e);
- } catch (SAXException e) {
- log.error(internal.getMessage("configWebXml"), e);
- throw new ServletException(e);
- } finally {
- try {
- input.close();
- } catch (IOException e) {
- log.error(internal.getMessage("configWebXml"), e);
- throw new ServletException(e);
- }
- }
- }
protected void initServlet() throws ServletException {
// Remember our servlet name
this.servletName = getServletConfig().getServletName();
// Prepare a Digester to scan the web application deployment descriptor
Digester digester = new Digester();
digester.push(this);
digester.setNamespaceAware(true);
digester.setValidating(false);
// Register our local copy of the DTDs that we can find
for (int i = 0; i < registrations.length; i += 2) {
URL url = this.getClass().getResource(registrations[i+1]);
if (url != null) {
digester.register(registrations[i], url.toString());
}
}
// Configure the processing rules that we need
digester.addCallMethod("web-app/servlet-mapping",
"addServletMapping", 2);
digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
InputStream input =
getServletContext().getResourceAsStream("/WEB-INF/web.xml");
try {
digester.parse(input);
} catch (IOException e) {
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
} catch (SAXException e) {
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
} finally {
try {
input.close();
} catch (IOException e) {
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
}
}
}
在解析时,会调用ActionServlet类中的addServletMapping()方法,该方法会根据当前servlet对应servletName去匹配对应的url-pattern。
4、initModuleConfigFactory()
初始化创建模块配置对象的工厂类,将初始化参数configFactory指定的类名赋予ModuleConfigFactory类的factoryClass字段。
5、对web.xml中配置的多个模块分别进行解析,生成不同的moduleConfig对象。主要代码如下:
- // Initialize modules as needed
- ModuleConfig moduleConfig = initModuleConfig("", config);
- initModuleMessageResources(moduleConfig);
- initModuleDataSources(moduleConfig);
- initModulePlugIns(moduleConfig);
- moduleConfig.freeze();
- Enumeration names = getServletConfig().getInitParameterNames();
- while (names.hasMoreElements()) {
- String name = (String) names.nextElement();
- if (!name.startsWith("config/")) {
- continue;
- }
- String prefix = name.substring(6);
- moduleConfig = initModuleConfig
- (prefix, getServletConfig().getInitParameter(name));
- initModuleMessageResources(moduleConfig);
- initModuleDataSources(moduleConfig);
- initModulePlugIns(moduleConfig);
- moduleConfig.freeze();
- }
// Initialize modules as needed
ModuleConfig moduleConfig = initModuleConfig("", config);
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
Enumeration names = getServletConfig().getInitParameterNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
if (!name.startsWith("config/")) {
continue;
}
String prefix = name.substring(6);
moduleConfig = initModuleConfig
(prefix, getServletConfig().getInitParameter(name));
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
}
在方法initModuleConfig()中,会根据生成一个工厂对象,该工厂会根据各模块前缀生成不同的ModuleConfig对象。
这里,很明显的使用到了抽象工厂模式。为了对抽象工厂模式有更深的理解,在这里也多说几句。
ModuleConfigFactory是一个抽象类,它有一个抽象方法createModuleConfig()供子类实现,该方法就是用来生产 ModuleConfig对象的。在Struts1中,有个默认的工厂实现类DefaultModuleConfigFactory,该类负责生产 ModuleConfigImpl对象。
ModuleConfigFactory的实现子类可以在web.xml中进行配置,通过调用setFactoryClass()和createFactory()方法,可以生成具体的工厂类。
抽象工厂模式定义:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
在生成了ModuleConfig对象后,会利用Digester组件对struts的配置文件进行解析,解析后的内容会放入ModuleConfig对象。
转载地址: http://blog.csdn.net/duwenchao1986/article/details/8464794