Tomcat容器运行struts2+spring+mybatis架构的java web应用程序简单分析

1、具体的环境为

MyEclipse 8.5以及自带的tomcat

spring3.0.5

struts2.3.15.1

mybatis3.0.5

 

2、想弄明白的一些问题

tomcat集成spring,那么spring是如何启动的?

spring是如何读取配置文件的?

spring是如何处理依赖关系的?

应该说所有的bean都是spring实例化的,那么Action的实例是如何产生的?

 

3、spring是如何启动的?

了解了tomcat的启动过程(容器engine-host-context-wrapper一级一级启动,并且通过事件如ContextConfig等来读取各自应用的web.xml来初始化context),
读了web.xml中的配置,spring也是通过事件来启动的,配置如下:

    <listener>
        <listener-class>
            org.springframework.web.util.Log4jConfigListener
        </listener-class>
    </listener>    
    
     <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

注意的是,如果配置了Log4jConfigListener,则Log4jConfigListener的配置要先于ContextLoaderListener的配置(spring3.0.5源码注释得知)。

 

4、spring启动后做了哪些事情?

通过debug3.0.5的源码,spring解析所有的配置文件,配置文件路径可以如下配置:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext*.xml</param-value>
    </context-param>

然后,spring可以得到该应用共有多少个bean(如现在的项目,大概是200+个)

这里,spring还会解析所有的*Sql.xml文件,得到所有的statement mapper
.
.
.

 

5、Action的实例是如何产生的?也是spring进行管理的吗?

首先Action的实例化不是由spring管理的,因为默认spring产生的实例都是单例的,而Action为了避免多线程产生的不一致,是多例的。

Action的作用是通过Filter机制来实现的:

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

这样所有的请求都会经过这个filter,而如果url是以.action(可在struts.properties中配置)结尾的,则会根据struts*.xml文件中的映射关系new出一个Action
进行处理。

5.1、

首先web application启动时,在初始化spring后,会调用org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.init(FilterConfig filterConfig) throws ServletException方法,在Dispatcher.java的init方法中会读取解析struts*xml配置文件:

   /**
     * Load configurations, including both XML and zero-configuration strategies,
     * and update optional settings, including whether to reload configurations and resource files.
     */
    public void init() {

        if (configurationManager == null) {
            configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
        }

        try {
            init_FileManager();
            init_DefaultProperties(); // [1]
            init_TraditionalXmlConfigurations(); // [2]
            init_LegacyStrutsProperties(); // [3]
            init_CustomConfigurationProviders(); // [5]
            init_FilterInitParameters() ; // [6]
            init_AliasStandardObjects() ; // [7]

            Container container = init_PreloadConfiguration();
            container.inject(this);
            init_CheckWebLogicWorkaround(container);

            if (!dispatcherListeners.isEmpty()) {
                for (DispatcherListener l : dispatcherListeners) {
                    l.dispatcherInitialized(this);
                }
            }
        } catch (Exception ex) {
            if (LOG.isErrorEnabled())
                LOG.error("Dispatcher initialization failed", ex);
            throw new StrutsException(ex);
        }
    }

然后通过*Provider的register方法来读取各种配置文件(struts默认的以及自己配置的)
如DefaultPropertiesProvider、XmlConfigrationProvider...

 

注意:和spring有一点不同,struts的ConfigurationManager只会解析struts.xml这个文件(可以配置指定特定名称),如果我们建立了另外的xml配置文件,
如struts-wms.xml,则需要在struts.xml中如下配置,
<include file="struts-wms.xml"></include>
配置文件的包含关系可以多层级的。

5.2、

在每次请求到来的时候,会调用void org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException这个方法,

        //首先,判断这个请求url是不是在struts2处理的排除范围内,如果是,则继续调用其他filter进行处理
            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                chain.doFilter(request, response);
            } else {
                request = prepare.wrapRequest(request);
        //根据请求url得到对应的action mapping
                ActionMapping mapping = prepare.findActionMapping(request, response, true);
        //如果action mapping为空,则调用其他的filter进行处理
                if (mapping == null) {
                    boolean handled = execute.executeStaticResourceRequest(request, response);
                    if (!handled) {
                        chain.doFilter(request, response);
                    }
                } else {
            //否则调用action mapping对应的Action进行处理
                    execute.executeAction(request, response, mapping);
                }
            }

 

一点猜想:
在调用完所有的filter之后,如果是由对应Action处理,则由Action处理完成后,通过向response中设值,交由tomcat处理后返回客户端;
如果不是,如图片/js等静态资源,应该是有Tomcat分派的处理线程调用默认的DefaultServlet返回相应资源。
结论是,不论是不是由Action处理,还是tomcat内部直接处理,只要通过封装了request和response,最后就一定可以通过tomcat的处理返回结果到客户端。。

 

6、Action中使用到的*Service可以通过@Autowired自动注入,这是通过什么样的机制??

ActionContext会new出对应的Action,那么Action中的字段如*Service是通过什么样的机制注入的?猜想是通过某个Interceptor。
但是,通过在*.java文件中搜索"Autowired.class",结果为0,意识到*Service的注入可能不是通过Interceptor。


在通过debug查看StrutsActionProxy中的DefaultActionInvocation字段中的*Action示例,发现*Service都是实例化过后的。

现在重点是弄明白StrutsActionProxy是如何实例化的?
通过这个类Object com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception
来实例化Action并注入*Service的,其中会得到spring的BeanFactory的实现进而得到所有的由spring管理的*Service。。

Tomcat容器运行struts2+spring+mybatis架构的java web应用程序简单分析,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。