Struts工作原理的一些理解
Struts工作原理
首先看看struts官方给出的图解,如下:
接下来一个个的来看看这个图解里面的的意义所在:
1、 ActionContextCleanUp,在FilterDispatcher源码中有这样一句话:
finally { ActionContextCleanUp.cleanUp(req); }
在ActionContextCleanUp中有这样的代码:
req.setAttribute(CLEANUP_PRESENT,Boolean.TRUE),如果FilterDispatcher检测到这个属性,就不会清除ActionContext中的内容了,而由ActionContextCleanUp后续的代码来清除,保证了一系列的Filter访问正确的ActionContext。在这里ActionContextCleanUp会在doFilter方法中添加一个计数器counter初始化为1,标志着后续的过滤器就不在回清除ActionContext,而是在最后交予之前的ActionContextCleanUp来清理。
2、 Other filter(SiteMesh,etc),调用的先后顺序是这样的,首先是ActionContextCleanUp,然后才是其他的一些Filter,可以在web.xml中这样配置:
<?xml version="1.0" encoding="UTF-8"?> <filter> <filter-name>ActionContextCleanUp</filter-name> <filter-class>com.opensymphony.webwork.dispatcher.ActionContextCleanUp</filter-class> </filter> <filter> <filter-name>sitemesh</filter-name> <filter-class>com.opensymphony.webwork.sitemesh.FreeMarkerPageFilter</filter-class> </filter> <filter> <filter-name>webwork</filter-name> <filter-class>com.opensymphony.webwork.dispatcher.FilterDispatcher</filter-class> </filter> <filter-mapping> <filter-name>ActionContextCleanUp</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>sitemesh</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>webwork</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3、 FilterDispatcher,这是struts核心Filter,主要负责功能有:
- 执行Actions
- 清除ActionContext
- 维护静态内容
- 清除request生命周期内XWork的interceptors
过滤器首先通过ActionMapper对象判断某个请求是否被映射到Action,如果有配置相应的映射,则终止过滤链,进入Action。
如果之前没有ActionContextCleanUp的计数器提示,这里将自动清除ActionContext,上面也见到,如果ActionContextCleanUp设置计数器标志为1,这这里将不进行ActionContext清理,交由ActionContextCleanUp清理。
维护一些路html,js,css这样一些静态页面的内容请求等。
另外,FilterDispatcher的核心在于ActionMapper,进入FilterDispatcher之后默认会调用init()然后是doFilter()方法,解析配置文件,得到uri,如果有Action配置,进入Action。请求分发完成。
4、 ActionProxy,这是一个动态代理接口,通过调用execute()方法实现代理。有如下的一个方法调用流程DefaultActionInvocation()->init()->createAction()。
(1) 首先来看看ActionProxy:
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) { ActionInvocation inv = new DefaultActionInvocation(extraContext, true); container.inject(inv); return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); }
(2)然后DefaultActionInvocation的init()方法中初始化ActionContext,
// Setting this so that other classes, like object factories, can use the ActionProxy and other contextual information to operate ActionContext actionContext = ActionContext.getContext(); if (actionContext != null) { actionContext.setActionInvocation(this); }
(3)并为每一个Request请求创建一个新的Action,存入stack中,
createAction(contextMap); if (pushAction) { stack.push(action); contextMap.put("action", action); } invocationContext = new ActionContext(contextMap); invocationContext.setName(proxy.getActionName());
(4)初始化拦截器Interceptor List,
// get a new List so we don't get problems with the iterator if someone changes the list List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors()); 、 interceptors = interceptorList.iterator();
(5)上面createAction()采用跟spring一样的ioc工厂方式创建,将Action写入到配置文件中,首先build相应的bean(action),然后放入到Map中以键值对方式存储,用到时取出。如:
o = appContext.getBean(beanName);
(6)再然后就是核心的调用invoke方法,遍历调用interceptor。调用invokeAction(),遍历调用Action,采用java反射实现Action中方法的调用:
//java反射机制得到要执行的方法 method = getAction().getClass().getMethod(methodName, new Class[0]);
如果没有指定调用的method方法(在struts.xml中配置的method属性),这默认去调用do+Xxx方法处理请求。如:
try { String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1); method = getAction().getClass().getMethod(altMethodName, new Class[0]); }
(7)Action执行完毕,就是接下来的Result.
5、 Result,同Action一样,以工厂模式在java反射基础上创建相应的Result实现HttpServletResponse返回跳转。
另外,struts2还涉及到OGNL标签,国际化等内容,在这里不涉及那么多。