`

struts2源码初读(二)预处理

阅读更多
下面开始浏览struts2请求处理部分源码,最核心的方法doFilter

/**
 *	Dispatcher
 */
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    	/**
    	 * 实例化HttpServletRequest和HttpServletResponse
    	 */
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
        	/**
        	 * 处理编码与本地化
        	 */
            prepare.setEncodingAndLocale(request, response);
            /**
             * 创建action上下文
             */
            prepare.createActionContext(request, response);
            /**
             * 关联当前的dispatcher与当前线程
			 * 
             */
            prepare.assignDispatcherToThread();
            /**
             * 判断请求的URL模式是不是规定的模式
             */
			if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
				chain.doFilter(request, response);
			} else {
				/**
				 * 封装request对象
				 */
				request = prepare.wrapRequest(request);
				/**
				 * xwork的ConfigurationManager读取struts.xml
				 * 返回值来自Dispatcher的Container
				 */
				ActionMapping mapping = prepare.findActionMapping(request, response, true);
				/**
				 * 根据请求的URL,如果ActionMapping找不到,执行静态资源请求
				 * 如果找到就转发给action处理
				 */
				if (mapping == null) {
					boolean handled = execute.executeStaticResourceRequest(request, response);
					if (!handled) {
						chain.doFilter(request, response);
					}
				} else {
					/**
					 * 在执行这个方法之前对http请求做预处理,为业务处理准备数据和运行环境
					 * 执行这个方法后把控制权交给xwork
					 * struts核心设计就是解耦合,消除核心程序对外部运行环境的依赖
					 * Web容器和MVC分离
					 */
					execute.executeAction(request, response, mapping);
				}
			}
        } finally {
        	/**
        	 * 清理本次请求相关内容
        	 */
            prepare.cleanupRequest(request);
        }
    }

每一个请求过来就会调用一次doFilter方法,下面来看一下doFilter方法中都做了哪些操作。在实例化HttpServletRequest和HttpServletResponse后,设置请求的编码和国际化

/**
	 * PrepareOperations
	 */
	public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {
        dispatcher.prepare(request, response);
    }
	
	/**
	 * Dispacher
	 */
	public void prepare(HttpServletRequest request, HttpServletResponse response) {
        String encoding = null;
        if (defaultEncoding != null) {
            encoding = defaultEncoding;
        }

        Locale locale = null;
        if (defaultLocale != null) {
            locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
        }

        if (encoding != null) {
            try {
                request.setCharacterEncoding(encoding);
            } catch (Exception e) {
                LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);
            }
        }

        if (locale != null) {
            response.setLocale(locale);
        }

        if (paramsWorkaroundEnabled) {
            request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request
        }
    }


struts2用注解的方式在初始化的时候对属性赋值,类似与spring的依赖注入

在StrutsConstants中
public static final String STRUTS_LOCALE = "struts.locale";
public static final String STRUTS_I18N_ENCODING = "struts.i18n.encoding";


在default.properties中
# struts.locale=en_US
struts.i18n.encoding=UTF-8


@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
    public void setDefaultEncoding(String val) {
        defaultEncoding = val;
    }
	
@Inject(value=StrutsConstants.STRUTS_LOCALE, required=false)
    public void setDefaultLocale(String val) {
        defaultLocale = val;
    }


所以在初始化的时候defaultEncoding赋值为UTF-8,stauts.locale被注掉了,同时注解设置了required=false,对不存在的属性,初始化注入的时候被忽略了,故defaultLocale为null

下面来看一下是如何创建action上下文,又做了哪些操作。我们知道每一个请求都会创建一个action上下文,不同的action不会共享action上下文,这是通过本地线程变量实现的。
public static ActionContext getContext() {
        return (ActionContext) actionContext.get();
    }
static ThreadLocal actionContext = new ThreadLocal();


下面来看看ActionContext中有哪些属性,除了一些静态常量外就只有Map<String, Object> context一个属性。
/**
	 * PrepareOperations
	 */
	public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
        ActionContext ctx;
        Integer counter = 1;
        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
        if (oldCounter != null) {
            counter = oldCounter + 1;
        }
        /**
		 *  从ThreadLocal中获取ActionContext
		 */
        ActionContext oldContext = ActionContext.getContext();
        if (oldContext != null) {
            // detected existing context, so we are probably in a forward
            ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
        } else {
            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
            stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));
            ctx = new ActionContext(stack.getContext());
        }
        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
        ActionContext.setContext(ctx);
        return ctx;
    }


createActionContext中最重要的是dispatcher.createContextMap(request, response, null, servletContext),该方法就是将容器对象封装成普通java对象.这个方法是struts2核心设计,前面说过struts的核心设计就是解耦合,消除核心程序对外部运行环境的依赖,即Web容器和MVC分离,在创建action上下文的时候把web容器相关的数据Request,Session,
Applicatioin...封装成普通的java对象Map使得xwork不依赖与web容器,从而解耦和。
/**
	 * Dispatcher
	 */
	public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
            ActionMapping mapping, ServletContext context) {

        // request map wrapping the http request objects
        Map requestMap = new RequestMap(request);

        // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately
        Map params = new HashMap(request.getParameterMap());

        // session map wrapping the http session
        Map session = new SessionMap(request);

        // application map wrapping the ServletContext
        Map application = new ApplicationMap(context);

        Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);

        if (mapping != null) {
            extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
        }
        return extraContext;
    }


createContextMap代码很清晰,先把容器对象封装成Map,在把这些Map作为value添加到contextMap中,创建之后再通过ActionContext.setContext(ctx)把context加到ThreadLocal中。

包装request的方法wrapRequest,在wrapRequest方法中又调用
/**
	 * Dispatcher
	 */
	public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
        /**
		 *  判断有没有做过封装,确保只做一次封装
		 */
        if (request instanceof StrutsRequestWrapper) {
            return request;
        }

        String content_type = request.getContentType();
		/**
		 *  封装Request对象,判断content_type是不是multipart/form-data
		 *  如果是返回MultiPartRequestWrapper对象处理文件上传
		 *  如果不是返回StrutsRequestWrapper对象处理普通请求
		 */
        if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
            MultiPartRequest mpr = null;
            //check for alternate implementations of MultiPartRequest
            Set<String> multiNames = getContainer().getInstanceNames(MultiPartRequest.class);
            if (multiNames != null) {
                for (String multiName : multiNames) {
                    if (multiName.equals(multipartHandlerName)) {
                        mpr = getContainer().getInstance(MultiPartRequest.class, multiName);
                    }
                }
            }
            if (mpr == null ) {
                mpr = getContainer().getInstance(MultiPartRequest.class);
            }
            request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext));
        } else {
            request = new StrutsRequestWrapper(request);
        }

        return request;
    }


接下来看一看prepare.findActionMapping(request, response, true)方法,在PrepareOperations的findActionMapping方法中又调用了dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager())
这里是调用ActionMapper的实现类DefaultActionMapper的getMapping方法,分析getMapping之前先看一下ActionMapping类的属性
/**
	 *  action名
	 */
	private String name;
	/**
	 * action的命名空间
	 */
    private String namespace;
	/**
	 * action的执行方法
	 */
    private String method;
	/**
	 * url后缀名
	 */
    private String extension;
	/**
	 * 参数
	 */
    private Map<String, Object> params;
	/**
	 * 返回的结果
	 */
    private Result result;


/**
	 * DefaultActionMapper
	 */
	public ActionMapping getMapping(HttpServletRequest request,
            ConfigurationManager configManager) {
        ActionMapping mapping = new ActionMapping();
		/**
		 * 获取请求中的url
		 */
        String uri = getUri(request);
		/**
		 * 去掉url中的参数
		 * eg:test/test.cation;id=1-->test/test.cation
		 */
        int indexOfSemicolon = uri.indexOf(";");
        uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;
		/**
		 * 去掉url中的后缀名test/test.cation-->test/test
		 */
        uri = dropExtension(uri, mapping);
        if (uri == null) {
            return null;
        }
		/**
		 * 解析Action的名称和命名空间
		 */
        parseNameAndNamespace(uri, mapping, configManager);
		/**
		 * 去掉请求中的重复项
		 */
        handleSpecialParameters(request, mapping);

        if (mapping.getName() == null) {
            return null;
        }
		/**
		 * 处理test!mehtod格式的请求
		 */
        parseActionName(mapping);

        return mapping;
    }


/**
	 * DefaultActionMapper
	 */
	protected String getUri(HttpServletRequest request) {
        /**
		 *  判断请求是否来自于一个jsp的include
		 *  如果通过属性"javax.servlet.include.servlet_path"取得url
		 */
        String uri = (String) request
                .getAttribute("javax.servlet.include.servlet_path");
        if (uri != null) {
            return uri;
        }

        uri = RequestUtils.getServletPath(request);
        if (uri != null && !"".equals(uri)) {
            return uri;
        }

        uri = request.getRequestURI();
		/**
		 *	去掉contextPath的路径
		 */
        return uri.substring(request.getContextPath().length());
    }


下面看一下去除url后缀名的方法,先说一下extensions的赋值
在StrutsConstants中
String STRUTS_ACTION_EXTENSION = "struts.action.extension";


在default.properties中
struts.action.extension=action,,


@Inject(StrutsConstants.STRUTS_ACTION_EXTENSION)
    public void setExtensions(String extensions) {
        if (extensions != null && !"".equals(extensions)) {
            List<String> list = new ArrayList<String>();
            String[] tokens = extensions.split(",");
            for (String token : tokens) {
                list.add(token);
            }
            if (extensions.endsWith(",")) {
                list.add("");
            }
            this.extensions = Collections.unmodifiableList(list);
        } else {
            this.extensions = null;
        }
    }

通过注解在初始化的时候赋值,所以extensions值为[action, ]

/**
	 * DefaultActionMapper
	 */
	protected String dropExtension(String name, ActionMapping mapping) {
        if (extensions == null) {
            return name;
        }
        for (String ext : extensions) {
            if ("".equals(ext)) {
                /**
				 *  如果name中不包含.
				 *  或name中最后一个点后还有/直接返回name
				 */
                int index = name.lastIndexOf('.');
                if (index == -1 || name.indexOf('/', index) >= 0) {
                    return name;
                }
            } else {
                String extension = "." + ext;
				/**
				 *  如果name结尾匹配定义后缀,则去掉name的匹配部分
				 */
                if (name.endsWith(extension)) {
                    name = name.substring(0, name.length() - extension.length());
                    mapping.setExtension(ext);
                    return name;
                }
            }
        }
        return null;
    }


parseNameAndNamespace解析action的名称和命名空间
/**
	 * DefaultActionMapper
	 */
	protected void parseNameAndNamespace(String uri, ActionMapping mapping,
            ConfigurationManager configManager) {
        String namespace, name;
        int lastSlash = uri.lastIndexOf("/");
		/**
		 *  如果处理过的url中不包含/或/在url的开头,那么namespace为""
		 */
        if (lastSlash == -1) {
            namespace = "";
            name = uri;
        } else if (lastSlash == 0) {
            namespace = "/";
            name = uri.substring(lastSlash + 1);
        } else if (alwaysSelectFullNamespace) {
            /**
			 *  alwaysSelectFullNamespace默认是false
			 *  判断是否把最后一个/前的字符全作为命名空间
			 */
            namespace = uri.substring(0, lastSlash);
            name = uri.substring(lastSlash + 1);
        } else {
            // Try to find the namespace in those defined, defaulting to ""
            Configuration config = configManager.getConfiguration();
            String prefix = uri.substring(0, lastSlash);
            namespace = "";
            boolean rootAvailable = false;
            /**
			 * 匹配最长的命名空间
			 * eg:url test1/test2/test
			 * 如果配置文件有两个namespace test1/test2和test1
			 * 会匹配最长的那个test1/test2即贪婪匹配
			 */
            for (Object cfg : config.getPackageConfigs().values()) {
                String ns = ((PackageConfig) cfg).getNamespace();
                if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) {
                    if (ns.length() > namespace.length()) {
                        namespace = ns;
                    }
                }
                if ("/".equals(ns)) {
                    rootAvailable = true;
                }
            }

            name = uri.substring(namespace.length() + 1);

            // Still none found, use root namespace if found
            if (rootAvailable && "".equals(namespace)) {
                namespace = "/";
            }
        }

        if (!allowSlashesInActionNames && name != null) {
            int pos = name.lastIndexOf('/');
            if (pos > -1 && pos < name.length() - 1) {
                name = name.substring(pos + 1);
            }
        }

        mapping.setNamespace(namespace);
        mapping.setName(name);
    }

根据请求的URL,如果ActionMapping找不到,执行静态资源请求,如果找到就转发给action处理,struts的预处理到此就结束了。
分享到:
评论

相关推荐

    struts2框架源码

    深入理解Struts2的源码对于提升Java Web开发技能,尤其是在面试中讨论底层实现时,具有非常重要的价值。 首先,我们来看看Struts2的核心组件和设计理念: 1. **Action**:在Struts2中,Action类是业务逻辑处理的...

    struts2 源码

    深入学习Struts2的源码,有助于理解其运行机制,从而更好地优化代码、调试问题,甚至开发自己的扩展。对于Java Web开发者来说,掌握Struts2的基本原理和使用技巧,能够显著提高开发效率和应用质量。

    深入浅出Struts2源码-例子程序(完整版)

    1. **Action与拦截器**:在Struts2中,Action是业务逻辑的载体,而拦截器则实现了对Action调用的预处理和后处理,如登录验证、日志记录等。通过自定义拦截器,开发者可以扩展框架功能。 2. **配置机制**:Struts2的...

    浪曦struts2源码第二课

    在“浪曦struts2源码第二课”中,我们将会深入探讨Struts2的核心概念、工作原理以及源码分析。 首先,让我们了解Struts2的基本架构。Struts2的核心组件包括Action、Result、Interceptor(拦截器)等。Action是业务...

    struts2源码2

    Struts2是一个基于MVC(Model-View-Controller)设计模式的开源JavaEE框架,...通过对Struts2源码的深入学习,开发者不仅可以提升自己的技术水平,还能更好地解决实际开发中遇到的问题,实现更高效、更健壮的Web应用。

    struts2源码下载啦

    9. **过滤器Dispatcher**:Struts2通过FilterDispatcher或StrutsPrepareAndExecuteFilter(在较新版本中)作为Servlet Filter来拦截请求,进行预处理和执行动作。 10. **异常处理**:Struts2提供了强大的异常处理...

    struts2源码解析,个人感觉很不错

    ### Struts2源码解析及工作原理 #### Struts2简介 Struts2是一个流行的Java Web应用程序框架,它继承和发展了Struts1.x的一些特性,同时又采用了WebWork框架的核心技术,使得Struts2在设计理念和技术实现上都有了...

    struts2数据封装源码

    这个"struts2数据封装源码"很可能是为了演示如何在Struts2框架下处理用户输入数据并进行封装的过程。在Struts2中,数据封装是通过Action类和模型对象(通常称为POJOs,Plain Old Java Objects)来实现的,这使得业务...

    struts2列子源码

    拦截器是Struts2的一大特色,它们是可插拔的组件,可以拦截Action调用前后执行一些预处理或后处理任务。例如,我们可能在示例中看到登录验证、日志记录等自定义拦截器的实现。 6. **OGNL表达式语言** Struts2使用...

    Struts 2权威指南源码.rar

    这个"Struts 2权威指南源码.rar"压缩包文件包含了《Struts 2权威指南》一书中的示例代码,这些源码对于学习和理解Struts 2框架的工作原理非常有帮助。 在深入探讨源码之前,让我们先了解Struts 2的核心特性: 1. *...

    struts 2.0源码2

    这个"Struts 2.0源码2.rar"压缩包包含了多个关于Struts 2.0深入学习的视频教程,主要涉及以下几个关键知识点: 1. **声明式验证**:在Struts 2中,验证可以通过编写XML配置文件或使用注解实现,这种方式称为声明式...

    Struts2视频教程

    #### 二、Struts2入门案例 - **Hello Struts2**:通过创建一个简单的“Hello World”示例,帮助开发者快速上手Struts2的基本配置和运行流程。 - **Action类详解**:Action类是Struts2的核心组件之一,负责处理用户...

    struts2框架模拟-教学示范代码

    在“mvcch2”这个目录中,可能包含了Struts2 MVC实现的第二章内容,可能包括以下部分: 1. **Action类**:展示了如何创建Action类,以及如何在Action类中定义业务逻辑方法。 2. **配置文件**:可能包含struts.xml...

    java struts2上课源码2

    - **Interceptor(拦截器)**:是Struts2强大的特性,可以实现预处理和后处理功能,如日志记录、权限验证等。 - **配置文件**:`struts.xml`是Struts2的主要配置文件,定义了Action、Result、Interceptor等配置。 ...

    shiro kaptcha struts2 源码

    - **Interceptor**:拦截器是Struts2 的一大特色,可以在Action调用前后执行预处理和后处理,实现AOP(面向切面编程)的功能。 - **结果类型**:Result 负责将Action的执行结果转发到指定的视图,如JSP、...

    struts2-showcase.rar

    Struts2-showcase是一个用于演示和学习Apache Struts2框架功能的开源项目。这个压缩包“struts2-showcase.rar”包含了完整的源代码,旨在帮助开发者深入理解Struts2框架的工作原理及其各种特性。以下是对Struts2和...

    struts2 struts2.0.9 最全包下载

    Struts2是一个基于MVC(Model-View-Controller)设计模式的开源Java Web框架,由Apache软件基金会维护。Struts2作为Struts1的升级版,它提供了更强大的功能和更好的性能,使得开发者在构建动态、数据驱动的Web应用...

    Struts框架源码

    3. **Interceptor(拦截器)**:拦截器是Struts2的一个强大特性,它可以插入到Action调用的前后,执行一些预处理或后处理任务,如权限检查、日志记录等。 4. **Result**:Action执行后的结果,通常用来决定展示哪个...

    Struts2权威指南源码第4章

    拦截器是Struts2的一大特色,它们在Action执行前后进行预处理和后处理,提供了事务管理、权限验证、日志记录等功能。理解拦截器的工作原理和编写自定义拦截器是提升Struts2应用性能和功能的关键。 接着,我们会接触...

    struts 1 源码分析

    以上是对Struts 1.2源码分析的关键点,通过深入理解这些概念和机制,开发者可以更好地优化和维护基于Struts 1的应用程序,同时为学习其他MVC框架,如Spring MVC或Struts 2,打下坚实基础。然而,由于Struts 1的安全...

Global site tag (gtag.js) - Google Analytics