章节简言 |
上一章笔者写关于 Dispatcher 类如何处理接受来的 request 请求。当然读者们也知道他并非正真的执行 action 操作。他只是在执行 action 操作之前的准备工作。那么谁才是正真的执行 action 呢?本章笔者就带大家来看看 StrutsExecuteFilter 类的工作。在理解 StrutsExecuteFilter 类的工作之前,笔者还是希望大家回顾一下前一章讲到的 request 请求工作。为什么这样子讲呢?可以说 StrutsExecuteFilter 类的工作是建立在 StrutsPrepareFilter 类基础上运行的。先相信这一点笔者不需要声明了。笔者为了更好的理解小小的做一个张图片。如下
从上面的图片我们就是很清楚 StrutsPrepareFilter 类做了哪些工作。而图上的上五点对于后面的 StrutsExecuteFilter 类来讲是非常重要的。虽然我在前面几章也提过 StrutsExecuteFilter 类的知识。《》章节里面也讲过。只是很简单的略讲一下。并没有对他进特别的讲。主要是笔者认为不了解 StrutsPrepareFilter 类的工作的情况下,去了解 StrutsExecuteFilter 类的话。是一件比较吃力的事情。好了。笔者就不多说了。让我们进入本章的内容吧。
调结者的执行 action |
StrutsExecuteFilter 类的工作就是执行对应的 action 请求。StrutsExecuteFilter 类的工作还需要有一个叫 ExecuteOperations 类的帮助。如果看过源码的朋友都知道,StrutsExecuteFilter 类的代码里用了 ExecuteOperations 类的俩个方法。一个是:executeStaticResourceRequest 方法。一个是:executeAction 方法。光从字名面上我就知道他们的功能。executeStaticResourceRequest 是执行静态资源请求。如 JS 文件,CSS 文件等。而 executeAction 就是执行 action 请求。即是笔者想要讲的重点。好了。还是让我们先看一下 StrutsExecuteFilter 类代码吧。如下部分代码
StrutsExecuteFilter 类:
- 1 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
- ServletException {
- 2 3 HttpServletRequest request = (HttpServletRequest) req;
- 4 HttpServletResponse response = (HttpServletResponse) res;
- 5 6
- if (excludeUrl(request)) { //用于判断是否在排除的action之内。如果是就跳过。
- 7 chain.doFilter(request, response);
- 8
- return;
- 9
- }
- 10 11
- if (execute == null) {
- 12 lazyInit(); //初始化相关的信息类。
- 13
- }
- 14 15 ActionMapping mapping = prepare.findActionMapping(request, response); //找到ActionMapping实例
- 16 17 18 Integer recursionCounter = (Integer) request.getAttribute(PrepareOperations.CLEANUP_RECURSION_COUNTER);
- 19 20
- if (mapping == null || recursionCounter > 1) {
- 21 boolean handled = execute.executeStaticResourceRequest(request, response); //执行请求css,js文件。并返回是否成功。
- 22
- if (!handled) {
- 23 chain.doFilter(request, response);
- 24
- }
- 25
- } else {
- 26 execute.executeAction(request, response, mapping); //执行action请求,重要部分
- 27
- }
- 28
- }
根据上面的红色的代码,让笔者讲一下总共做了几件事件。
1. 判断当前的 request 请求是不是被排在外。如果就跳过去。(笔者不想过讲,太简单了)
2. 判断是否存在 ExecuteOperations 类的实例。如果没有就初始化。相关的代码如下。
StrutsExecuteFilter 类:
- 1
- /**
- 2 * 加载并初始化
- 3 */
- 4 protected synchronized void lazyInit() {
- 5
- if (execute == null) {
- 6 InitOperations init = new InitOperations(); //用于初始化的功能类
- 7 Dispatcher dispatcher = init.findDispatcherOnThread(); //StrutsPrepareFilter类的时候,就把Dispatcher实例存放在本地线程里面。这是只是把他拿出来。
- 8 init.initStaticContentLoader(new FilterHostConfig(filterConfig), dispatcher); //初始化用于加载css,js文件的加载类。
- 9 10 prepare = new PrepareOperations(dispatcher);
- 11 execute = new ExecuteOperations(dispatcher);
- 12
- }
- 13 14
- }
看了代码我们就知道 StrutsExecuteFilter 类的 lazyInit 方法做了什么。
1). 找到对应的 Dispatcher 实例。那么 Dispatcher 实例在哪里初始化呢?这就是 StrutsPrepareFilter 类的里面。(如果不理解的读者,请转至的章节)
2). 初始化 StaticContentLoader 类。即是用于加载 JS,CSS 文件等类似的加载类。
3). 初始化相关对应的 PrepareOperations 类和 ExecuteOperations 类。为了下面执行 action 请求准备。其中 ExecuteOperations 类很重要。用于执行 action 和加载 JS,CSS 文件类似的调动者。
3. 找到对应的 action 映射 (ActionMapping 类)。可以说没有 action 映射就没有办法执行相关的 action 操作。让我们看一下 findActionMapping 方法的代码吧。
PrepareOperations 类:
- public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
- ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
- if (mapping == null || forceLookup) {
- try {
- mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
- if (mapping != null) {
- request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
- }
- } catch(Exception ex) {
- dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
- }
- }
- return mapping;
- }
先从 request 请求中找到以 STRUTS_ACTION_MAPPING_KEY 常量为 Key 的 ActionMapping 值。如果不存在,则通过 Container 容器中找到的 ActionMapper 实例,并通过 ActionMapper 实例找到对应的 Action 映射,并存于 request 请求。其 Key 值为 STRUTS_ACTION_MAPPING_KEY 常量。相信读者又看 Dispatcher 类的实例了。又跟他有关系。关于这一步其实在 StrutsPrepareFilter 类工作的时候就已经做过一次了。(在这里用到 ActionMapper 类。关于他的作用读者目前只要知道所有的 struts.xml 上的配置 action 信息都在里面。笔者后面说找一个章节讲他)
4. 如果没有找到对应的 action 映射 (ActionMapping 类) 或 action 跳越的数量 > 1 就是执行加载 JS,CSS 文件的加载类。否则就是执行 action。实话实说笔者真不知道 recursionCounter > 1 是什么个意思。我只能把他理解为跳转的 action 数。笔者也做了相关通的实验就是希望看出一些事端。可惜失败了。
先看一下 executeStaticResourceRequest 方法吧。对于 executeStaticResourceRequest 方法。笔者在上面就讲到了。他是用于加载相关的静态资源。如 CSS 文件,JS 文件。这些文件是在 JAR 里面的。我们有时候 struts2 相关的 UI 的 TAG 的时候,就要加载对应的 CSS 文件,和 JS 文件吧。这个时候他就启作用了。让我们看一下代码吧。
ExecuteOperations 类:
- 1 public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException,
- ServletException {
- 2 // 如果没有找到对应的action,我们应该看一下是不是请求静态资源
- 3 String resourcePath = RequestUtils.getServletPath(request);
- 4 5
- if ("".equals(resourcePath) && null != request.getPathInfo()) {
- 6 resourcePath = request.getPathInfo();
- 7
- }
- 8 9 StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
- 10
- if (staticResourceLoader.canHandle(resourcePath)) {
- 11 staticResourceLoader.findStaticResource(resourcePath, request, response);
- 12
- return true;
- 13 14
- } else {
- 15 // 如果不是的话,就表示他是一个普通的请求
- 16
- return false;
- 17
- }
- 18
- }
因为这部分不是笔者这系列要讲的重点。如果有兴趣的读者可以自行继续研发下去。我们可以看又是跟 Dispatcher 类的实例有关系。相信读者这个时候很能明白笔者为什么说 Dispatcher 类很重要。很能做很多事情。
关于执行 action 的部分就在 executeAction 方法里面。让我们看一下代码吧。
ExecuteOperations 类:
- 1 public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
- 2 dispatcher.serviceAction(request, response, mapping);
- 3
- }
好吧。我有一种打人的冲动。Dispatcher 类的实例又出现。执行 request 请求的 action 也是 Dispatcher 类的实例来完成的。既然如此让我们看一下代码吧。如下
- 1 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) 2 throws ServletException {
- 3 4 Map extraContext = createContextMap(request, response, mapping);
- 5 6 //如果之前就有了值栈,就是新建一个新的值栈,放入extraContext
- 7 ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
- 8 boolean nullStack = stack == null;
- 9
- if (nullStack) {
- 10 ActionContext ctx = ActionContext.getContext();
- 11
- if (ctx != null) {
- 12 stack = ctx.getValueStack();
- 13
- }
- 14
- }
- 15
- if (stack != null) {
- 16 extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
- 17
- }
- 18 19 String timerKey = "Handling request from Dispatcher";
- 20
- try {
- 21 UtilTimerStack.push(timerKey);
- 22 String namespace = mapping.getNamespace(); //获得request请求里面的命名空间,即是struts.xml是的package节点元素
- 23 String name = mapping.getName(); //获得request请求里面的action名
- 24 String method = mapping.getMethod(); //要执行action的方法
- 25 26 ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, 27 method, extraContext, true, false); //获得action的代理
- 28 29 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
- 30 31 // 如果action映射是直接就跳转到网页的话,
- 32
- if (mapping.getResult() != null) {
- 33 Result result = mapping.getResult();
- 34 result.execute(proxy.getInvocation());
- 35
- } else {
- 36 proxy.execute(); //这里就是执行action
- 37
- }
- 38 39 40 41
- if (!nullStack) {
- 42 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
- 43
- }
- 44
- } catch(ConfigurationException e) {
- 45 logConfigurationException(request, e);
- 46 sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
- 47
- } catch(Exception e) {
- 48
- if (handleException || devMode) {
- 49 sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
- 50
- } else {
- 51
- throw new ServletException(e);
- 52
- }
- 53
- } finally {
- 54 UtilTimerStack.pop(timerKey);
- 55
- }
- 56
- }
从上面的代码就能看出在执行 action 的内部还需要用到一个叫 ActionProxy 类。关于这部分知识,笔者其实这里不想讲的很细。主要这部分的知识太多了。但这里笔者还是希望为后面的章节做好准备。ActionProxy 类可以理解他是一个代理。他的主要目地就是根据 action 映射得到的信息,寻找对应 action 类实例,然后执行对应的方法。其中包括加载对应的拦截器,初始化相应的结果。而这段代码中,在 ActionProxy 类的 execute() 方法的时候,还作了相应的判断。即是是否直接回返结果。其次还有在讲到一个关于值栈的知识。这里在获得 ActionProxy 类实例的时候,需要得到对应值栈的信息。但是不管如何,最后一定会把 request 请求的值栈重新更新一下。ValueStack(值栈) 的作用相信大家都懂。我就不做过多的讲解了。
本章总结 |
可以说相关 Dispatcher 类的知识点,到本章节算是结束了。笔者把 Dispatcher 类的功能分为三点:一是加载 struts2 运行的必要条件信息;二是初始化 action 请求需要的信息;三是执行 request 请求对应的 action。而关于核心机制图片的橙色部分的工作大部分笔者都有体现出来。而后面的章节都是为了这三个功能点进行的。所以希望读者能理解这三个功能。
来源: http://www.cnblogs.com/hayasi/p/5841721.html