您的位置:首页 > 编程语言 > Java开发

struts2源码的自我理解

2016-02-23 13:03 489 查看

1. 概述

Struts2是Apache提供的一个免费的重量级web层框架,其采用mvc设计模式。Struts2底层依赖的是WebWork。
**MVC设计模式:**
MVC是指从web层的角度看待整个web应用,可以将web应用分为3个部分:Model,view,Controller。
View 视图:            jsp         web层
Model 模型:       JavaBean        业务层,持久层
Controller 控制器: servlet     web层


2.使用

使用Struts2框架时,只需要在web.xml文件中注册struts2的核心过滤器并配置struts.xml文件,就能通过继承ActionSupport类来使用Struts2的功能。


<!-- struts2核心过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


3.Struts2框架的初始化:

注册StrutsPrepareAndExecuteFilter后,在web应用启动时,会先执行过滤器的init方法,对struts2进行初始化配置


public void init(FilterConfig filterConfig) throws ServletException {
//初始化器
//为struts2框架设置初始化参数
InitOperations init = new InitOperations();
//调度器
//核心类
Dispatcher dispatcher = null;
try {
//使用filterConfig构建struts2内部的config文件
//类似于装饰者模式,但父接口为HostConfig
//其缺少一个getFilterName(),因为没有必要
FilterHostConfig config = new FilterHostConfig(filterConfig);
//使用config文件进行初始化
init.initLogging(config);
//使用config文件对dispatcher进行初始化
dispatcher = init.initDispatcher(config);
//返回一个StaticContentLoader,但是没有保存这个对象的引用
//也没有静态方法可以获得其引用
//浪费内存?
init.initStaticContentLoader(config, dispatcher);

//创建预执行器
prepare = new PrepareOperations(dispatcher);
//创建执行器
execute = new ExecuteOperations(dispatcher);
//创建排除的正则表达式目录
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//为他人扩展提供便利
postInit(dispatcher, filterConfig);
} finally {
if (dispatcher != null) {
//将ContainerHolder中缓存的container信息清除,节省内存
//因为是线程特有数据(ThreadLocal)
dispatcher.cleanUpAfterInit();
}
//将初始化时,存入ActionContext中的数据清空,节省内存
//因为是线程特有数据(ThreadLocal)
init.cleanup();
}
}
protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
}


3.1 init.initLogging(config);

该方法实现了对服务器日志文件的整合


public void initLogging( HostConfig filterConfig ) {
//获取服务器的loggerFactory
String factoryName = filterConfig.getInitParameter("loggerFactory");
if (factoryName != null) {
try {
//使用这个类的classLoader,来加载loggerFactory
Class cls = ClassLoaderUtil.loadClass(factoryName, this.getClass());
LoggerFactory fac = (LoggerFactory) cls.newInstance();
//将对象的引用设置在静态变量中
//方便以后使用静态方法获取
LoggerFactory.setLoggerFactory(fac);
} catch ( InstantiationException e ) {
System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default");
e.printStackTrace();
} catch ( IllegalAccessException e ) {
System.err.println("Unable to access logger factory: " + factoryName + ", using default");
e.printStackTrace();
} catch ( ClassNotFoundException e ) {
System.err.println("Unable to locate logger factory class: " + factoryName + ", using default");
e.printStackTrace();
}
}
}


3.2 init.initDispatcher(config);

使用config中的数据对dispachter进行初始化


public Dispatcher initDispatcher( HostConfig filterConfig ) {
//创建一个dispachter
Dispatcher dispatcher = createDispatcher(filterConfig);
//执行dispacher的初始化设置
dispatcher.init();
return dispatcher;
}
private Dispatcher createDispatcher( HostConfig filterConfig ) {
//获取config里面的所有参数
Map<String, String> params = new HashMap<String, String>();
for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
String name = (String) e.next();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
//将servletContext和所有参数传递给dispatcher的构造器
return new Dispatcher(filterConfig.getServletContext(), params);
}


3.3 init.buildExcludedPatternsList(dispatcher);

从配置文件中读取不需要使用struts2的路径


public List<Pattern> buildExcludedPatternsList( Dispatcher dispatcher ) {
return buildExcludedPatternsList(dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN));
}

private List<Pattern> buildExcludedPatternsList( String patterns ) {
if (null != patterns && patterns.trim().length() != 0) {
List<Pattern> list = new ArrayList<Pattern>();
String[] tokens = patterns.split(",");
for ( String token : tokens ) {
list.add(Pattern.compile(token.trim()));
}
return Collections.unmodifiableList(list);
} else {
return null;
}
}


dispachter,prepare和execute的构造方法都十分简单,就不一一列出源码了。


3.4 dispatcher.init();

是一个十分重要的方法。对dispachter的相关参数,容器的参数等进行设置。


public void init() {

if (configurationManager == null) {
//创建一个默认的configurationManager,xwrok中的类
configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);//DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME="struts"
}

try {
//将相关参数设置到configurationManager中
//所有的ContainerProvider
init_FileManager();
init_DefaultProperties(); // [1]
init_TraditionalXmlConfigurations(); // [2]
init_LegacyStrutsProperties(); // [3]
init_CustomConfigurationProviders(); // [5]
init_FilterInitParameters() ; // [6]
init_AliasStandardObjects() ; // [7]

//xwork中的类
//即在ConfigurationManager中配置的List<ContainerProvider>
//上面7个init的配置内容的总和
//即容器本身
Container container = init_PreloadConfiguration();
//将dispatcher注入配置好的容器中
container.inject(this);
//检查是否需要支持WebLogic服务器的特殊设置
init_CheckWebLogicWorkaround(container);

//dispatcher的监听器,但在源码中并没有使用
//可能是扩展用的
if (!dispatcherListeners.isEmpty()) {
for (DispatcherListener l : dispatcherListeners) {
l.dispatcherInitialized(this);
}
}
//从头到尾没有看见赋值
//应该报NullPointerException啊?
//可能与configurationManager或container有关
errorHandler.init(servletContext);

} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}


4.执行action请求

每次有请求到来就会执行过滤器的doFilter方法,对请求相关数据进行包装


public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;

try {
if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
//是排除的请求地址
chain.doFilter(request, response);
} else {
//不是
//设置请求响应编码
//底层依赖dispatcher的prepare(request, response)方法
prepare.setEncodingAndLocale(request, response);
//给值栈和ActionContext域赋值
prepare.createActionContext(request, response);
//将dispatcher与线程绑定
//方便静态方法获取
//底层依赖Dispatcher.setInstance(dispatcher)静态方法
prepare.assignDispatcherToThread();
//替换request
//底层依赖dispatcher的wrapRequest(request)方法
request = prepare.wrapRequest(request);
//获取对应的解析结果对象
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
//true:struts2自己的一些东西
//false:只是普通的静态资源
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
//是静态资源
chain.doFilter(request, response);
}
} else {
//执行action
//底层依赖dispatcher.serviceAction(request, response, mapping)
execute.executeAction(request, response, mapping);
}
}
} finally {
//清除action的相关缓存资源
prepare.cleanupRequest(request);
}
}


由于prepare和execute的许多方法都是依赖于底层的dispatcher,所以只列出部分方法


4.1 prepare.createActionContext(request, response);

根据request,response创建相应的ActionContext


public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
ActionContext ctx;
//需要循环递归的次数
//不大明白存在的意义
Integer counter = 1;
//初次访问时为null
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null) {
counter = oldCounter + 1;
}

ActionContext oldContext = ActionContext.getContext();
if (oldContext != null) {
//不是初次访问服务器
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
//是初次访问
//获取一个新的值栈
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
//将request,session,application,page放入值栈
//供OGNL表达式使用
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
//将值栈中的数据放入ActionContext域
//供Action使用
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
//在Action中使用ActionContext的静态方法获取
//使用了ThreadLocal<ActionContext>避免多线程问题
ActionContext.setContext(ctx);
return ctx;
}


4.2 ActionMapping mapping = prepare.findActionMapping(request, response, true);

使用ActionMapper类对url地址进行解析,使其变为ActionMapping对象


public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
//ActionMapping代表的是一个请求的所有数据
//http://localhost:8080/sss/login.action?clzjxkl=asda
//namespace   name   params   method GET/POST
//ActionMapper:创建ActionMapping的类
//对url的进行解析
ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);    //STRUTS_ACTION_MAPPING_KEY = "struts.actionMapping"
if (mapping == null || forceLookup) {
try {
mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
if (mapping != null) {
request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
}
} catch (Exception ex) {
dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
}
}

return mapping;
}


4.3 dispatcher.prepare(request, response)

对request和response进行相关设置


public void prepare(HttpServletRequest request, HttpServletResponse response) {
String encoding = null;
//有默认编码就设置为默认编码
if (defaultEncoding != null) {
encoding = defaultEncoding;
}
// check for Ajax request to use UTF-8 encoding strictly http://www.w3.org/TR/XMLHttpRequest/#the-send-method //是ajax请求就设置为utf-8
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
encoding = "UTF-8";
}

Locale locale = null;
//有默认语言环境就设置为默认值
if (defaultLocale != null) {
locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
}

if (encoding != null) {
//当有默认编码,或是ajax请求时执行
//为请求设置编码
applyEncoding(request, encoding);
}

if (locale != null) {
//当有默认环境时执行
//为相应设置环境
response.setLocale(locale);
}

//当服务器是WebLogic时执行
if (paramsWorkaroundEnabled) {
request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request
}
}


4.4 dispatcher.createContextMap(request, response, null)

根据相关数据创建ContextMap,返回后设置在ActionContext中


public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
ActionMapping mapping) {

// request map wrapping the http request objects
//request域中的数据
Map requestMap = new RequestMap(request);

// parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately
//page域中的数据
Map params = new HashMap(request.getParameterMap());

// session map wrapping the http session
//session域中的数据
Map session = new SessionMap(request);

// application map wrapping the ServletContext
//application域中的数据
Map application = new ApplicationMap(servletContext);

//生成值栈中的数据
Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response);

if (mapping != null) {
extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
}
return extraContext;
}


4.5 dispatcher.wrapRequest(request)

替换request,如果有必要


public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
// don't wrap more than once
//是StrutsRequestWrapper类的子类
if (request instanceof StrutsRequestWrapper) {
return request;
}

String content_type = request.getContentType();
//判断是否是上传
if (content_type != null && content_type.contains("multipart/form-data")) {
//底层依赖
MultiPartRequest mpr = getMultiPartRequest();
LocaleProvider provider = getContainer().getInstance(LocaleProvider.class);
//供用户使用的request
request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup);
} else {
request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
}

return request;
}


4.6 dispatcher.serviceAction(request, response, mapping)

执行请求的核心代码,通过创建代理来执行请求


public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
throws ServletException {

//将四大域拷贝一份,用于构建代理
Map<String, Object> extraContext = createContextMap(request, response, mapping);

// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
//取出值栈
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
boolean nullStack = stack == null;
if (nullStack) {
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
stack = ctx.getValueStack();
}
}
if (stack != null) {
//将值栈放入容器中
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
}

String timerKey = "Handling request from Dispatcher";
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();

//创建一个action代理
ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
//将代理的值栈放入request
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

// if the ActionMapping says to go straight to a result, do it!
if (mapping.getResult() != null) {//这个请求已经被执行过了
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {//执行代理
proxy.execute();
}

// If there was a previous value stack then set it back onto the request
//用原来的值栈覆盖掉代理的值栈
//是同一个对象
//没什么用啊?
if (!nullStack) {
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch (ConfigurationException e) {
logConfigurationException(request, e);
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
} catch (Exception e) {
if (handleException || devMode) {
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
} else {
throw new ServletException(e);
}
} finally {
UtilTimerStack.pop(timerKey);
}
}


以上就是小弟对struts2框架执行过程的了解,有什么不对的地方还请各位大神指点
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: