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

时间:2023-03-08 18:40:33

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管理的,因为默认sprin*生的实例都是单例的,而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。。