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.getDefaultCla***ef()) // 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请求的类进行了依赖注入并调出了相关的拦截器。
转载于:https://blog.51cto.com/yoyanda/1714568