struts源码分析(1)-----如何获取action

来源:互联网 发布:阿里云新logo矢量图 编辑:程序博客网 时间:2024/05/22 14:25
在struts中,很神奇,我们在配置文件中配置好了ActionMapping的配置,当我们在访问的时候,框架通过我们使用的url地址,能够自动的定位到Action中,我们来看一个是如何做到的,


Struts初始化处理流程:
根据在web.xml中配置的初始化参数,Servlet容器将决定在在容器的第一次启动,或第一次客户请求ActionServlet的
时机加载ActionServlet ,不管哪种方式加载,和其它Servlet一样,ActionServlet的init( )方法将被调用,开始初始化
过程。让我们来看看在初始化过程中将发生些什么,理解了这些,对于我们debug和扩展自己的应用更加得心应手。
1 初始化框架的内部消息绑定,这些消息用来输出提示,警告,和错误信息到日志文件中。
org.apache.struts.action.ActionResources 用来获取内部消息;
2 加载web.xml中定义的不同参数,用以控制ActionServlet的不同行为,这些参数包括
config, debug, detail, and convertNull ;
3 加载并初始化web.xml中定义的servlet 名称和servlet映射信息。通过初始化,框架的各种DTD被注册,DTD用
来在下一步校验配置文件的有效性;
4、 为默认应用加载并初始化Struts配置文件,配置文件即初始化参数config 指定的文件。默认配置文件被解析,
产生一个ApplicationConfig 对象存于ServletContext 中。可以通过关键字
org.apache.struts.action.APPLICATION 从ServletContext中获取ApplicationConfig;
5 Struts配置文件中指定的每一个消息资源都被加载,初始化,并存在ServletContext的合适区域(基于每个
message-resources元素的key属性),如果key 属性没有设置,则为org.apache.struts.action.MESSAGE;
6 Struts配置文件中声明的每一个数据源被加载并且初始化,如果没有配置数据源,这一步跳过;
7 加载并初始化Struts配置文件中指定的插件。每一个插件的init()方法被调用;
8 当默认应用加载完成,init()方法判断是否有应用模块需要加载,如果有,重复步骤4—7万成应用模块的加载。

  1. public void init() throws ServletException {
  2.         final String configPrefix = "config/";
  3.         final int configPrefixLength = configPrefix.length() - 1;
  4.         // Wraps the entire initialization in a try/catch to better handle
  5.         // unexpected exceptions and errors to provide better feedback
  6.         // to the developer
  7.         try {
  8.             initInternal();
  9.             initOther();
  10.             initServlet();
  11.             initChain();
  12.             getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
  13.             initModuleConfigFactory();
  14.             // Initialize modules as needed
  15.             ModuleConfig moduleConfig = initModuleConfig("", config);
  16.             initModuleMessageResources(moduleConfig);
  17.             initModulePlugIns(moduleConfig);
  18.             initModuleFormBeans(moduleConfig);
  19.             initModuleForwards(moduleConfig);
  20.             initModuleExceptionConfigs(moduleConfig);
  21.             initModuleActions(moduleConfig);
  22.             moduleConfig.freeze();
  23.             Enumeration names = getServletConfig().getInitParameterNames();
  24.             while (names.hasMoreElements()) {
  25.                 String name = (String) names.nextElement();
  26.                 if (!name.startsWith(configPrefix)) {
  27.                     continue;
  28.                 }
  29.                 String prefix = name.substring(configPrefixLength);
  30.                 moduleConfig =
  31.                     initModuleConfig(prefix,
  32.                         getServletConfig().getInitParameter(name));
  33.                 initModuleMessageResources(moduleConfig);
  34.                 initModulePlugIns(moduleConfig);
  35.                 initModuleFormBeans(moduleConfig);
  36.                 initModuleForwards(moduleConfig);
  37.                 initModuleExceptionConfigs(moduleConfig);
  38.                 initModuleActions(moduleConfig);
  39.                 moduleConfig.freeze();
  40.             }
  41.             this.initModulePrefixes(this.getServletContext());
  42.             this.destroyConfigDigester();
  43.         } catch (UnavailableException ex) {
  44.             throw ex;
  45.         } catch (Throwable t) {
  46.             // The follow error message is not retrieved from internal message
  47.             // resources as they may not have been able to have been
  48.             // initialized
  49.             log.error("Unable to initialize Struts ActionServlet due to an "
  50.                 + "unexpected exception or error thrown, so marking the "
  51.                 + "servlet as unavailable.  Most likely, this is due to an "
  52.                 + "incorrect or missing library dependency.", t);
  53.             throw new UnavailableException(t.getMessage());
  54.         }
  55.     }

在ActionServlet中,不管是post还是get方法提交的数据都会经过process的处理:


  1.  protected void process(HttpServletRequest request,
  2.         HttpServletResponse response)
  3.         throws IOException, ServletException {
  4.         ModuleUtils.getInstance().selectModule(request, getServletContext());
  5.         ModuleConfig config = getModuleConfig(request);
  6.         RequestProcessor processor = getProcessorForModule(config);
  7.         if (processor == null) {
  8.             processor = getRequestProcessor(config);
  9.         }
  10.         processor.process(request, response);
  11.     }
通过该请求以及ServletContext获取配置文件参数取得当前的请求的模块,(对应ModuleConfig)然后根据该ModuleConfig找到该模块的RequestProcess处理器,进行处理:

  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.         // Identify the path component we will use to select a mapping
  6.         String path = processPath(request, response);
  7.         if (path == null) {
  8.             return;
  9.         }
  10.         if (log.isDebugEnabled()) {
  11.             log.debug("Processing a '" + request.getMethod() + "' for path '"
  12.                 + path + "'");
  13.         }
  14.         // Select a Locale for the current user if requested
  15.         processLocale(request, response);
  16.         // Set the content type and no-caching headers if requested
  17.         processContent(request, response);
  18.         processNoCache(request, response);
  19.         // General purpose preprocessing hook
  20.         if (!processPreprocess(request, response)) {
  21.             return;
  22.         }
  23.         this.processCachedMessages(request, response);
  24.         // Identify the mapping for this request
  25.         ActionMapping mapping = processMapping(request, response, path);
  26.         if (mapping == null) {
  27.             return;
  28.         }
  29.         // Check for any role required to perform this action
  30.         if (!processRoles(request, response, mapping)) {
  31.             return;
  32.         }
  33.         // Process any ActionForm bean related to this request
  34.         ActionForm form = processActionForm(request, response, mapping);
  35.         processPopulate(request, response, form, mapping);
  36.         // Validate any fields of the ActionForm bean, if applicable
  37.         try {
  38.             if (!processValidate(request, response, form, mapping)) {
  39.                 return;
  40.             }
  41.         } catch (InvalidCancelException e) {
  42.             ActionForward forward = processException(request, response, e, form, mapping);
  43.             processForwardConfig(request, response, forward);
  44.             return;
  45.         } catch (IOException e) {
  46.             throw e;
  47.         } catch (ServletException e) {
  48.             throw e;
  49.         }
  50.         // Process a forward or include specified by this mapping
  51.         if (!processForward(request, response, mapping)) {
  52.             return;
  53.         }
  54.         if (!processInclude(request, response, mapping)) {
  55.             return;
  56.         }
  57.         // Create or acquire the Action instance to process this request
  58.         Action action = processActionCreate(request, response, mapping);
  59.         if (action == null) {
  60.             return;
  61.         }
  62.         // Call the Action instance itself
  63.         ActionForward forward =
  64.             processActionPerform(request, response, action, form, mapping);
  65.         // Process the returned ActionForward instance
  66.         processForwardConfig(request, response, forward);
  67.     }
接下来,让我们一步一步地了解process()方法到底做了什么。
1、 调用processMultipart( )方法。如果HttpServletRequest是POST方式,且请求为multipart/form-data ,
Struts框架将请求对象包装成处理multipart 请求专用的请求对象,否则,只是简单地返回原有的请求对象。一
般来说,除非需要处理文件上传,否则不用关心multipart 功能的具体细节。
2、 调用processPath( ) 方法,该方法用来从请求URL中获应用取路径部分。获取到的信息在稍后的步骤中用于
选择合适的Struts Action调用。
3、 调用processLocale( ) 方法处理一些国际化的事务。
4、 调用方法来决定processContent( )请求的content type编码(encoding)方式。content type可以配合在配置
文件中,也可以在jsp文件中配置,默认为text/html。
5、 根据noCache属性的设置调用processNoCache( ) 方法,如果noCache设置为true。则添加合适的响应头到响
应对象中,使得页面保留在浏览器的Cache中。这些响应头包含Pragma, Cache-Control, 和Expires 。
6、 调用processPreprocess( )方法,这个方法在这儿设置一个钩子,方法的默认实现只是简单地返回true,这样
给了自定义处理器的开发者提供了一个合适的地方让你添加自己的业务逻辑。因为这个方法在调用Action之前
被调用,如果你重载这个方法,只需要返回false,则Action就不会被调用。例如,你可以重载这个方法用户检
查客户session,如果不通过就返回false。
7、 调用processMapping( )方法,根据客户请求信息中的path 信息来决定是否返回ActionMapping对象实例。如
果不能够找到path 的映射,则客户将会得到一个error 响应。
8、 通过调用processRoles( )方法检查是否为Action配置了安全角色。如果配置了角色要求,则请求对象的
isUserInRole( )方法被调用,如果用户属于这些角色,则客户会得到显示一个error 响应。
9、 调用processActionForm( )方法检查是否存在为ActionMapping配置的ActionForm 。如果存在,则在有效区
域内查找是否存在该ActionForm 的实例,存在,则复用,不存在,则创建一个实例。然后将实例保存与再配置
文件中配置好的有效区域(request,session,application)内,并用Action元素的name属性作为该实例的关键字。
10、 调用processPopulate( )方法,如果存来存在为ActionMapping配置的ActionForm,则封装请求对象中的数据
到ActionForm 中,在进行封装之前,先调用ActionForm 的reset( )方法进行属性值的默认化。
11、 调用processValidate( )方法。如果ActionForm被配置好,并且action元素的属性validate被设
置为true ,则进一步调用validate( )方法进行规则校验。如果validate( )方法校验失败,就会保存一
个ActionErrors 对象到请求区域中,请求将会自动重定向到action映射的input属性所指定的页面中。如
果校验通过或在action 映射中没有配置ActionForm,则继续处理请求。
12、 根据action 映射是否配置了forward属性或include属性来决定下一步操作。如果配置了任意一个,则相应
地调用RequestDispatcher对象的forward( )方法或include( )方法,调用后,对客户请求的处理结束。否则,
继续处理请求。
13、 调用processActionCreate( )方法创建或获取一个Action对象实例处理请求。processActionCreate( )方法会
在缓存中查找是否存在已经创建好的Action实例,如果存在,则复用,否则,则重新创建并将其村于缓存中。
14、 调用processActionPerform( )方法,该方法用于在一个try/catch 代码块中调用action 实例的execute( )方法,
这样确保action 的execute( )方法一旦发生执行异常能够被RequestProcessor捕获。
15、 调用processActionForward( )方法,并传入action的execute( )方法所返回的ActionForward对象实例,方
法通过检查ActionForward对象实例,决定采用redirect或forword方式进行重定向。究竟采用redirect还是
forword取决于forward元素的redirect属性值。

  1. String path = processPath(request, response);
获取该url对应的action名称;

  1. protected String processPath(HttpServletRequest request,
  2.         HttpServletResponse response)
  3.         throws IOException {
  4.         String path;
  5.         // For prefix matching, match on the path info (if any)
  6.         path = (String) request.getAttribute(INCLUDE_PATH_INFO);
  7.         if (path == null) {
  8.             // /*.*查找模式;
  9.             path = request.getPathInfo();
  10.         }
  11.         if ((path != null) && (path.length() > 0)) {
  12.             return (path);
  13.         }
  14.         //*.do的查找模式;
  15.         // For extension matching, strip the module prefix and extension
  16.         path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
  17.         if (path == null) {
  18.             path = request.getServletPath();
  19.         }
  20.         String prefix = moduleConfig.getPrefix();
  21.         if (!path.startsWith(prefix)) {
  22.             String msg = getInternal().getMessage("processPath");
  23.             log.error(msg + " " + request.getRequestURI());
  24.             response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
  25.             return null;
  26.         }
  27.         path = path.substring(prefix.length());
  28.         int slash = path.lastIndexOf("/");
  29.         int period = path.lastIndexOf(".");
  30.         if ((period >= 0) && (period > slash)) {
  31.             path = path.substring(0, period);
  32.         }
  33.         //
  34.         return (path);
  35.     }

  1. ActionMapping mapping = processMapping(request, response, path);
这一步获取了配置文件中该action的actionMapping标记;


  1.  Action action = processActionCreate(request, response, mapping);
生成Action对象实例;

  1.   protected Action processActionCreate(HttpServletRequest request,
  2.         HttpServletResponse response, ActionMapping mapping)
  3.         throws IOException {
  4.         // Acquire the Action instance we will be using (if there is one)
  5.         String className = mapping.getType();
  6.         if (log.isDebugEnabled()) {
  7.             log.debug(" Looking for Action instance for class " + className);
  8.         }
  9.         Action instance;
  10.         
  11.         //线程安全;可见action都是延迟生成的,多个线程是共享了action 的,但是将action在hashmap中的curd是线程安全的;
  12.         synchronized (actions) {
  13.             // Return any existing Action instance of this class
  14.             instance = (Action) actions.get(className);
  15.             if (instance != null) {
  16.                 if (log.isTraceEnabled()) {
  17.                     log.trace("  Returning existing Action instance");
  18.                 }
  19.                 return (instance);
  20.             }
  21.             try {
  22.                 instance = (Action) RequestUtils.applicationInstance(className);
  23.                 // Maybe we should propagate this exception
  24.                 // instead of returning null.
  25.             } catch (Exception e) {
  26.                 log.error(getInternal().getMessage("actionCreate",
  27.                         mapping.getPath()), e);
  28.                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  29.                     getInternal().getMessage("actionCreate", mapping.getPath()));
  30.                 return (null);
  31.             }
  32.             actions.put(className, instance);
  33.         }
  34.         if (instance.getServlet() == null) {
  35.             instance.setServlet(this.servlet);
  36.         }
  37.         return (instance);
  38.     }

  1.     private RequestProcessor getProcessorForModule(ModuleConfig config) {
  2.         String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix();
  3.         return (RequestProcessor) getServletContext().getAttribute(key);
  4.     }
可见RequestProcess都是存储在上下文中的实例;在应用程序的生命周期类,他们是复用的,对应与特定的模块;
而actions是每一个RequestProcess的属性,同理对应于一个模块,所以在应用程序的生命周期内也是复用的;

action的生成是通过反射的:

在action配置中获取类名:
  1. String className = mapping.getType();
  1.     public static Object applicationInstance(String className,
  2.         ClassLoader classLoader)
  3.         throws ClassNotFoundException, IllegalAccessException,
  4.             InstantiationException {
  5.         return (applicationClass(className, classLoader).newInstance());
  6.     }
  1.    public static Class applicationClass(String className,
  2.         ClassLoader classLoader)
  3.         throws ClassNotFoundException {
  4.         if (classLoader == null) {
  5.             // Look up the class loader to be used
  6.             classLoader = Thread.currentThread().getContextClassLoader();
  7.             if (classLoader == null) {
  8.                 classLoader = RequestUtils.class.getClassLoader();
  9.             }
  10.         }
  11.         // Attempt to load the specified class
  12.         return (classLoader.loadClass(className));
  13.     }
通过反射得到类实例;

原创粉丝点击