第一步:在tomcat启动的时候

1、在tomcat启动的时候,首先会加载struts2的核心过滤器StrutsPrepareAndExecuteFilter

<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()方法,用于在启动tomcat的时候初始化struts2用的

 public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
dispatcher = init.initDispatcher(config);//用于加载配置文件
init.initStaticContentLoader(config, dispatcher);//用于静态注入 prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
dispatcher.cleanUpAfterInit();
}
init.cleanup();
}
}

首先我们去看一下如何去加载配置文件,打开下面方法的源代码

 dispatcher = init.initDispatcher(config);//用于加载配置文件

就会进入initDispatcher方法,在打开注释对应的源代码

 public Dispatcher initDispatcher( HostConfig filterConfig ) {

        Dispatcher dispatcher = createDispatcher(filterConfig);

        dispatcher.init();//打开他的源代码

        return dispatcher;

}

然后你就会发现,在这个init方法中

public void init() {

        if (configurationManager == null) {
configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
} try {
        //有兴趣的朋友可以自己再去看更深层次的源代码,这里就不深究了
init_FileManager();
init_DefaultProperties(); // [1]//加载struts.properties配置文件
init_TraditionalXmlConfigurations(); // [2]//按照顺序加载下面三个配置文件struts-default.xml(一个),struts-plugin.xml(可能有多个),struts.xml(一个)
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);
}
}

看完加载配置文件以后,在回到我们的StrutsPrepareAndExecuteFilter核心过滤器,进入下面方法的源代码

public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
Dispatcher dispatcher = null;
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
init.initStaticContentLoader(config, dispatcher);//用于静态注入
}

这个方法是用来静态注入

静态注入:加载struts-defalut.xml里面的bean属性的java类,把这些类加载进入struts2

就是下面这些内容(部分)

<struts>
<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />
<bean type="com.opensymphony.xwork2.factory.ActionFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultActionFactory"/>
<bean type="com.opensymphony.xwork2.factory.ConverterFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultConverterFactory" />
<bean type="com.opensymphony.xwork2.factory.InterceptorFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultInterceptorFactory" />
<bean type="com.opensymphony.xwork2.factory.ValidatorFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultValidatorFactory" /> <bean type="com.opensymphony.xwork2.FileManager" class="com.opensymphony.xwork2.util.fs.DefaultFileManager" name="system" scope="singleton"/>
<bean type="com.opensymphony.xwork2.FileManagerFactory" class="com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory" name="struts" scope="singleton"/>
 ...................................

经过配置文件的加载和静态注入,struts2容器基本上就启动了

在请求一个url的时候

回到我们的过滤器,在请求url的时候,struts2首先会执行doFilter(ServletRequest, ServletResponse, FilterChain)方法

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 {
prepare.setEncodingAndLocale(request, response);
          //首先打开这个方法的源代码
prepare.createActionContext(request, response);//创建actionContext
prepare.assignDispatcherToThread();
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
//然后打开这个方法的源代码
execute.executeAction(request, response, mapping);//创建代理对象(代理对象下面我们做简单的说明)
}
}
} finally {
prepare.cleanupRequest(request);
}
}

在访问action之前,会先创建actionContext对象

 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;
} //在进入getContext()的源代码
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.setContext(ctx);
        return ctx;
  
public static ActionContext getContext() {
//在进get()的源代码
return actionContext.get();
}
public T get() {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//从当前线程中获取
if (map != null) {//如果当前线程中不存在,就创建,并且放入当前线程中
ThreadLocalMap.Entry e = map.getEntry(this);//到这里说明了actionContext存放在当前线程中,所以里面的数据是线程安全的
if (e != null)
return (T)e.value;
}
return setInitialValue();
}

通过上述的源代码分析,就可以看出struts2是如何创建actionContext的,并且在创建actionContext之前,值栈就已经创建好了,而且值栈里面map和actionContext里面的

map是一样的。

在看完了了如果创建actionContext以后,我们就应该去看如何去创建action代理对象了。创建action对象的方法有很多,一种是利用struts2本身的反射机制,通过ObjectFactory来创建action对象。第二个把创建对象委托给其他容器,例如spring。下面来说一下使用struts2本身反射机制来创建对象的过程

首先说一下struts2本身来创建action对象,使用代理的方法来生成action代理对象。这里的action是代理对象

在struts2中,拦截器成为aop的切面,而我们自己写的action里面的方法是目标方法(这里设计面向切面编程aop的思想,在这里就不在多叙述)

在struts2中,有一个核心类,为ObjectFactory,这个类负责所有struts2类的创建,我们可以看一下他的方法体系

这里面有buildAction(创建action对象),buildResult(创建结果集对象)等一系列创建struts2所需要对象的方法

也就是说,如果要使用其他容器来创建action,必须重新继承ObjectFactory,然后重新buildAction等方法。

首先我们打开executeAction这个方法

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

         ....................................................................
        
            //打开executeAction方法的源代码
execute.executeAction(request, response, mapping);
....................................................................
}

然后进入下面代码

public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
//在打开这个方法的源代码
dispatcher.serviceAction(request, response, servletContext, mapping);
}
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException { .................................................................
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod(); Configuration config = configurationManager.getConfiguration();
//获取action代理对象
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false); 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 {
         //打开execute()方法的源代码,要是DefaultActionProxy这个类下面的这个方法
proxy.execute();
}
.................................................................
}
public String execute() throws Exception {
ActionContext nestedContext = ActionContext.getContext();
ActionContext.setContext(invocation.getInvocationContext()); String retCode = null; String profileKey = "execute: ";
try {
UtilTimerStack.push(profileKey);
//进行invoke()源代码,invoke方法是DefaultActionInvocation类下面的
retCode = invocation.invoke();/
} finally {
if (cleanupContext) {
ActionContext.setContext(nestedContext);
}
UtilTimerStack.pop(profileKey);
} return retCode;
}

invoke方法就是拦截器struts2的关键方法,跟着注释走,可以看到是先执行拦截器,在执行action里面的方法,在执行结果集对象

public String invoke() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey); if (executed) {
throw new IllegalStateException("Action has already executed");
} //执行各种拦截器
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
resultCode = invokeActionOnly();//执行action里面的方法
} // this is needed because the result will be executed, then control will return to the Interceptor, which will
// return above and flow through again
if (!executed) {
if (preResultListeners != null) {
for (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener; String _profileKey = "preResultListener: ";
try {
UtilTimerStack.push(_profileKey);
listener.beforeResult(this, resultCode);
}
finally {
UtilTimerStack.pop(_profileKey);
}
}
} // now execute the result, if we're supposed to
if (proxy.getExecuteResult()) {
executeResult();//执行结果集对象
} executed = true;
} return resultCode;
}
finally {
UtilTimerStack.pop(profileKey);
}
}

通过结果集对象返回到页面,就执行了一次请求的过程。上面介绍的

如果是使用spring容器来作为action的产生,那么就需要对action里面ObjectFactory类继承继承,然后重写里面的方法

我们来看一下首先怎么覆盖ObjectFactory类,在上面,我说过,xml文件的加载顺序是:struts-default.xml,struts-plugin.xml,struts.xml

ObjectFactory在struts-default.xml默认加载的,查看静态注入可以发现,ObjectFactory被默认加载,如果想成spring的话,可以新建一个struts-plugin.xml

在里面把它替换掉

<struts>
<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>

下面把文件替换掉,新建一个struts-plugin.xml,这么ObjectFactory"加载就由spring产生,我们可以进入StrutsSpringObjectFactory源代码看看

<struts>
<!-- 在struts配置文件中引入spring,并且把由struts2自己产生action的 方法,变成由Spring容器产生,覆盖本身有struts2本身产生的ObjectFactory,进入class源代码 -->
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" /> <!-- Make the Spring object factory the automatic default -->
<constant name="struts.objectFactory" value="spring" /> <constant name="struts.class.reloading.watchList" value="" />
<constant name="struts.class.reloading.acceptClasses" value="" />
<constant name="struts.class.reloading.reloadConfig" value="false" /> <package name="spring-default">
<interceptors>
<interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
<interceptor name="sessionAutowiring" class="org.apache.struts2.spring.interceptor.SessionContextAutowiringInterceptor"/>
</interceptors>
</package>
</struts>

 // 进入 SpringObjectFactory
public class StrutsSpringObjectFactory extends SpringObjectFactory {
  
private static final Logger LOG = LoggerFactory.getLogger(StrutsSpringObjectFactory.class);

这样就进入了SpringObjectFactory,实现了ObjectFactory并且重写了ObjectFactory里面的一些方法

public class SpringObjectFactory extends ObjectFactory implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(SpringObjectFactory.class);

我可可以看一下的他的方法体系

在这些方法中,我们来看一下buildBean方法

public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
Object o; if (appContext.containsBean(beanName)) {
/*
首先从spring容器中获取action对象,如果获取不到
就利用反射机制去获取action,这里的反射机制,和struts2本身的反射机制应该差不多
*/
o = appContext.getBean(beanName);
} else {
//利用反射机制,实现创建action
Class beanClazz = getClassInstance(beanName);
o = buildBean(beanClazz, extraContext);
}
if (injectInternal) {
injectInternalBeans(o);
}
return o;
} 通过spring容器,只是action产生方式和以前不同而已,其余的步骤和方法都相同。我们就分析到这里了。大家可以试着跟着我的注释去找源码,然后设置断点,使用debug调试,很轻松
就可以得到结果。
我也才刚刚学到struts2和spring的整合,把老师上课讲的内容总结了一些,希望对求知的人有用。第一次写博客,写的不好,请大家见谅

浅谈:深入理解struts2的流程已经spring和struts2的整合的更多相关文章

  1. 前端工程化的的理解,浅谈web工程化的开发流程

    1. 什么是前端工程化 自有前端工程师这个称谓以来,前端的发展可谓是日新月异.相比较已经非常成熟的其他领域,前端虽是后起之秀,但其野蛮生长是其他领域不能比的.虽然前端技术飞快发展,但是前端整体的工程生 ...

  2. 浅谈Android的Activity运行流程(生命周期)

    关于Android的Activity运行流程,我们可以写一些程序来直观的查看Activity的运行流程.在这里我们使用Log工具来获取Activity运行日志.假如我们新建一个Android项目,Pr ...

  3. 浅谈Token理解运用

    周末没带电脑,闲着也是闲着,出来分享一点东西,也当自己学习和巩固了. 今天分享一下Token的理解,首先Token的定义是什么? 概念 Token被翻译成为('令牌','标记')在计算机中的含义也差不 ...

  4. [Hadoop]浅谈MapReduce原理及执行流程

    MapReduce MapReduce原理非常重要,hive与spark都是基于MR原理 MapReduce采用多进程,方便对每个任务资源控制和调配,但是进程消耗更多的启动时间,因此MR时效性不高.适 ...

  5. [转] 浅谈Linux系统的启动流程

    原文:http://blog.csdn.net/justdb/article/details/9621271 版权声明:本文为博主原创文章. Linux系统的启动时通过读取不同的配置文件,执行相应的S ...

  6. 浅谈Linux系统的启动流程

    Linux系统的启动时通过读取不同的配置文件,执行相应的Shell脚本完成的.当然本文只是简单的从文件的角度分析,更深层次的本文没涉及. 主要读取了以下文件:  /boot/grub/grub.con ...

  7. 深夜浅谈我理解的DIV对SEO的影响

    又到了夜深人静的时候,对于以前的我来说每天的这个时候都是在敲一下代码啊或者看一会书,但是今夜突然间又一次心血来潮,想写一篇博文来记录一下这一段时间做SEO优化所遇到的问题. 其实对于我来说SEO并不是 ...

  8. Struts2,Hibernate和Spring之间的框架整合关系

    1.首先要认清,hibernate和struts没有半点关系,所以他们之间没有任何可以整合的东西.a:struts 作为中心控制器,肯定要调用一些类来完成一些逻辑.而hibernate开发中,经常使用 ...

  9. 浅谈struts2之chain

    转自:http://blog.csdn.net/randomnet/article/details/8656759 前一段时间,有关chain的机制着实困绕了许久.尽管网上有许多关于chain的解说, ...

随机推荐

  1. 在执行xp_cmdshell的过程中出错,调用'LogonUserW'失败,错误代码:'1909'

    在上篇文章Could not obtain information about Windows NT group/user 'xxxx\xxxx', error code 0x5里面,我介绍了SQL ...

  2. Sqoop 结合多种系统的具体应用

    Sqoop与HDFS结合 下面我们结合 HDFS,介绍 Sqoop 从关系型数据库的导入和导出. Sqoop import 它的功能是将数据从关系型数据库导入 HDFS 中,其流程图如下所示. 我们来 ...

  3. 深入解析Windows操作系统笔记——CH2系统结构

    2.系统结构 本章主要介绍系统的总体结构,关键部件之间的交互,以及运行在什么环境. 2.系统结构 2.1 需求和设计目标 2.2 操作系统模型 2.3 总体结构 2.3.1 可移植性 2.3.2 对称 ...

  4. ARM汇编程序结构

    ARM汇编程序结构 一个ARM程序可以被划分为多个代码段和数据段,在汇编的时候这些段会被形成一个可执行文件 .text ;正文段,包含程序的指令代码 .data ;数据段,包含固定的数据,如常量,字符 ...

  5. springmvc和struts2的区别

    springmvc和struts2的区别 1.springmvc基于方法开发的,struts2基于类开发的. 2.单例和多例的区别:springmvc在映射的时候,通过形参来接收参数的,是将url和c ...

  6. Eclipse debug断点调试代码时出现source not found问题

    偶尔调试代码的时候会出现这种事情,之前并没有特别注意,今天稍微搜集一下相关资料: 1.跳转到的代码的确没有源码,下载源码后选择源码位置后便会正常显示源码. 2.源码和class文件不一致.即便勾选了a ...

  7. 【转帖】分享一个迅为4412开发板OTG烧录批处理文件

    平台:iTOP-4412开发板 Bat 功能: 1.可以分条的执行烧录,不需要每次烧录都去复制命令 2.可以批量烧录 开发板系统烧录批处理文件,请将此文件放置在fastboot程序同目录下,下载地址: ...

  8. 浅谈Java中的equals和==

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...

  9. postman使用之一:安装启动篇

    官网介绍:http://www.getpostman.com/docs/ postman是干什么的就不介绍了,本文从postman的安装开始介绍,后续会有其它使用的介绍. 安装: 1.mac app安 ...

  10. Serial Communication Protocol Design Hints And Reference

    前面转载的几篇文章详细介绍了UART.RS-232和RS-485的相关内容,可以知道,串口通信的双方在硬件层面需要约定如波特率.数据位.校验位和停止位等属性,才可以正常收发数据.实际项目中使用串口通信 ...