您的位置:首页 > 编程语言 > Java开发

struts ModuleConfig类加载初始化的过程以及RequestProcessor类源码分析

2011-11-09 15:55 676 查看
 
简要的对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选择是进行转发还是重定向。如果没有设置,默认是进

行转发。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息