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框架执行过程的了解,有什么不对的地方还请各位大神指点
相关文章推荐
- java回调函数的初步理解
- JavaBean与表单
- 一个简单音乐播放器的java实现(一)
- jenkins调整jdk版本不生效的解决办法
- 大坑----java生成xls,xlsx文件的行数啊
- 初学Struts
- JAVA反射系列之Field,java.lang.reflect.Field使用获取方法。
- MyEclipse 2014 搭建 Android 开发环境
- java反射机制详解
- JAVA锁漫谈,最好的锁是无锁
- JAVA学习笔记(一):一个小爬虫的例子
- Java编程思想 第1章 对象导论
- 转:java读文件时判断其编码格式(亲测可用)
- Java笔记20:迭代器模式
- 出现java.lang.UnsupportedClassVersionError 错误的原因
- java 深入了解String
- 如何在Mac系统安装eclipse并运行java程序?
- Java反射机制深入研究
- java泛型中<? super T> 和<? extends T>的区别---转发
- LEETCODE 17 Letter Combinations of a Phone Number (JAVA题解)