struts2源码的自我理解

1. 概述

    Struts2是Apache提供的一个免费的重量级web层框架,其采用mvc设计模式。Struts2底层依赖的是WebWork。
**MVC设计模式:**
    MVC是指从web层的角度看待整个web应用,可以将web应用分为3个部分:Model,view,Controller。
        View 视图:            jsp         web层 
        Model 模型:       JavaBean        业务层,持久层
        Controller 控制器: servlet     web层

2.使用

    使用Struts2框架时,只需要在web.xml文件中注册struts2的核心过滤器并配置struts.xml文件,就能通过继承ActionSupport类来使用Struts2的功能。
  <!-- struts2核心过滤器 -->
  <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>

3.Struts2框架的初始化:

    注册StrutsPrepareAndExecuteFilter后,在web应用启动时,会先执行过滤器的init方法,对struts2进行初始化配置
    public void init(FilterConfig filterConfig) throws ServletException {
        //初始化器
        //为struts2框架设置初始化参数
        InitOperations init = new InitOperations();
        //调度器
        //核心类
        Dispatcher dispatcher = null;
        try {
            //使用filterConfig构建struts2内部的config文件
            //类似于装饰者模式,但父接口为HostConfig
            //其缺少一个getFilterName(),因为没有必要
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            //使用config文件进行初始化
            init.initLogging(config);
            //使用config文件对dispatcher进行初始化
            dispatcher = init.initDispatcher(config);
            //返回一个StaticContentLoader,但是没有保存这个对象的引用
            //也没有静态方法可以获得其引用
            //浪费内存?
            init.initStaticContentLoader(config, dispatcher);

            //创建预执行器
            prepare = new PrepareOperations(dispatcher);
            //创建执行器
            execute = new ExecuteOperations(dispatcher);
            //创建排除的正则表达式目录
            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
            //为他人扩展提供便利
            postInit(dispatcher, filterConfig);
        } finally {
            if (dispatcher != null) {
                //将ContainerHolder中缓存的container信息清除,节省内存
                //因为是线程特有数据(ThreadLocal)
                dispatcher.cleanUpAfterInit();
            }
            //将初始化时,存入ActionContext中的数据清空,节省内存
            //因为是线程特有数据(ThreadLocal)
            init.cleanup();
        }
    }
    protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
    }
3.1 init.initLogging(config);
    该方法实现了对服务器日志文件的整合
    public void initLogging( HostConfig filterConfig ) {
        //获取服务器的loggerFactory
        String factoryName = filterConfig.getInitParameter("loggerFactory");
        if (factoryName != null) {
            try {
                //使用这个类的classLoader,来加载loggerFactory
                Class cls = ClassLoaderUtil.loadClass(factoryName, this.getClass());
                LoggerFactory fac = (LoggerFactory) cls.newInstance();
                //将对象的引用设置在静态变量中
                //方便以后使用静态方法获取
                LoggerFactory.setLoggerFactory(fac);
            } catch ( InstantiationException e ) {
                System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default");
                e.printStackTrace();
            } catch ( IllegalAccessException e ) {
                System.err.println("Unable to access logger factory: " + factoryName + ", using default");
                e.printStackTrace();
            } catch ( ClassNotFoundException e ) {
                System.err.println("Unable to locate logger factory class: " + factoryName + ", using default");
                e.printStackTrace();
            }
        }
    }
3.2 init.initDispatcher(config);
    使用config中的数据对dispachter进行初始化
    public Dispatcher initDispatcher( HostConfig filterConfig ) {
        //创建一个dispachter
        Dispatcher dispatcher = createDispatcher(filterConfig);
        //执行dispacher的初始化设置
        dispatcher.init();
        return dispatcher;
}
    private Dispatcher createDispatcher( HostConfig filterConfig ) {
        //获取config里面的所有参数
        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);
        }
        //将servletContext和所有参数传递给dispatcher的构造器
        return new Dispatcher(filterConfig.getServletContext(), params);
    }
3.3 init.buildExcludedPatternsList(dispatcher);
    从配置文件中读取不需要使用struts2的路径
    public List<Pattern> buildExcludedPatternsList( Dispatcher dispatcher ) {
        return buildExcludedPatternsList(dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN));
    }

    private List<Pattern> buildExcludedPatternsList( String patterns ) {
        if (null != patterns && patterns.trim().length() != 0) {
            List<Pattern> list = new ArrayList<Pattern>();
            String[] tokens = patterns.split(",");
            for ( String token : tokens ) {
                list.add(Pattern.compile(token.trim()));
            }
            return Collections.unmodifiableList(list);
        } else {
            return null;
        }
}
    dispachter,prepare和execute的构造方法都十分简单,就不一一列出源码了。
3.4 dispatcher.init();
    是一个十分重要的方法。对dispachter的相关参数,容器的参数等进行设置。
    public void init() {

        if (configurationManager == null) {
            //创建一个默认的configurationManager,xwrok中的类
            configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);//DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME="struts"
        }

        try {
            //将相关参数设置到configurationManager中
            //所有的ContainerProvider
            init_FileManager();
            init_DefaultProperties(); // [1]
            init_TraditionalXmlConfigurations(); // [2]
            init_LegacyStrutsProperties(); // [3]
            init_CustomConfigurationProviders(); // [5]
            init_FilterInitParameters() ; // [6]
            init_AliasStandardObjects() ; // [7]

            //xwork中的类
            //即在ConfigurationManager中配置的List<ContainerProvider>
            //上面7个init的配置内容的总和
            //即容器本身
            Container container = init_PreloadConfiguration();
            //将dispatcher注入配置好的容器中
            container.inject(this);
            //检查是否需要支持WebLogic服务器的特殊设置
            init_CheckWebLogicWorkaround(container);

            //dispatcher的监听器,但在源码中并没有使用
            //可能是扩展用的
            if (!dispatcherListeners.isEmpty()) {
                for (DispatcherListener l : dispatcherListeners) {
                    l.dispatcherInitialized(this);
                }
            }
            //从头到尾没有看见赋值
            //应该报NullPointerException啊?
            //可能与configurationManager或container有关
            errorHandler.init(servletContext);

        } catch (Exception ex) {
            if (LOG.isErrorEnabled())
                LOG.error("Dispatcher initialization failed", ex);
            throw new StrutsException(ex);
        }
}

4.执行action请求

    每次有请求到来就会执行过滤器的doFilter方法,对请求相关数据进行包装
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                //是排除的请求地址
                chain.doFilter(request, response);
            } else {
                //不是
                //设置请求响应编码
                //底层依赖dispatcher的prepare(request, response)方法
                prepare.setEncodingAndLocale(request, response);
                //给值栈和ActionContext域赋值
                prepare.createActionContext(request, response);
                //将dispatcher与线程绑定
                //方便静态方法获取
                //底层依赖Dispatcher.setInstance(dispatcher)静态方法
                prepare.assignDispatcherToThread();
                //替换request
                //底层依赖dispatcher的wrapRequest(request)方法
                request = prepare.wrapRequest(request);
                //获取对应的解析结果对象
                ActionMapping mapping = prepare.findActionMapping(request, response, true);
                if (mapping == null) {
                    //true:struts2自己的一些东西
                    //false:只是普通的静态资源
                    boolean handled = execute.executeStaticResourceRequest(request, response);
                    if (!handled) {
                        //是静态资源
                        chain.doFilter(request, response);
                    }
                } else {
                    //执行action
//底层依赖dispatcher.serviceAction(request, response, mapping)
                    execute.executeAction(request, response, mapping);
                }
            }
        } finally {
            //清除action的相关缓存资源
            prepare.cleanupRequest(request);
        }
}
    由于prepare和execute的许多方法都是依赖于底层的dispatcher,所以只列出部分方法
4.1 prepare.createActionContext(request, response);
    根据request,response创建相应的ActionContext
    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
        ActionContext ctx;
        //需要循环递归的次数
        //不大明白存在的意义
        Integer counter = 1;
        //初次访问时为null
        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();
            //将request,session,application,page放入值栈
            //供OGNL表达式使用
            stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
            //将值栈中的数据放入ActionContext域
            //供Action使用
            ctx = new ActionContext(stack.getContext());
        }
        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
        //在Action中使用ActionContext的静态方法获取
        //使用了ThreadLocal<ActionContext>避免多线程问题
        ActionContext.setContext(ctx);
        return ctx;
}
4.2 ActionMapping mapping = prepare.findActionMapping(request, response, true);
    使用ActionMapper类对url地址进行解析,使其变为ActionMapping对象
        public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
        //ActionMapping代表的是一个请求的所有数据
        //http://localhost:8080/sss/login.action?clzjxkl=asda
        //namespace   name   params   method GET/POST
        //ActionMapper:创建ActionMapping的类
        //对url的进行解析
        ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);    //STRUTS_ACTION_MAPPING_KEY = "struts.actionMapping"
        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;
}
4.3 dispatcher.prepare(request, response)
    对request和response进行相关设置
    public void prepare(HttpServletRequest request, HttpServletResponse response) {
        String encoding = null;
        //有默认编码就设置为默认编码
        if (defaultEncoding != null) {
            encoding = defaultEncoding;
        }
        // check for Ajax request to use UTF-8 encoding strictly http://www.w3.org/TR/XMLHttpRequest/#the-send-method
        //是ajax请求就设置为utf-8
        if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
            encoding = "UTF-8";
        }

        Locale locale = null;
        //有默认语言环境就设置为默认值
        if (defaultLocale != null) {
            locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
        }

        if (encoding != null) {
            //当有默认编码,或是ajax请求时执行
            //为请求设置编码
            applyEncoding(request, encoding);
        }

        if (locale != null) {
            //当有默认环境时执行
            //为相应设置环境
            response.setLocale(locale);
        }

        //当服务器是WebLogic时执行
        if (paramsWorkaroundEnabled) {
            request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request
        }
}
4.4 dispatcher.createContextMap(request, response, null)
    根据相关数据创建ContextMap,返回后设置在ActionContext中
    public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
            ActionMapping mapping) {

        // request map wrapping the http request objects
        //request域中的数据
        Map requestMap = new RequestMap(request);

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

        // session map wrapping the http session
        //session域中的数据
        Map session = new SessionMap(request);

        // application map wrapping the ServletContext
        //application域中的数据
        Map application = new ApplicationMap(servletContext);

        //生成值栈中的数据
        Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response);

        if (mapping != null) {
            extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
        }
        return extraContext;
    }
4.5 dispatcher.wrapRequest(request)
    替换request,如果有必要
    public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
        // don't wrap more than once
        //是StrutsRequestWrapper类的子类
        if (request instanceof StrutsRequestWrapper) {
            return request;
        }

        String content_type = request.getContentType();
        //判断是否是上传
        if (content_type != null && content_type.contains("multipart/form-data")) {
            //底层依赖
            MultiPartRequest mpr = getMultiPartRequest();
            LocaleProvider provider = getContainer().getInstance(LocaleProvider.class);
            //供用户使用的request
            request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup);
        } else {
            request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
        }

        return request;
    }
4.6 dispatcher.serviceAction(request, response, mapping)
    执行请求的核心代码,通过创建代理来执行请求
    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
            throws ServletException {

        //将四大域拷贝一份,用于构建代理
        Map<String, Object> extraContext = createContextMap(request, response, mapping);

        // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
        //取出值栈
        ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
        boolean nullStack = stack == null;
        if (nullStack) {
            ActionContext ctx = ActionContext.getContext();
            if (ctx != null) {
                stack = ctx.getValueStack();
            }
        }
        if (stack != null) {
            //将值栈放入容器中
            extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
        }

        String timerKey = "Handling request from Dispatcher";
        try {
            UtilTimerStack.push(timerKey);
            String namespace = mapping.getNamespace();
            String name = mapping.getName();
            String method = mapping.getMethod();

            //创建一个action代理
            ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, method, extraContext, true, false);
            //将代理的值栈放入request
            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

            // if the ActionMapping says to go straight to a result, do it!
            if (mapping.getResult() != null) {//这个请求已经被执行过了
                Result result = mapping.getResult();
                result.execute(proxy.getInvocation());
            } else {//执行代理
                proxy.execute();
            }

            // If there was a previous value stack then set it back onto the request
            //用原来的值栈覆盖掉代理的值栈
            //是同一个对象
            //没什么用啊?
            if (!nullStack) {
                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
            }
        } catch (ConfigurationException e) {
            logConfigurationException(request, e);
            sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
        } catch (Exception e) {
            if (handleException || devMode) {
                sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
            } else {
                throw new ServletException(e);
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }

以上就是小弟对struts2框架执行过程的了解,有什么不对的地方还请各位大神指点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值