Struts相对来说是一个比较简单的MVC框架,实现的
Service to Worker模式。研究Struts源代码从ActionServlet开始,因为它是统一的入口处理类,通常配置如下:
1 <servlet>
2 <servlet-name>action</servlet-name>
3 <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
4 <init-param>
5 <param-name>config</param-name>
6 <param-value>/WEB-INF/struts-config.xml</param-value>
7 </init-param>
8 <init-param>
9 <param-name>debug</param-name>
10 <param-value>2</param-value>
11 </init-param>
12 <init-param>
13 <param-name>application</param-name>
14 <param-value>ApplicationResources</param-value>
15 </init-param>
16 <load-on-startup>2</load-on-startup>
17 </servlet>
18 <servlet-mapping>
19 <servlet-name>action</servlet-name>
20 <url-pattern>*.do</url-pattern>
21 </servlet-mapping>
ActionServlet是一个Servlet,继承HttpServlet。先分析一个这个类的init方法,这里做了很多初始化的工作,会被容器调用,初始化代码如下:
1 public void init() throws ServletException {
2 final String configPrefix = "config/";
3 final int configPrefixLength = configPrefix.length() - 1;
4
5 // Wraps the entire initialization in a try/catch to better handle
6 // unexpected exceptions and errors to provide better feedback
7 // to the developer
8 try {
9 initInternal();
10 initOther();
11 initServlet();
12 initChain();
13
14 getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
15 initModuleConfigFactory();
16
17 // Initialize modules as needed
18 ModuleConfig moduleConfig = initModuleConfig("", config);
19
20 initModuleMessageResources(moduleConfig);
21 initModulePlugIns(moduleConfig);
22 initModuleFormBeans(moduleConfig);
23 initModuleForwards(moduleConfig);
24 initModuleExceptionConfigs(moduleConfig);
25 initModuleActions(moduleConfig);
26 moduleConfig.freeze();
27
28 Enumeration names = getServletConfig().getInitParameterNames();
29
30 while (names.hasMoreElements()) {
31 String name = (String) names.nextElement();
32
33 if (!name.startsWith(configPrefix)) {
34 continue;
35 }
36
37 String prefix = name.substring(configPrefixLength);
38
39 moduleConfig =
40 initModuleConfig(prefix,
41 getServletConfig().getInitParameter(name));
42 initModuleMessageResources(moduleConfig);
43 initModulePlugIns(moduleConfig);
44 initModuleFormBeans(moduleConfig);
45 initModuleForwards(moduleConfig);
46 initModuleExceptionConfigs(moduleConfig);
47 initModuleActions(moduleConfig);
48 moduleConfig.freeze();
49 }
50
51 this.initModulePrefixes(this.getServletContext());
52
53 this.destroyConfigDigester();
54 } catch (UnavailableException ex) {
55 throw ex;
56 } catch (Throwable t) {
57 // The follow error message is not retrieved from internal message
58 // resources as they may not have been able to have been
59 // initialized
60 log.error("Unable to initialize Struts ActionServlet due to an "
61 + "unexpected exception or error thrown, so marking the "
62 + "servlet as unavailable. Most likely, this is due to an "
63 + "incorrect or missing library dependency.", t);
64 throw new UnavailableException(t.getMessage());
65 }
66 }
初始化的工作基本见名知义。当收到请求时,ActionServlet的doGet()或doPost()方法都调用process()方法来处理请求:
1 protected void process(HttpServletRequest request,
2 HttpServletResponse response)
3 throws IOException, ServletException {
4 ModuleUtils.getInstance().selectModule(request, getServletContext());
5
6 ModuleConfig config = getModuleConfig(request);
7
8 RequestProcessor processor = getProcessorForModule(config);
9
10 if (processor == null) {
11 processor = getRequestProcessor(config);
12 }
13
14 processor.process(request, response);
15 }
这个方法中重点是14行,调用RequestProcessor的process方法。RequestProcessor类可以定制,在struts.config中设置:
<controller processorClass="org.springframework.web.struts.DelegatingTilesRequestProcessor" />
process方法具体如下:
1 public void process(HttpServletRequest request, HttpServletResponse response)
2 throws IOException, ServletException {
3 // Wrap multipart requests with a special wrapper
4 request = processMultipart(request);
5
6 // Identify the path component we will use to select a mapping
7 String path = processPath(request, response);
8
9 if (path == null) {
10 return;
11 }
12
13 if (log.isDebugEnabled()) {
14 log.debug("Processing a '" + request.getMethod() + "' for path '"
15 + path + "'");
16 }
17
18 // Select a Locale for the current user if requested
19 processLocale(request, response);
20
21 // Set the content type and no-caching headers if requested
22 processContent(request, response);
23 processNoCache(request, response);
24
25 // General purpose preprocessing hook
26 if (!processPreprocess(request, response)) {
27 return;
28 }
29
30 this.processCachedMessages(request, response);
31
32 // Identify the mapping for this request
33 ActionMapping mapping = processMapping(request, response, path);
34
35 if (mapping == null) {
36 return;
37 }
38
39 // Check for any role required to perform this action
40 if (!processRoles(request, response, mapping)) {
41 return;
42 }
43
44 // Process any ActionForm bean related to this request
45 ActionForm form = processActionForm(request, response, mapping);
46
47 processPopulate(request, response, form, mapping);
48
49 // Validate any fields of the ActionForm bean, if applicable
50 try {
51 if (!processValidate(request, response, form, mapping)) {
52 return;
53 }
54 } catch (InvalidCancelException e) {
55 ActionForward forward = processException(request, response, e, form, mapping);
56 processForwardConfig(request, response, forward);
57 return;
58 } catch (IOException e) {
59 throw e;
60 } catch (ServletException e) {
61 throw e;
62 }
63
64 // Process a forward or include specified by this mapping
65 if (!processForward(request, response, mapping)) {
66 return;
67 }
68
69 if (!processInclude(request, response, mapping)) {
70 return;
71 }
72
73 // Create or acquire the Action instance to process this request
74 Action action = processActionCreate(request, response, mapping);
75
76 if (action == null) {
77 return;
78 }
79
80 // Call the Action instance itself
81 ActionForward forward =
82 processActionPerform(request, response, action, form, mapping);
83
84 // Process the returned ActionForward instance
85 processForwardConfig(request, response, forward);
86 }
每项处理都封装成一个方法,对struts控制器的扩展,可以继承这两个类,然后覆盖以上方法。
具体看一下processActionCreate方法:
protected Action processActionCreate(HttpServletRequest request,
HttpServletResponse response, ActionMapping mapping)
throws IOException {
// Acquire the Action instance we will be using (if there is one)
String className = mapping.getType();
if (log.isDebugEnabled()) {
log.debug(" Looking for Action instance for class " + className);
}
// If there were a mapping property indicating whether
// an Action were a singleton or not ([true]),
// could we just instantiate and return a new instance here?
Action instance;
synchronized (actions) {
// Return any existing Action instance of this class
instance = (Action) actions.get(className);
if (instance != null) {
if (log.isTraceEnabled()) {
log.trace(" Returning existing Action instance");
}
return (instance);
}
// Create and return a new Action instance
if (log.isTraceEnabled()) {
log.trace(" Creating new Action instance");
}
try {
instance = (Action) RequestUtils.applicationInstance(className);
// Maybe we should propagate this exception
// instead of returning null.
} catch (Exception e) {
log.error(getInternal().getMessage("actionCreate",
mapping.getPath()), e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("actionCreate", mapping.getPath()));
return (null);
}
actions.put(className, instance);
if (instance.getServlet() == null) {
instance.setServlet(this.servlet);
}
}
return (instance);
}
针对所有的请求,只实例化一个Action,因此必须注意线程安全的问题。