struct2源码解读之执行action请求(1)
上篇博文我们讨论了struct2是如何用PrepareOperations的方法去处理action请求的:
①设置编码和本地化信息
②设置值栈,并把request,session,application等信息放到ActionContext.context中,构建起action的运行环境。
③把dispacher放到threadLoack变量中。
④过滤action请求,也就是处理黑名单。
⑤把不是黑名单的请求(url)封装成actionMapping对象
代码清单:actionMapping public class ActionMapping { private String name; private String namespace; private String method; private String extension; private Map<String, Object> params; private Result result; }
因为actionMapping封装了url的信息,因此当处理了以前的信息后,就开始根据actionMapping执行相应的请求。
if (mapping == null) { boolean handled = execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { //执行正常的action请求 execute.executeAction(request, response, mapping); }
如果uri为空,或者是action为空时,返回一个空的actionMapping对象,这时struct2会尝试去加载/struct/或者是/static/下的静态资源,如果不成功,则退出当前过滤器跳到下个过滤器;如果返回一个不是空的actionMapping对象,则取执行正常的action请求。
上面就是struct2处理action请求的整个流程。下面就详细探讨下struct2是如何执行action请求的。
执行action请求是通过调用ExecuteOperations的executeAction方法来执行。这个方法一共传进了3个参数:request,respone,mapping.刚好拿到了处理url的所有信息
execute.executeAction(request, response, mapping);
从这个方法可以看出,调用PrepareOperations或者是ExecuteOperations的方法,其实最终调用的都是disapcher的方法,设计这两个类其实都是为了对同一功能的方法进行更进一步的封装,以方便管理。
这里最终调用了dispatcher.serviceAction()方法。
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping) throws ServletException { /** *1.把所有web对象放进一个map中 */ Map<String, Object> extraContext = createContextMap(request, response, mapping, context); /** *2.获得值栈valueStack */ ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); //nullStack 的值为stack==null的值 boolean nullStack = stack == null; if (nullStack) { //如果request的值栈为空,从ActionContext中取 ActionContext ctx = ActionContext.getContext(); if (ctx != null) { stack = ctx.getValueStack(); } } if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } try { /** *3.获得url信息 */ String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); /** *4.获取配置信息 */ Configuration config = configurationManager.getConfiguration(); /** *5.从容器中取出ActionProxyFactory对象,并实例化一个ActionProxy 对象 */ ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); /** *6.执行action请求 */ if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { proxy.execute(); } /** *7.把值栈添加到request中去 */ if (!nullStack) { request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); } } catch (ConfigurationException e) { //出现异常,发送404或者是500错误 } }
一、封装参数到一个map中
public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext context) { // request Map requestMap = new RequestMap(request); // parameters Map params = new HashMap(request.getParameterMap()); // session Map session = new SessionMap(request); // application Map application = new ApplicationMap(context); /** *封装所有参数到一个map中,特别注入,key为request的保存的是一个requestMap对象 *key值为StrutsStatics.HTTP_REQUEST的保存的才是一个request对象,session, *applicaiton也是如此 */ Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context); //mapping if (mapping != null) { extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); } return extraContext; }
二、获得值栈valueStack
valueStack最终会以structs.valueStack为key值保存到request中,所以会先从request中获取valueStack,如果获取不到,则从 ActionContext中去取(ActionContext中的valueStack在PrepareOperations创建ActionContext的时候已经实例化)
//实例化一个OgnlValueStack对象 ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext)); ctx = new ActionContext(stack.getContext());
如果request中存在一个valueStack对象,则对这个valueStack进一步封装成OgnlValueStack对象
public ValueStack createValueStack(ValueStack stack) { ValueStack result = new OgnlValueStack(stack, xworkConverter, compoundRootAccessor, allowStaticMethodAccess); container.inject(result); stack.getContext().put(ActionContext.CONTAINER, container); return result; }
并把这个对象以ValueStack.VALUE_STACK为key值,保存到上面的那个map中。
三、获取配置信息
struct2在初始化的时候已经把所有配置信息到都封装到了configuration对象,而这个configuration对象交给了configurationManager对象管理,这个configurationManager又是dispacher的一个属性,因此这里就可以通过configurationManager获得了配置文件的信息。
Configuration config = configurationManager.getConfiguration();
四、实例化一个ActionProxy对象
4.1.实例化一个ActionInvocation对象对象
struct2把执行action请求的所有方法都封装到了ActionProxy接口,所以这里需要实现ActionProxy接口,通过ActionProxyFactory生成一个ActionProxy对象(通过DefaultActionProxyFactory的createActionProxy方法)
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) { //实例化一个ActionInvocation对象 ActionInvocation inv = new DefaultActionInvocation(extraContext, true); //对ActionInvocation依赖注入 container.inject(inv); return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); }
在这个方法中先实例化一个ActionInvocation对象,然后重载createActionProxy方法.struct2实现的是StrutsActionProxyFactory
4.2.实例化一个ActionProxy对象
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) { //实例化一个ActionProxy对象 StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); //对ActionProxy对象依赖注入 container.inject(proxy); //初始化 proxy.prepare(); return proxy; }
这里其实是对参数进行了进一步的封装,把运行环境封装到了ActionInvocation对象,把nameSpace,actionName等信息封装到了ActionProxy。
上面都对ActionInvocation对象和ActionProxy对象进行了依赖注入,这里注入了哪些对象呢?
@Inject public void setObjectFactory(ObjectFactory factory) { this.objectFactory = factory; } @Inject public void setConfiguration(Configuration config) { this.configuration = config; }
上面列了两条特别重要的,需要注入的bean.这个ObjectFactory,看它的名字,对象工厂,表示是一个存放了对象的容器,在buildAction的时候,会往action里面注入action所需要的bean.正因为是容器,spring也是ioc容器,因此在struct2和spring整合的时候,就可以从这里做为切入点,依赖注入ObjectFactory的时候,注入的是spring的factory,具体实现原理,后面再分析。
准备了所有对象(环境信息)后,ActionProxy还做了一些准备工作。
4.3.ActionProxy初始化
protected void prepare() { try { //根据url中的namespace和actionName获取配置信息中的 ActionConfig 对象 config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName) if (config == null && unknownHandlerManager.hasUnknownHandlers()) { config = unknownHandlerManager.handleUnknownAction(namespace, actionName); } if (config == null) { //如果找不到,抛出异常信息 } //默认方法 resolveMethod(); //异常信息 //初始化ActionInvocation invocation.init(this); } finally { UtilTimerStack.pop(profileKey); } }
在初始化的时候,我们把package中的action标签信息都封装成ActionConfig,这里就通过namespace匹配package,通过actionName匹配action,如果url中的action在配置文件中找不到,则抛出异常。action中默认执行的excute方法也是在这里指定的
private void resolveMethod() { // 如果url方法为空 if (StringUtils.isEmpty(this.method)) { //取配置文件中的方法 this.method = config.getMethodName(); //如果配置文件中的方法为空 if (StringUtils.isEmpty(this.method)) { //使用excute this.method = "execute"; } } }
在proxy的准备工作,也对ActionInvocation进行了初始化。
4.4. ActionInvocation初始化
public void init(ActionProxy proxy) { this.proxy = proxy; Map<String, Object> contextMap = createContextMap(); ActionContext actionContext = ActionContext.getContext(); if (actionContext != null) { actionContext.setActionInvocation(this); } //对Action类依赖注入 createAction(contextMap); //把action压入值顶 if (pushAction) { stack.push(action); contextMap.put("action", action); } invocationContext = new ActionContext(contextMap); invocationContext.setName(proxy.getActionName()); // 拦截器 List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors()); interceptors = interceptorList.iterator(); }
4.4.1.依赖注入
ActionInvocation初始化工作主要是对请求的action类进行依赖注入
protected void createAction(Map<String, Object> contextMap) { try { action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap); } //异常信息 if (actionEventListener != null) { action = actionEventListener.prepare(action, stack); } }
action是一个object类型,因为actionName有不同的类型,所以这里用到了所有类的超类。通过objectFactory的buildAction对actionName对应的类进行依赖注入。objectFactory是一个接口,struct2自带的实现类为StrutsObjectFactory
代码清单:struct-default.xml配置 <bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />
如果和spring整合后,struct-plugin.xml的配置为
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" /> <constant name="struts.objectFactory" value="spring" />
constant的配置会替换它本身的配置。所以spring和struct2整合后,调用的是StrutsSpringObjectFactory的buildAction方法。但是StrutsSpringObjectFactory没有这个方法,我们从它的父类SpringObjectFactory中查找这个方法
@Override public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception { Object o = null; try { //依赖注入 o = appContext.getBean(beanName); } //异常信息 if (injectInternal) { injectInternalBeans(o); } return o; }
getBean()就是spring容器对一个类进行依赖注入的起点。具体实现原理,我们在介绍spring的时候再作具体介绍。这里就把actionName对应的类进行了依赖注入,并把这个类压入了栈顶。接着就获取这个action里面的拦截器。
4.4.2.拦截器
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors()); interceptors = interceptorList.iterator();
这个拦截器是从proxy.getConfig也就是ActionConfig中取出的,这里用到了一个新的list集合,以防止有人在改动这个集合后,在循环遍历的时候不会出现问题。
那么这个拦截器包含哪些拦截器呢?大家还记得在初始化的时候有个rebuildRuntimeConfiguration()方法,对packageconfig里面的值设置了初始值,包括ActionConfig,寻寻觅觅,我们找到rebuildRuntimeConfiguration()方法buildFullActionConfig()这个方法,其中一小段代码,设置了这个ActionConfig的初始值
//baseConfig.getInterceptors()先判断action中是否有拦截器 List<InterceptorMapping> interceptors = new ArrayList<InterceptorMapping>(baseConfig.getInterceptors()); //如果没有,使用默认拦截器 if (interceptors.size() <= 0) { //使用package中的默认拦截器栈,当前package没有,去父package中找 String defaultInterceptorRefName = packageContext.getFullDefaultInterceptorRef(); if (defaultInterceptorRefName != null) { //如果找到默认拦截器栈,则把栈中的拦截器放到interceptors集合中 interceptors.addAll(InterceptorBuilder.constructInterceptorReference(new PackageConfig.Builder(packageContext), defaultInterceptorRefName, new LinkedHashMap<String, String>(), packageContext.getLocation(), objectFactory)); } } //如果有,直接使用action中的拦截器 return new ActionConfig.Builder(baseConfig) .addParams(params) .addResultConfigs(results) .defaultClassName(packageContext.getDefaultClassRef()) // fill in default if non class has been provided .interceptors(interceptors) //actionConfig中添加拦截器 .addExceptionMappings(packageContext.getAllExceptionMappingConfigs()) .build();
从这里可以看出,如果action中配有拦截器,则默认的拦截器栈中的拦截器是不生效的,因此在自定义拦截器之后,需要加上默认拦截器栈中的拦截器
<interceptors> <!-- 声明拦截器 --> <interceptor name="checkPrivilege" class="cn.thinmore.oa.utils.CheckPrivilegeInteceptor"></interceptor> <!-- 重新定义默认的拦截器栈 --> <interceptor-stack name="defaultStack"> <interceptor-ref name="checkPrivilege"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors>
五、执行action请求
if (mapping.getResult() != null) { Result result = mapping.getResult(); //执行结果 result.execute(proxy.getInvocation()); } else { //执行action请求 proxy.execute(); }
下篇博文详解。
六、总结
本篇博文,介绍了执行action请求前的一些操作:比如把http参数封装到一个map中;创建一个值栈对象;找到配置信息中的actionConfig对象;并创建两个执行这个action请求的对象:actionProxy和actionInvocation,在创建这连个对象过程中,对执行这个action请求的类进行了依赖注入并调出了相关的拦截器。