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

Struts2执行流程

2017-04-20 21:33 393 查看
1. 请求示意图





2. 一个请求在Struts2框架中的处理步骤:

1. 客户端初始化一个指向Servlet容器的请求

2. 根据web.xml配置,请求先经过ActionContextCleanUp过滤器,主要清理当前线程中ActionContext和Dispatcher

3. 请求经过插件过滤器(该步可以略过)

4. 请求经过StrutsPrepareAndExecuteFilter核心过滤器,执行doFilter方法,在该方法中,询问ActionMapper来决定这个请求是否需要调用Action

5. 如果ActionMapper决定调用某个Action,则ActionMapper会返回一个ActionMapping实例(存储Action的配置文件),并创建ActionProxy(Action代理)对象,将请求交给代理对象继续处理

6. ActionProxy对象根据ActionMapping和Configuration Manager询问框架的配置文件,找到需要调用的Action类

7. ActionProxy对象创建时,会同时创建一个ActionInvocation的实例

8. ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器的调用

9. 一旦Action执行完毕,ActionInvocation实例负责struts.xml中的配置创建并返回到result结果视图,result通常是一个需要被表示的JSP或者FreeMarker的模板,也可能是另外的一个Action链

10. 如果要在返回result之前做些什么,可以实现PreResultListener接口,PreResultListener可以在Interceptor中实现,也可以在Action中实现

11. 根据Result对象信息,生成用户响应信息response

3. 源码分析阶段

3.1 服务器启动阶段

1. 服务器启动后,进入web.xml,跳进配置的核心过滤器StrutsPrepareAndExecuteFilter

2. 类内部首先是三个变量的声明

protected PrepareOperations prepare;

protected ExecuteOperations execute;

protected List<Pattern> excludedPatterns = null;

[/code]

3. 进入到init方法

public void init(FilterConfig filterConfig) throws ServletException {

InitOperations init = new InitOperations();

Dispatcher dispatcher = null;

try {

FilterHostConfig config = new FilterHostConfig(filterConfig);

init.initLogging(config);

dispatcher = init.initDispatcher(config);

init.initStaticContentLoader(config, dispatcher);


prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);

execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);

this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);


postInit(dispatcher, filterConfig);

} finally {

if (dispatcher != null) {

dispatcher.cleanUpAfterInit();

}

init.cleanup();

}

}

[/code]

4.对于第5行

FilterHostConfig config = new FilterHostConfig(filterConfig);

[/code]

进入到FilterHostConfig内部查看,发现FilterHostConfig主要是用来获取Servlet上下文、web.xml中的init-param配置的param-name获取param-value的值

public class FilterHostConfig implements HostConfig {


private FilterConfig config;


public FilterHostConfig(FilterConfig config) {

this.config = config;

}

public String getInitParameter(String key) {

return config.getInitParameter(key);

}


public Iterator<String> getInitParameterNames() {

return MakeIterator.convert(config.getInitParameterNames());

}


public ServletContext getServletContext() {

return config.getServletContext();

}

}

[/code]

dispatcher中serviceAction方法
/**

* Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.

加载Action类用于映射,执行相应的Action方法,或者直接转到Result视图

* <p/>

* This method first creates the action context from the given parameters,

这个方法首先从给定的参数创建action上下文

* and then loads an <tt>ActionProxy</tt> from the given action name and namespace.

然后,从给定的action名和名称空间中加载一个ActionProxy

* After that, the Action method is executed and output channels through the response object.

之后,action方法被执行以及通过响应对象输出通道

* Actions not found are sent back to the user via the {@link Dispatcher#sendError} method,

Action如果没有发现,将通过sendError方法返回用户提示

* using the 404 return code.

使用404返回代码

* All other errors are reported by throwing a ServletException.

其他的错误将被抛出ServletException异常

*

* @param request  HttpServletRequest 对象

* @param response HttpServletResponse对象

* @param mapping  ActionMapping对象

* @throws ServletException when an unknown error occurs (not a 404, but typically something that

*                          would end up as a 5xx by the servlet container)

* @param context Our ServletContext object

*/

public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,

ActionMapping mapping) throws ServletException {


Map<String, Object> extraContext = createContextMap(request, response, mapping, context);


// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action

//如果先前有一个值栈,那么创建一个新的副本,并传递给新的Action使用


//从request中获取到ValueStack对象

ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

//判断ValueStack是否为空

boolean nullStack = stack == null;

if (nullStack) {

	//为空,获取ActionContext

ActionContext ctx = ActionContext.getContext();

if (ctx != null) {

//如果ActionContext不为空,通过ActionContext获取ValueStack---通过查看ActionContext,发现ActionContext包含ValueStack,

stack = ctx.getValueStack();

}

}

if (stack != null) {

	//把ValueStack放入ActionContext中

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();


Configuration config = configurationManager.getConfiguration();

	//加载ActionProxy

ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(

namespace, name, method, extraContext, true, false);


request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());


// if the ActionMapping says to go straight to a result, do it!

	//如果ActionMapping决定直接返回到结果视图,完成

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) {

// WW-2874 Only log error if in devMode

if (devMode) {

String reqStr = request.getRequestURI();

if (request.getQueryString() != null) {

reqStr = reqStr + "?" + request.getQueryString();

}

LOG.error("Could not find action or result\n" + reqStr, e);

} else {

if (LOG.isWarnEnabled()) {

LOG.warn("Could not find action or result", e);

}

}

sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);

} catch (Exception e) {

if (handleException || devMode) {

sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);

} else {

throw new ServletException(e);

}

} finally {

UtilTimerStack.pop(timerKey);

}

}

[/code]

1. 请求示意图



2. 一个请求在Struts2框架中的处理步骤:

1. 客户端初始化一个指向Servlet容器的请求

2. 根据web.xml配置,请求先经过ActionContextCleanUp过滤器,主要清理当前线程中ActionContext和Dispatcher

3. 请求经过插件过滤器(该步可以略过)

4. 请求经过StrutsPrepareAndExecuteFilter核心过滤器,执行doFilter方法,在该方法中,询问ActionMapper来决定这个请求是否需要调用Action

5. 如果ActionMapper决定调用某个Action,则ActionMapper会返回一个ActionMapping实例(存储Action的配置文件),并创建ActionProxy(Action代理)对象,将请求交给代理对象继续处理

6. ActionProxy对象根据ActionMapping和Configuration Manager询问框架的配置文件,找到需要调用的Action类

7. ActionProxy对象创建时,会同时创建一个ActionInvocation的实例

8. ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器的调用

9. 一旦Action执行完毕,ActionInvocation实例负责struts.xml中的配置创建并返回到result结果视图,result通常是一个需要被表示的JSP或者FreeMarker的模板,也可能是另外的一个Action链

10. 如果要在返回result之前做些什么,可以实现PreResultListener接口,PreResultListener可以在Interceptor中实现,也可以在Action中实现

11. 根据Result对象信息,生成用户响应信息response

3. 源码分析阶段

3.1 服务器启动阶段

1. 服务器启动后,进入web.xml,跳进配置的核心过滤器StrutsPrepareAndExecuteFilter

2. 类内部首先是三个变量的声明

protected PrepareOperations prepare;

protected ExecuteOperations execute;

protected List<Pattern> excludedPatterns = null;

[/code]

3. 进入到init方法

public void init(FilterConfig filterConfig) throws ServletException {

InitOperations init = new InitOperations();

Dispatcher dispatcher = null;

try {

FilterHostConfig config = new FilterHostConfig(filterConfig);

init.initLogging(config);

dispatcher = init.initDispatcher(config);

init.initStaticContentLoader(config, dispatcher);


prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);

execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);

this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);


postInit(dispatcher, filterConfig);

} finally {

if (dispatcher != null) {

dispatcher.cleanUpAfterInit();

}

init.cleanup();

}

}

[/code]

4.对于第5行

FilterHostConfig config = new FilterHostConfig(filterConfig);

[/code]

进入到FilterHostConfig内部查看,发现FilterHostConfig主要是用来获取Servlet上下文、web.xml中的init-param配置的param-name获取param-value的值

public class FilterHostConfig implements HostConfig {


private FilterConfig config;


public FilterHostConfig(FilterConfig config) {

this.config = config;

}

public String getInitParameter(String key) {

return config.getInitParameter(key);

}


public Iterator<String> getInitParameterNames() {

return MakeIterator.convert(config.getInitParameterNames());

}


public ServletContext getServletContext() {

return config.getServletContext();

}

}

[/code]

dispatcher中serviceAction方法
/**

* Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.

加载Action类用于映射,执行相应的Action方法,或者直接转到Result视图

* <p/>

* This method first creates the action context from the given parameters,

这个方法首先从给定的参数创建action上下文

* and then loads an <tt>ActionProxy</tt> from the given action name and namespace.

然后,从给定的action名和名称空间中加载一个ActionProxy

* After that, the Action method is executed and output channels through the response object.

之后,action方法被执行以及通过响应对象输出通道

* Actions not found are sent back to the user via the {@link Dispatcher#sendError} method,

Action如果没有发现,将通过sendError方法返回用户提示

* using the 404 return code.

使用404返回代码

* All other errors are reported by throwing a ServletException.

其他的错误将被抛出ServletException异常

*

* @param request  HttpServletRequest 对象

* @param response HttpServletResponse对象

* @param mapping  ActionMapping对象

* @throws ServletException when an unknown error occurs (not a 404, but typically something that

*                          would end up as a 5xx by the servlet container)

* @param context Our ServletContext object

*/

public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,

ActionMapping mapping) throws ServletException {


Map<String, Object> extraContext = createContextMap(request, response, mapping, context);


// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action

//如果先前有一个值栈,那么创建一个新的副本,并传递给新的Action使用


//从request中获取到ValueStack对象

ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

//判断ValueStack是否为空

boolean nullStack = stack == null;

if (nullStack) {

	//为空,获取ActionContext

ActionContext ctx = ActionContext.getContext();

if (ctx != null) {

//如果ActionContext不为空,通过ActionContext获取ValueStack---通过查看ActionContext,发现ActionContext包含ValueStack,

stack = ctx.getValueStack();

}

}

if (stack != null) {

	//把ValueStack放入ActionContext中

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();


Configuration config = configurationManager.getConfiguration();

	//加载ActionProxy

ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(

namespace, name, method, extraContext, true, false);


request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());


// if the ActionMapping says to go straight to a result, do it!

	//如果ActionMapping决定直接返回到结果视图,完成

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) {

// WW-2874 Only log error if in devMode

if (devMode) {

String reqStr = request.getRequestURI();

if (request.getQueryString() != null) {

reqStr = reqStr + "?" + request.getQueryString();

}

LOG.error("Could not find action or result\n" + reqStr, e);

} else {

if (LOG.isWarnEnabled()) {

LOG.warn("Could not find action or result", e);

}

}

sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);

} catch (Exception e) {

if (handleException || devMode) {

sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);

} else {

throw new ServletException(e);

}

} finally {

UtilTimerStack.pop(timerKey);

}

}

[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: