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,因此必须注意线程安全的问题。