struts2源码初读(二)预处理

来源:互联网 发布:苹果手机怎样开4g网络 编辑:程序博客网 时间:2024/06/07 06:33

下面开始浏览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_USstruts.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方法中又调用

request = dispatcher.wrapRequest(request, servletContext);/** * 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的预处理到此就结束了。

原创粉丝点击