一、Struts2容器初始化过程
1> 创建Dispatcher对象
a> 将ServletContext设置到Dispatcher中
b> 读取default.properties,struts-default.xml,struts-plugin.xml,struts.xml等配置文件
c> 获取创建Container全局容器,在此期间会解析配置文件并创建xml文件中配置的Bean
d> 使用ContainerHolder的静态方法将Container实例放入ThreadLocal中
e> 初始化一些全局配置,比如是否为开发模式等
f> 将Dispatcher对象放入Container全局容器中
2> 创建PrepareOperations和ExecuteOperations两个对象并将Dispatcher放入其中,之后doFilter会用到这两个对象
二、Struts2插件机制
介绍一下ObjectFactory类,它是创建对象的工厂,这些对象包括Action对象、结果类型、拦截器、验证器、类型转换器等。可以在struts.xml中配置
SmallbugFactory是ObjectFactory的子类,以此来修改默认的对象工厂,spring插件也是配置这个参数达到了整合Struts2的目的。之所以可以这样做是因为在Struts2容器中很多地方都是调用了ObjectFactory的buildXxx方法来获取实例的。在运行时如果你给它配置了不同的子类,那么如何创建Action就有你来决定了。想使用某种工具直接可以将它的jar包放到部署目录下就ok,不想用直接取走,连配置文件都不用该极其灵活。这种灵活度的实现也得益于struts-default.xml,struts-plugin.xml,struts.xml配置文件的依次加载,出现重复的话后者覆盖前者。
三、Action核心流程(过滤器匹配成功时触发)
1> PrepareOperations对象设置结果集
2> PrepareOperations对象创建ActionContext
a> Dispatcher获取ValueStackFactory的实例然后创建ValueStack
b> 将request,request.getParameterMap(),session,servletContext分别封装成map
c> 将上述所有Map放入一个大Map中以它们的名字作为key
d> 获取ValueStack中名为context的Map,将上述大Map以putAll的方式添加进context中
e> 创建一个ActionContext对象将ValueStack添加进去
3> 调用Dispatcher的静态setInstance方法,将Dispatcher实例放入ThreadLocal中
4> 将Request对象包装为StrutsRequestWrapper对象,其作用在于如果request域中找不到的属性会去stack中找
5> 根据request,response创建mapping映射对象
6> 使用ExecuteOperations执行目标方法
a> 创建代理Action
-> createAction(contextMap);创建Action:调用ObjectFactory中的buildAction方法创建action
-> stack.push(action);把action放入到了栈顶
-> 获取拦截器
b> proxy.execute()执行代理方法,按照顺序的方式执行:
-> 拦截器
-> 执行action
-> 执行结果集
-> 如果拦截器的返回形式不是return invocation.invoke方法,则按照倒叙的方式执行
c> 清空数据
四、拦截器调用原理
首先是代理对象触发ActionInvocation实例的invoke方法,拦截器开始执行。ActionInvocation中有一个Iterator
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(); }
它其实是把自己的实例作为参数传了下去,这时候拦截器链上的节点执行会有两种情况
1> return invocation.invoke();它会在执行完自己的逻辑之后返回ActionInvocation之后不再执行。
2> invocation.invoke();//之后还有逻辑,它会在执行完自己的逻辑之后返回ActionInvocation,在ActionInvocation的逻辑执行完之后还会执行自己剩余的部分的逻辑。