struts2 部分源码分析

来源:互联网 发布:亚马逊对网络的要求 编辑:程序博客网 时间:2024/05/05 18:22

Struts2是个非常优秀的开源框架,最近抽空看了一下源码,然后从互联网看了一下大家的总结,发现随着版本的更新,人的思想也需要及时更新啊。 关于其主要的工作原理,我也不想在阐述更多的了,如果你比较熟悉servlet,理解起来会更加容易,可以参考看一下CSDN的这篇文章: 参考文章
大概的工作原理从图片中描述得很清楚了。 在总结的过程发现Struts的版本更新也很快,web.xml文件中配置的过滤在2.1.2版本之前都是配置FilterDispatcher,但是在之后版本配置的是一个全新的过滤器:StrutsPrepareAndExecuteFilter,从字面上看貌似后者必前者功能更加强大。FilterDispatcher是struts2.0.x到2.1.2版本的核心过滤器。StrutsPrepareAndExecuteFilter是自2.1.3开始就替代了FilterDispatcher的。这样的改革当然是有好处的。我们自己定义过滤器的话, 是要放在strtus2的过滤器之前的, 如果放在struts2过滤器之后,你自己的过滤器对action的过滤作用就废了。
在web.xml中是这样配置的:

    <filter>        <filter-name>struts2</filter-name>        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>    </filter>    <filter-mapping>        <filter-name>struts2</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>

在该过滤器中有一个初始化的方法 init(FilterConfig filterConfig);看看其作用

   public void init(FilterConfig filterConfig) throws ServletException {        //这只是一个普通类,封装下面的初始化函数,可以当做工具类一样        InitOperations init = new InitOperations();        try {            //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在Iterator中,在后面的初始化dispatcher中使用            FilterHostConfig config = new FilterHostConfig(filterConfig);            //初始化strut的内部日志            init.initLogging(config);            //初始化dispatcher             Dispatcher dispatcher = init.initDispatcher(config);            //加载静态内容加载器            init.initStaticContentLoader(config, dispatcher);            //初始化上面定义的prepare ,execute ,会在下面doFilter中使用到,看起参数,放置了两个重要的东西            prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);            execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);             //这是一个回调方法            postInit(dispatcher, filterConfig);        } finally {            init.cleanup();        }    }

创建dispatcher对象,并初始化

 public Dispatcher initDispatcher( HostConfig filterConfig ) { //相当于初始两个参数ServletContext servletContext和Map<String, String> initParams        Dispatcher dispatcher = createDispatcher(filterConfig);            //包括初始换文件管理器,default.properties文件提供者,struts-default.xml,struts-          //plugin.xml,struts.xml等文件,用户自己实现的ConfigurationProviders类,Filter的初始化参数等           dispatcher.init();        return dispatcher;    }  //将filterConfig 对象中参数去除并封装,然后new对象   private Dispatcher createDispatcher( HostConfig filterConfig ) {        Map<String, String> params = new HashMap<String, String>();        for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {            String name = (String) e.next();            String value = filterConfig.getInitParameter(name);            params.put(name, value);        }        return new Dispatcher(filterConfig.getServletContext(), params);    }

总体上来说,这个init初始化还是做了很多事情的,大致会根据配置来初始换参数,初始化适配器,初始化加载各种文件等。

下面doFilter是过滤器的执行方法,它拦截提交的HttpServletRequest请求,HttpServletResponse响应,作为strtus2的核心拦截器,在doFilter里面也做了很多工作,具体如下:

//设置编码和国际化,底层执行 dispatcher.prepare(request, response);方法很简单只是设置了//encoding 、locale ,做的只是一些辅助的工作 prepare.setEncodingAndLocale(request, response);  //创建Action上下文(重点) prepare.createActionContext(request, response);

创建ActionContext,ActionContext本来是一个容器,主要存储request、session、application、parameters等相关信息,并且它是一个线程的本地变量,不会造成线程之间的变量分享问题,这意味着不同的action之间不会共享一个ActionContext,当然不会存在线程安全的问题,

 //创建Action上下文,初始化thread local 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;        }         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保存到ThreadLocal        ActionContext.setContext(ctx);        return ctx;    }

dispatcher是怎么createContextMap的呢?

  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;    }

可以看到这里获取得到request和context中获取相关参数,封装为map,其中

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

再次将各个参数map再封装到另外Map中,得到extraContext变量。这里的ActionMapping mapping为空,不会做任何处理。

在prepare将适配器分给线程之后,开始查找本次请求的ActionMapping

ActionMapping mapping = prepare.findActionMapping(request, response, true);

这里暂时不详细讨论是怎么根据request相关参数来形成ActionMapping 的,但是我们应该有感觉,上面已经加载了struts的相关xml配置文件,这部分另外介绍。在找到ActionMapping 之后就是执行action了,execute.executeAction(request, response, mapping);这部分详细见后面一章吧。

0 0
原创粉丝点击