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