从Struts2源码学习Struts2的工作原理
来源:互联网 发布:怕出头知乎 编辑:程序博客网 时间:2024/06/06 00:18
今天我和我好基友啊斌通过探讨struts2的源码,总结了一下它的原理,代码是不会骗人的。
总的来说:struts的工作原理有7步:
1 客户端初始化一个指向Servlet容器的请求;
web应用程序启动时就会加载并初始化ActionServler。
用户提交表单时,一个配置好的ActionForm对象被创建,并被填入表单相应的数据,
ActionServler根据Struts-config.xml文件配置好的设置决定是否需要表单验证,
如果需要就调用ActionForm的Validate()验证后选择将请求发送到哪个Action
2 这个请求经过一系列的过滤器
在项目部署的时候,由tomcat容器读取项目的web.xml文件,测试的web.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name></display-name> <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>*.action</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list></web-app>
然后被org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter这个过滤器拦截,执行其的init方法
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { //PrepareOperations 包含在执行前请求的准备操作 protected PrepareOperations prepare; protected ExecuteOperations execute; protected List<Pattern> excludedPatterns = null; public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); Dispatcher dispatcher = null; try { //主机配置包裹filterconfig FilterHostConfig config = new FilterHostConfig(filterConfig); //初始化日志 init.initLogging(config); dispatcher = init.initDispatcher(config); init.initStaticContentLoader(config, dispatcher); prepare = new PrepareOperations(dispatcher); execute = new ExecuteOperations(dispatcher); this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); postInit(dispatcher, filterConfig); } finally { if (dispatcher != null) { //清理任何资源用于初始化调度 dispatcher.cleanUpAfterInit(); } init.cleanup(); } }
我们可以看到,传入的参数是一些配置信息,包括框架的版本,过滤器的名字,发布的项目等等
然后执行一系列初始化的的方法用于初始化环境:eg:
这里面通过执行此方法:创建和初始化调度
得到了很多有用的信息,例如:默认编码集,默认的defalutFrameworkBeanName,还有通过FreeMarker技术动态生成的一个错误页面
以前不知到报错的页面什么时候生成的,现在知道啦。
最后就是执行一些收尾工作:eg:
dispatcher.cleanUpAfterInit()清理任何资源用于初始化调度 –> init.cleanup()–> ActionContext.setContext(null); init–>FilterHostConfig(主机配置包裹filterconfig),
3: 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action
好,初始化之后一个请求冲了过来,过滤器直接拦截,并执行里面的doFilter()方法。
我们接着看源码:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; //request,response的转换 HttpServletResponse response = (HttpServletResponse) res; try { if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { chain.doFilter(request, response); } else { //prepare包含在执行前请求的准备操作 prepare.setEncodingAndLocale(request, response); //设置好Action的上下文,这个非常的重要 prepare.createActionContext(request, response); prepare.assignDispatcherToThread(); //用Struts的包装处理多部分请求的请求 request = prepare.wrapRequest(request); ActionMapping mapping = prepare.findActionMapping(request, response, true); if (mapping == null) { boolean handled = execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { execute.executeAction(request, response, mapping); } } } finally { prepare.cleanupRequest(request); } }
其中在执行prepare.createActionContext(request, response)方法中设置很多有用的信息,eg:请求的类,属性,栈工厂等等;
然后就是通过返回的map判断是否有action需要调用某个Action.
我们这里的话返回的mapping不为空,具体值如下:
,所以直接执行executeAction方法.
4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy(action的代理)
继续看我们的代码:executeAction方法中只有一句代码,就是
dispatcher.serviceAction(request, response, mapping);
然后我们进入serviceAction方法.
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { //创建上下文的map Map<String, Object> extraContext = createContextMap(request, response, mapping); // 如果前面有一个值栈,然后创建一个新的副本,并将它传递给新的操作 ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); //根据stack的状态判断nullStack的状态 boolean nullStack = stack == null; if (nullStack) { //返回特定于当前线程的Action上下文。 ActionContext ctx = ActionContext.getContext(); if (ctx != null) { //获取OGNL值栈。 stack = ctx.getValueStack(); } } if (stack != null) { extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack)); } String timerKey = "Handling request from Dispatcher"; try { UtilTimerStack.push(timerKey); //取得对应的值 eg:namespace=/ name=reg_student 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.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack()); // 如果ActionMapping说直冲向结果,则实现它 if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { //执行动态代理(非常重要) proxy.execute(); } }
执行动态代理的过程我在之前的博客有写到,这里就不过多写了。
5ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。
返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。
在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
总的来说看了源码之后对struts的工作原理加深了印象,看优秀的代码,能进步。感觉在看源码的过程中千万要坐的住,遇到看不懂的不要心烦意乱,要稳得住,坚持慢慢看,总会有所收获的。
- 从Struts2源码学习Struts2的工作原理
- Struts2的工作原理(源码分析)
- Struts2的工作原理
- Struts2的工作原理
- Struts2的工作原理
- Struts2的工作原理
- struts2的工作原理
- Struts2的工作原理
- Struts2的工作原理
- struts2的工作原理
- struts2的工作原理
- Struts2的工作原理
- struts2的工作原理
- Struts2的工作原理
- Struts2的工作原理
- Struts2的工作原理
- Struts2的工作原理
- Struts2的工作原理
- 瓦片地图-Cocos2dx
- OpenGL ES颜色混合
- ScriptManager和UpdatePanel控件的使用
- 【细说PHP学习】第十三章 PHP的错误和异常处理
- HPU 1700:做不出来踢协会!!!
- 从Struts2源码学习Struts2的工作原理
- 1034. 有理数四则运算(20)
- liferay6.2系统管理员篇-liferay的内容管理系统WCM来进行站点快速建立
- hibernate mysql映射类型
- noip1998 进位制表 (模拟)
- POJ 3349-Snowflake Snow Snowflakes(哈希)
- HDU 5384 Danganronpa(AC自动机模式串重复)
- Openerp附件存储位置
- hdu5135 Little Zu Chongzhi's Triangles(状态压缩dp)