struts2 部分源码分析

Struts2是个非常优秀的开源框架,最近抽空看了一下源码,然后从互联网看了一下大家的总结,发现随着版本的更新,人的思想也需要及时更新啊。 关于其主要的工作原理,我也不想在阐述更多的了,如果你比较熟悉servlet,理解起来会更加容易,可以参考看一下优快云的这篇文章: 参考文章
大概的工作原理从图片中描述得很清楚了。 在总结的过程发现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);这部分详细见后面一章吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值