posts - 3, comments - 1, trackbacks - 0, articles - 5

简要的对struts ActionServlet init()方法以及 struts RequestProcessor类做源码分析

首先要确定的是Servlet至始至终只有一个对象以及init()方法会在Servlet第一次加载时被执行一次也是唯

一的一次,所以Servlet的初始化工作一般在init()方法进行

一、解析ModuleConfig
ModuleConfig封装了struts-config的所有配置信息
actionConfigs(ActionMapping)/actionConfigList 、exceptions 、formBeans(ActionForm)、forwards

(ActionForward)、messageResources、plugIns等
// 初始化ModuleConfig配置工厂
initModuleConfigFactory();
// 由配置工厂实例化一个ModuleConfig的对象
ModuleConfig moduleConfig = initModuleConfig("", config);
initModuleConfig(String prefix, String paths)方法做了一下事情
1、 ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
由生成的配置工厂生成一个工厂实例
2、ModuleConfig config = factoryObject.createModuleConfig(prefix);
创建ModuleConfig实例createModuleConfig()方法会有DefaultModuleConfigFactory 执行
这是在配置文件中指定的工厂类
方法内部new 出ModuleConfig对象
在ModuleConfig构造方法内对以下参数进行了初始化
 

this.prefix = prefix;
        
this.actionConfigs = new HashMap();  //ActionMapping集合
        this.actionConfigIds = new HashMap();
        
this.actionConfigList = new ArrayList();
        
this.actionFormBeanClass = "org.apache.struts.action.ActionFormBean";
        
this.actionMappingClass = "org.apache.struts.action.ActionMapping";
        
this.actionForwardClass = "org.apache.struts.action.ActionForward";
        
this.configured = false;
        
this.controllerConfig = null;
        
this.exceptions = new HashMap();
        
this.formBeans = new HashMap();   //ActionForm集合
        this.forwards = new HashMap();   //ActionForward集合
        this.messageResources = new HashMap();
        
this.plugIns = new ArrayList();

 

到此config构造完成
Digester digester = initConfigDigester();
initConfigDigester()方法添加解析struts文件的解析规则

3、循环struts配置文件并把所有标签封装为相应对象填充到config对象相应的map集合中中
List urls = splitAndResolvePaths(paths);
        URL url;
        for (Iterator i = urls.iterator(); i.hasNext();) {
            url = (URL) i.next();
            digester.push(config);
            this.parseModuleConfigFile(digester, url);
        }
最后getServletContext().setAttribute(Globals.MODULE_KEY
            + config.getPrefix(), config);
把ModuleConfig设入ServletContext中
至此config初始化工作全部完成

struts每个模块都对应一个ModuleConfig对象个,在init方法内被初始化,内部封装了模块xml文

件中的配置


二、struts核心类RequestProcessor类解析
ActionServlet做为前端控制器当有请求被接收时,会调用process(request, response)方法
1、ModuleConfig config = getModuleConfig(request);
通过Servlet上下文找到ModuleConfig对象
2、RequestProcessor processor = getProcessorForModule(config);
通过Servlet上下文找到RequestProcessor对象  --所以RequestProcessor类也是单例的
if (processor == null) {
            processor = getRequestProcessor(config);
     }
如果为空 也就说明是服务器端第一次接收客户端的接收请求
那么执行getRequestProcessor(config)方法生成RequestProcessor并设入ServletContext里
此方法为同步方法,借此我猜测此方法为生成RequestProcessor的唯一方法
方法内部对RequestProcessor调用了init()方法,进行了RequestProcessor的初始化并设入ServletContext

里。
为什么RequestProcessor要做成单例,原因就是RequestProcessor类内部有
HashMap actions = new HashMap();
这样一个集合封装了所有的控制器。
这样的设计是面向对象的。当ActionServlet接收到请求时它需要对请求分发到相应控制器中
此时它获取中央控制器,而这个中央控制器内部拥有所有控制器的引用。
/*
这里我的疑惑是为什么要通过反射生成RequestProcessor,此类并没有继承实现任何接口或类。
process方法最后调用了RequestProcessor的process()方法。此方法为RequestProcessor的核心方法
*/原因经过学习发现RequestProcessor并不是单例的,而实际因为struts的多模块应用模式,导致

RequestProcessor类是多例的

三、解析RequestProcessor类核心方法process
1、 String path = processPath(request, response);
截取客户端请求字符串相当于<action>标签的path属性
2、ActionMapping mapping = processMapping(request, response, path);
获取控制器相对的ActionMapping 对象
 processMapping(request, response, path)
 1、ActionMapping mapping =
            (ActionMapping) moduleConfig.findActionConfig(path);
 方法内部调用了moduleConfig.findActionConfig(path);
 ModuleConfig对象前面已做过解析,在findActionConfig方法内部已path属性做为Key值,直接 

 到ActionConfigHashMap集合内寻找ActionMapping,原因见一.3
 2.if (mapping != null) {
            request.setAttribute(Globals.MAPPING_KEY, mapping);

            return (mapping);
        }
 如果找到mapping那么放入request中,并返回
 3、 ActionConfig[] configs = moduleConfig.findActionConfigs();

        for (int i = 0; i < configs.length; i++) {
            if (configs[i].getUnknown()) {
                mapping = (ActionMapping) configs[i];
                request.setAttribute(Globals.MAPPING_KEY, mapping);

                return (mapping);
            }
        }
 //ActionMapping说明,和Action的unknow属性有关
3、if (mapping == null) {
            return;
        }
容错处理,说明客户端请求的path路径并没有被配置
4、ActionForm form = processActionForm(request, response, mapping);
 processActionForm(request, response, mapping);
 1、ActionForm instance =
            RequestUtils.createActionForm(request, mapping, moduleConfig,
                servlet);
  1、String name = mapping.getName();
  获得mapping name属性也就是映射的相对ActionForm的name
  2、FormBeanConfig config = moduleConfig.findFormBeanConfig(name);
  已name做为Key值在缓存的HashMap内获取相对(FormBeanConfig)对象
  
  <!--对这里的一些继承关系做下说明
  struts Config类的基类 BaseConfig
  ForwardConfig从BaseConfig继承
  FormBeanConfig从BaseConfig继承
  ActionConfig从BaseConfig继承
  ActionMapping从ActionConfig继承
  -->
  
  3、if (config == null) {
              log.warn("No FormBeanConfig found under '" + name + "'");
              return (null);
         }
  如果为空也就说明并没有配置相对ActionForm,而这是合法的所以返回空
  4、ActionForm instance =
             lookupActionForm(request, attribute, mapping.getScope());
  此方法试图在request,session内寻找ActionForm
  5、if ((instance != null) && config.canReuse(instance)) {
              return (instance);
         }
  如果找到那么返回
  6、return createActionForm(config, servlet);
  如果没有找到则进行创建,方法传递了config,因为config内封装了formbean标签的所 

 有配置信息。方法可以根据类名进行反射生成对象。
 2、if ("request".equals(mapping.getScope())) {
            request.setAttribute(mapping.getAttribute(), instance);
        } else {
            HttpSession session = request.getSession();

            session.setAttribute(mapping.getAttribute(), instance);
        }
 根据配置信息,把ActionForm设入相应作用域内
此方法作用就是获取ActionForm
5、processPopulate(request, response, form, mapping);
此方法用于做ActionForm的数据收集工作
 1、form.reset(mapping, request);
 此方法用于做数据重置,程序员可以重写此方法
 2、RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
            request);
 调用工具类进行数据收集
  1、if ((contentType != null)
             && (contentType.startsWith("multipart/form-data"))
             && (method.equalsIgnoreCase("POST"))) { 
  判断是否是数据上传
  2、if (!isMultipart) {
             names = request.getParameterNames();
         }
  如果不是获取request里所有参数参数名。
  3、 while (names.hasMoreElements()) { 
            String name = (String) names.nextElement(); //循环获取参数名
             String stripped = name;
             Object parameterValue = null;
  ...
             if (isMultipart) {   
                parameterValue = multipartParameters.get(name);
                parameterValue = rationalizeMultipleFileProperty(bean, name,    

 parameterValue);
             } else {  //不是上传 则获取此参数名的所有参数值
                parameterValue = request.getParameterValues(name);
             }
             if (!(stripped.startsWith("org.apache.struts."))) {
                properties.put(stripped, parameterValue); 
             }
         }
  方法最后把参数名做为Key,所对应的所有参数值做为键值放入hashmap内
  5、BeanUtils.populate(bean, properties);
  由第三方工具类根据ActionForm以及所有参数名参数值进行数据收集
 3、if (!processValidate(request, response, form, mapping)) {
                return;
            }
 进行数据验证,方法内部会调用ActionForm的validate方法,程序员可以重写此方法
 
6、Action action = processActionCreate(request, response, mapping);
获取控制器
 1、方法首先获取Action的类名
 2、然后此方法对actions进行了同步处理,原因RequestProcessor是多例的,actions是一个成 

员变量,缓存了所有action。
 3、instance = (Action) actions.get(className);

            if (instance != null) {
                if (log.isTraceEnabled()) {
                    log.trace("  Returning existing Action instance");
                }

                return (instance);
            }
 试图在集合内取出action,如果不为空 则返回。此时说明此Action已被执行过。
 4、instance = (Action) RequestUtils.applicationInstance(className);
 如果没有反射创建Action引用。
 5、actions.put(className, instance);
 最后把action放入集合,并返回。
7、ActionForward forward =
            processActionPerform(request, response, action, form, mapping);
此方法内部调用Action的execte方法,获取转向类。

 <!--说明类组织关系
 在他的父类里有 HashMap forwards = new HashMap();这样一个集合
 这里的类组织关系是这样的,ActionMapping从ActionConfig继承,
 ActionConfig拥有forwards 集合,
 而ModuleConfig内部拥有ActionConfig集合,
 这和struts-config标签的组织关系是完全类似的.
 ModuleConfig模块配置类对应一个struts配置文件,
 ActionConfig控制器配置类对应一个Action标签,
 ForwardConfig转向配置类对应Forward标签,
 而ModuleConfig在ActionServlet初始化时被加载。
 这里可以看出大师对类的组织的合理,每个标签都有相应的类。
 -->

综上所述,由于forward标签属于ActionMapping标签所以在程序里,能用findForward()方法找到

ActionForward.
而在ActionConfig内部有这样一个方法addForwardConfig(ForwardConfig config);它已ForwardConfig 的

name属性做为Key,ForwardConfig 类实例做为值添加入缓存中。
这也就是为什么我们可以在findForward()方法内通过传name属性获取相应的ActionForward实例。

8、processForwardConfig(request, response, forward);
在process方法的最后调用了processForwardConfig()方法。完成转向操作。
在这个方法内部,他根据Redirect()是否设置为true选择是进行转发还是重定向。如果没有设置,默认是进

行转发。

说明:
         此篇学习笔记算是阅读尚学堂王勇老师struts视频的读后感吧,当时小弟刚学习struts,也算是个j2ee的新人就斗胆写了这篇源码的分析,今天发上来并未做任何删减,错误是难免的,还希望各位大虾阅读时,多多指正,不吝赐教!!
        最后感谢由衷的感谢王勇老师,感谢尚学堂!!


只有注册用户登录后才能发表评论。


网站导航: