启动过程图
启动服务器,加载web.xml文件
注:StrutsPreparedAndExecuteFilter过滤器是对 StrutsPrepareFilter和 StrutsExecuteFilter 两个过滤器的包装,配置上面的两个过滤器也可以实现struts中的机制
解析StrutsPreparedAndExecuteFilter过滤器
//在tomcat启动的时候,准备过程
protected PrepareOperations prepare;
protected ExecuteOperations execute;
//拦截器配置的非拦截的元素,比如,比如静态页面,图片等
protected List<Pattern> excludedPatterns = null
执行过滤器中的方法
public void init(FilterConfig filterConfig) throws ServletException 只是在初始化的时候执行,在tomcat启动的时候执行此方法
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
/** init.initDispatcher先使用FilterHostConfig 判断一下是否有过滤器的初始化参数
如果有参数将参数组织放到一个list集合中
创建Dispatcher,(读取资源文件文件的)会接受FilterHostConfig组织好的参数
初始化Dispatcher, dispatcher.init()做的事
//加载org/apache/struts2/default.properties
init_DefaultProperties();// [1]
* 具体 configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
* defaultSettings = new PropertiesSettings("org/apache/struts2/default");
//加载struts-default.xml,struts-plugin.xml,struts.xml
init_TraditionalXmlConfigurations(); // [2]
configPaths = DEFAULT_CONFIGURATION_PATHS; 是一个常量DEFAULT_CONFIGURATION_PATHS=struts-default.xml,struts-plugin.xml,struts.xml 在这里将拦截器都实例化了
init_LegacyStrutsProperties(); // [3]
//用户自己实现的ConfigurationProviders类, 配置类全名和实现ConfigurationProvider接口,用逗号隔开即可
init_CustomConfigurationProviders(); // [5]
//Filter的初始化参数
init_FilterInitParameters() ;// [6]
//初始化用户自定义的别名
init_AliasStandardObjects() ;// [7]
*/
Dispatcher dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
//struts环境的准备过程
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
//struts执行环境的准备过程
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
//excludedPatterns存放的是配置文件中所有自定义不拦截的请求或者资源
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
doFilter 每次url请求都会执行这个方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
doFilter是过滤器的执行方法,它拦截提交的HttpServletRequest请求,HttpServletResponse响应,
核心代码(注:以下代码有点乱,最好参考源代码,一个个的对比着点进去)
理解下面的代码首先得了解
//OgnlValueStack里面有两个重要的属性:
//CompoundRoot root
//transient Map<String, Object> context;
//而context又是一个OgnlContext,OgnlContext也有两个重要的属性:
//private Object _root;
//private Map _values = new HashMap(23);
//设置默认编码和local
prepare.setEncodingAndLocale(request, response);
//根据拦截到的request和response创建Action上下文
prepare.createActionContext(request, response);
//解析createActionContext方法中的内容
ActionContext ctx;
Integer counter = 1;
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null) {//如果存在表示曾经创建过ActionContext
counter = oldCounter + 1;
}
//获取曾建创建的ActionContext对象
ActionContext oldContext = ActionContext.getContext();
if (oldContext != null) {
// detected existing context, so we are probably in a forward
//创建当前流程的ActionContext对象,并将原有的对象的中的request的参数保存到新的ActionContext对象中,应该是在转发的时候用到
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
/**
public ValueStack createValueStack() {
//ValueStack是接口,其默认实现类是OgnlValueStack,所以实际上是new一个OgnlValueStack出来。
ValueStack stack = new OgnlValueStack(xworkConverter, compoundRootAccessor, textProvider, allowStaticMethodAccess);
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
//setRoot方法详解
this.root = compoundRoot;//OgnlValueStack.root = compoundRoot;
this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess);//方法/属性访问策略
//创建context了,创建context使用的是ongl的默认方式。
this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter), securityMemberAccess);
// 将OgnlValueStack放入到reuqest的指定的属性中VALUE_STACK=com.opensymphony.xwork2.util.ValueStack.ValueStack
context.put(VALUE_STACK, this);
Ognl.setClassResolver(context, accessor);
((OgnlContext) context).setTraceEvaluations(false);
((OgnlContext) context).setKeepLastEvaluation(false);
push(prov);
}
container.inject(stack);
//把容器加入OgnlValueStack.context,实际是加入到OgnlContext的context中。
stack.getContext().put(ActionContext.CONTAINER, container);
return stack;
}
*/
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;
//将处理后的编码,国际化和创建的actioncontext上下文绑定到tomcat创建的本地线程上去
prepare.assignDispatcherToThread();
//excludedPatterns非拦截的资源选项,
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
/** prepare.isUrlExcluded(request, excludedPatterns)判断现在的request请求是否包含在不拦截的资源里
*/
//放行
chain.doFilter(request, response);
} else {
//request增强,在后台判断了表单的类型,主要是上传的时候对request进行包装
request = prepare.wrapRequest(request);
//实际是在prepare中的dispatcher中的解析的struts的配置文件中查找request过来的链接,返回一个actionMapping的映射(也就是在这里建立了请求和action中的方法的关联)调用的是defaultActionMapper
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
/**就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,
* 当然.class文件除外。如果再没有则跳转到404
*/
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
//映射的请求存在,这个时候会在这里去执行mapping映射中的拦截器
//映射的请求存在,这个时候会在这里去执行mapping映射中的拦截器找到对应action配置文件后,调用ExecuteOperations类中executeAction
execute.executeAction(request, response, mapping);
/**
在这里执行了ExecuteOperations 类下的 dispatcher.serviceAction(request, response, servletContext, mapping);方法
ExecuteOperations在strutsPrepareAndExecuteFilter中已经声明过了 protected ExecuteOperations execute;
然后进入dispatcher的serviceAction方法,在这里面生成了访问的代理对象,这里使用了aop编程的思想,将拦截器和目标方法织入到了代理方法中,
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
//将本次请求生产的值栈valueStack放入到request请求中
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
在serviceAction方法中
proxy.execute();
代理对象执行的方法
StrutsActionProxy 中的execute(),
* return invocation.invoke();
* 真正执行的是 DefaultActionInvocation下的invoke()方法
* 在这个invoke()方法下,依次遍历struts2默认栈的拦截器
由于拦截器都实现了Interceptor接口,所以拦截器都能获得ActionInvocation
ActionInvocation是一个action执行的上下文环境,里面封装了值栈等,操作request都是从值栈中去的值,
可以说,拦截器中操作的对象都是值栈中的内容
if (interceptors.hasNext()) {//拦截器的迭代器
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
//执行每个拦截器的拦截方法
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
* 在DefaultActionInvocation下的init(ActionProxy proxy)下
//这里遍历了配置文件中的拦截器,将拦截器都装到了interceptors中
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
*/
}
}