`
eleven027
  • 浏览: 21759 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Struts2解读

阅读更多

最近抽些时间研究了一下struts2源码,总结了一下前辈的研究成果,形成自己的一点心得吧。

 

框架整合WEB的入口位于web.xml文件,只有配置在web.xml文件中Servlet才会被应用加载。Struts2推荐的入口方法是StrutsPrepareAndExecuteFilter其在工程中作为一个Filter配置在web.xml中,配置如下:

 

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

 

 

/**
 * 
 * Struts 2框架的处理过程: 
 * 1. 客户端初始化一个指向Servlet容器(如Tomcat)的请求。 
 * 2. 这个请求经过一系列过滤器(如ActionContextCleanUp、SiteMesh等)。 
 * 3. StrutsPrepareAndExecuteFilter/FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action。 
 * 4. 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求交给ActionProxy。 
 *    这儿已经转到它的Delegate--Dispatcher来执行 
 * 5. ActionProxy通过Configuration Manager询问Struts配置文件,找到需要调用的Action类。 
 * 6. ActionProxy创建一个ActionInvocation实例。 
 * 7. ActionInvocation实例使用命名的模式来调用,回调Action的execute方法。 当然这涉及到相关拦截器的调用
 * 8. 一旦Action执行完毕,ActionInvocation负责根据Struts.xml的配置返回结果    
 * 	     当然如果要在返回之前做些什么,可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现
 * 
 * @author Josn
 *
 */
 

StrutsPrepareAndExecuteFilter中主要组成有:

属性:

protected PrepareOperations prepare; protected ExecuteOperations execute; protected List<Pattern> excludedPatterns=null;

方法:

init(FilterConfig filterConfig)      继承自Filter,初始化参数

doFilter(ServletRequest req, ServletResponse res, FilterChain chain)    继承自Filter,执行方法

postInit(Dispatcher dispatcher, FilterConfig filterConfig)  一个空的方法,用于方法回调初始化

 destroy()   继承自Filter,用于资源释放

 

 

具体实现及其代码描述如下:

 

/* 继承自Filter,初始化参数

	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	public void init(FilterConfig filterConfig) throws ServletException {
		InitOperations init=new InitOperations();//初始化辅助对象,封装了初始化的一些操作
		try {
			System.out.println(this.getClass().getName()+":"+filterConfig.getFilterName());
			Enumeration e=filterConfig.getInitParameterNames();
			while(e.hasMoreElements()){
				System.out.println(e.nextElement().toString());
			}
			//对filterConfig进行封装  其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中
			FilterHostConfig config=new FilterHostConfig(filterConfig);
			//通过config, 初始化struts内部日志
			Iterator<String> cs=config.getInitParameterNames();
			init.initLogging(config); 
			Dispatcher dispatcher=init.initDispatcher(config); //通过config,创建并初始化dispatcher
			init.initStaticContentLoader(config, dispatcher);//通过config和dispatcher,初始化与过滤器相关的静态内容加载器
			prepare=new PrepareOperations(filterConfig.getServletContext(),dispatcher); //通过config和dispatcher,创建request被处理前的系列操作对象
			execute=new ExecuteOperations(filterConfig.getServletContext(),dispatcher);//通过config和dispatcher,创建处理request的系列操作对象
			this.excludedPatterns=init.buildExcludedPatternsList(dispatcher);//构建Struts2不处理的URL列表
			
			//回调空的postInit方法
			postInit(dispatcher,filterConfig);
		} finally {
			init.clearup();//清空ActionContext
		}
	}
	
	/* 
	 * 继承自Filter,用于资源释放
	 */
	public void destroy() {
		prepare.cleanupDispatcher();
	}
	/* 继承自Filter,执行方法
	 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
	 */
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
		//父类向子类转:强转为http请求、响应
		HttpServletRequest request=(HttpServletRequest)req;
		HttpServletResponse response=(HttpServletResponse) resp;
		try{
			//setEncodingAndLocale调用了dispatcher方法的prepare方法:
			prepare.setEncodingAndLocale(request, response); //设置request的编码和LOCAL
			//创建Action上下文
			prepare.createActionContext(request, response);//创建ACTIONCONTEXT,并初始化Theadlocal 
            prepare.assignDispatcherToThread(); //指派dispatcher给Theadlocal把dispatcher绑定到当前线程上,这样就可以通过 Dispatcher.getInstance()来获得当前线程的dispatcher,这样dispatcher就可以被所有请求共享
                                                // 以上操作完成后就根据excludedPatterns来判断这个请求是否应该交给当前这个filter进行处理,如果不需要就调
                                                //用chain.doFilter(request, response);交给其他filter进行处理,如果需要,那么PrepareOperations就执行
			 if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
				chain.doFilter(request, response);
			 } else {
				request = prepare.wrapRequest(request);//对request进行包装,包装成StrutsRequestWrapper或者 MultiPartRequestWrapper(请求类型是multipart/form-data的情况)
				ActionMapping mapping = prepare.findActionMapping(request, response, true);//查找并选择创建ActionMapping根据request的请求找
																							//到对应的ActionMapping,如果返回的是null,那就当做静态资源进行处理boolean handled =
																							//execute.executeStaticResourceRequest(request, response);处理失败就交给其他的filter,如果返回的不是
																							//null,那么就调用execute.executeAction(request, response, mapping);处理完成之后
																							//prepare.cleanupRequest(request);会把当前线程的ActionContext和Dispatcher设置为null
				if (mapping == null) {//如果映射不存在
					boolean handled=execute.executeStaticResourceRequest(request, response);
					if(!handled){
						chain.doFilter(request, response);
					}
				}else{ //如果存在映射
					execute.executeAction(request, response, mapping);//执行action
				}
			 }
		}finally{
			prepare.cleanupRequest(request);//清除request的Threadlocal
		}
	}
	/**
	 * Callback for post initialization(一个空的方法,用于方法回调初始化)
	 * @param dispatcher
	 * @param filterConfig
	 */
	protected void postInit(Dispatcher dispatcher,FilterConfig filterConfig){
		
	}
 

 

ActionContext保存了当前请求的上下文信息,当接到一个request以后,经过一些准备工作,都是再去调用Dispatcher.serviceAction(HttpServletRequest, HttpServletResponse,   ServletContext, ActionMapping)方法处理请求,也就是这一步,让后续的Action能够和Servlet体系解耦而单独存在。StrutsPrepareAndExecuteFilter里面,先准备好上下文需要的东西,接着就会 构造出一个ActionMapping对象,这对象很简单(跟xwork.xml/struts.xml里的一个节点相对应的)。构造完,就扔给Dispatcher的serviceAction方法了。Dispatcher根据拿到的actionMapping对 象,创建ActionProxy对象(内部包含了目标Action),然后开始执行。ActionProxy的默认实现DefaultActionProxy里有一个叫ActionInvocation的对象,采用Command模式实现调用,命令: Action     调用者:ActionInvocation。

 

 init是Filter第一个运行的方法,我们看下struts2的核心Filter在调用init方法初始化时做哪些工作:

在init中带哦用了方法init.initDispatcher(config);这个方法里面有两个动作:

createDispatcher(filterConfig);

dispatcher.init();

来看这两个方法体的实现:

/**

	 * 创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,
	 * 然后根绝servlet上下文和参数Map构造Dispatcher 
	 * @param filterConfig
	 * @return
	 */
	private Dispatcher createDispatcher(HostConfig filterConfig){
		Map<String,String> params=new HashMap<String, String>();
		Iterator e=filterConfig.getInitParameterNames();
		while(e.hasNext()){
			String key=(String)e.next();
			String value=filterConfig.getInitParameter(key);
			params.put(key, value);
		}
		return  new Dispatcher(filterConfig.getServletContext(),params);
	}

	public void init(){
		if(configurationManager == null){
			configurationManager=new ConfigurationManager();
		}
		try {
			init_DefaultProperties();// [1]读取properties信息,默认的default.properties  struts.properties/xwork.properties
			init_TraditionalXmlConfigurations();//[2]读取xml配置文件,默认的struts-default.xml,struts-plugin.xml,struts.xml ,xwork.xml文件(我们配置的具体的action的文件
			init_LegacyStrutsProperties(); // [3]读取用户自定义的struts.properties国际化相关的配置
           		init_CustomConfigurationProviders(); // [5]自定义的configProviders 
           	        init_FilterInitParameters() ; // [6]载入FilterDispatcher传进来的initParams 整了一些Servlet相关的对象
           		init_AliasStandardObjects() ; // [7]将配置文件中的bean与具体的类映射   结合第1步读到的东西,注册StrutsConstants里提到的种种配置
            
            Container container=init_PreloadConfiguration();//构建一个用于依赖注射的Container对象     
            												//在这里面会循环调用上面七个ConfigurationProvider的register方法     
            												//其中的重点就是DefaultConfiguration的#reload()方法
            container.inject(this);
            init_CheckConfigurationReloading(container);
            init_CheckWebLogicWorkaround(container);
            
            if(!dispatcherListeners.isEmpty()){
            	for(DispatcherListener l:dispatcherListeners){
            		l.dispatcherInitialized(this);
            	}
            }
		} catch (Exception ex) {
			 if (LOG.isErrorEnabled()) LOG.error("Dispatcher initialization failed", ex);
	            throw new StrutsException(ex);
		}
	}

	/**
	 * 这个方法中是将一个DefaultPropertiesProvider对象追加到ConfigurationManager对象内部的ConfigurationProvider队列中。 
	 * DefaultPropertiesProvider的#register()方法可以载入org/apache/struts2/default.properties中定义的属性。
	 */
	private void init_DefaultProperties(){
		configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
	}
	/**
	 * 这里负责载入的是FilterDispatcher的配置中所定义的config属性。 
	 * 如果用户没有定义config属性,struts默认会载入DEFAULT_CONFIGURATION_PATHS这个值所代表的xml文件。 
	 * 它的值为"struts-default.xml,struts-plugin.xml,struts.xml"。也就是说框架默认会载入这三个项目xml文件。 
	 * 下一步框架会逐个判断每个config属性中定义的文件。
	 * 如果文件名为"xwork.xml",框架会用XmlConfigurationProvider类去处理,
	 * 反之则用StrutsXmlConfigurationProvider类去处理。 
	 */
	private void init_TraditionalXmlConfigurations(){
		String configPaths=initParams.get("config");
		if(configPaths==null){
			configPaths=DEFAULT_CONFIGURATION_PATHS;
		}
		String[] files=configPaths.split("\\s*[,]\\s*");
		for(String file:files){
			if(file.endsWith(".xml")){
				if("xwork.xml".equals(file)){
					configurationManager.addConfigurationProvider(createXmlConfigurationProvider(file,false));
				}else{
					configurationManager.addConfigurationProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
				}
			}else {
                throw new IllegalArgumentException("Invalid configuration file name");
            }
		}
	}
    protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {
        return new XmlConfigurationProvider(filename, errorIfMissing);
    }
    protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
        return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
    }
	/**
	 * 向ConfigurationManager加入了一个LegacyPropertiesConfigurationProvider。
	 */
	private void init_LegacyStrutsProperties(){
		configurationManager.addConfigurationProvider(new LegacyPropertiesConfigurationProvider());
	}
	/**
	 * 此方法处理的是FilterDispatcher的配置中所定义的configProviders属性。
	 * 负责载入用户自定义的ConfigurationProvider。
	 */
	private void init_CustomConfigurationProviders(){
		 String configProvs = initParams.get("configProviders");
		 if (configProvs != null) {
	            String[] classes = configProvs.split("\\s*[,]\\s*");
	            for (String cname : classes) {
	                try {
	                	Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
	                    ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
	                    configurationManager.addConfigurationProvider(prov);
	                } catch (InstantiationException e) {
	                    throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
	                } catch (IllegalAccessException e) {
	                    throw new ConfigurationException("Unable to access provider: "+cname, e);
	                } catch (ClassNotFoundException e) {
	                    throw new ConfigurationException("Unable to locate provider class: "+cname, e);
	                }
	            }
	     }
	}
	/**
	 * 此方法用来处理FilterDispatcher的配置中所定义的所有属性。 
	 */
	@SuppressWarnings("deprecation")
	private void init_FilterInitParameters(){
		 configurationManager.addConfigurationProvider(new ConfigurationProvider() {
	            public void destroy() {}
	            public void init(Configuration configuration) throws ConfigurationException {}
	            public void loadPackages() throws ConfigurationException {}
	            public boolean needsReload() { return false; }
	            public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
	                props.putAll(initParams);
	            }
	        });
	}
	private void init_AliasStandardObjects(){
		configurationManager.addConfigurationProvider(new BeanSelectionProvider());
	}
	
	/**
	 * 执行完七个init_*方法后,Dispatcher的#init()会接着调用#init_PreloadConfiguration(),
	 * 构建一个用于依赖注射的Container对象。 
	 * 此方法首先获取到ConfigurationManager中的Configuration对象,
	 * 在#getConfiguration()内部,调用上边6步添加到ConfigurationManager的ConfigurationProviders的#register()方法。 
	 */
	private Container init_PreloadConfiguration(){
		Configuration config=configurationManager.getConfiguration();
		Container container=config.getContainer();
		boolean reloadi18n=Boolean.valueOf(container.getInstance(String.class,StrutsConstants.STRUTS_I18N_RELOAD));
		LocalizedTextUtil.setReloadBundles(reloadi18n);
		return container;
	}
 

 

分享到:
评论
1 楼 taoshi 2014-11-24  
好文章,谢谢

相关推荐

    struts2技术内幕+struts2权威指南

    《Struts2技术内幕:深入解析Struts2架构设计与实现原理》由国内极为资深的...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象

    struts2 技术内幕——深入解析struts2架构设计

    《Struts2技术内幕:深入解析Struts2架构设计与实现原理》由国内极为资深的...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象。

    struts2 源码解读

    NULL 博文链接:https://tpsnake.iteye.com/blog/1566327

    Struts2 技术内幕-深入解析Struts2架构设计与实现原理

    内容简介 出版日期: 2012年1月10日 《Struts2技术内幕:深入解析Struts2架构...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象。

    Struts2技术内幕.pdf

    本书由国内极为资深的Struts2技术专家(网名:downpour)亲自执笔,iteye兼...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象。

    struts2教程(完全版)

    struts2教程 关于struts2的基本文件配置及实例解读

    Struts1.2源码解读

    该文档是针对解析Struts源码所编写的一个文档,是入门Struts,精通Struts的精品文档,该文档为北大青鸟学习Struts的内部资料!

    struts详细文档讲解及实例

    struts详细文档,strut讲解、struts实例

    struts.xml常用配置解析

    struts.xml常用配置解析 网上转载

    struts2 session 解读

    session 在struts2中的用法

    struts2框架原理及详细配置

    struts2框架有关的详细解读,方便开发的朋友查阅,特别是配置的相关知识。

    java结合struts的上传下载文件

    java结合struts的上传下载文件,看不懂的可以私信我,在线解读。

    ssh2(struts2+spring2.5+hibernate3.3)自动生成模版

    {自定义的存放包}目录:4个xml文件(applicationContext-dao.xml(dao注入配置),applicationContext-service.xml(service注入配置),action-servlet.xml(action注入配置),struts-{自定义的存放包名}.xml(struts2的...

    Servlet解读

    深入解读servlet的执行流程,理解servlet的开发,方便struts的学习

    spring.jar spring-webmvc-struts.jar spring-webmvc.jar

    spring.jar spring-webmvc-struts.jar spring-webmvc.jar 很难找.......欢迎下载!

    SSH框架与AJAX技术的Java_Web应用开发(4页,pdf书籍)不是代码

    《Java Web开发技术大全:JSP+Servlet+Struts+Hibernate+Spring+Ajax》重点讲解了Struts 2、Speing和HIbernate框架的基础知识和高级技术,如Sruts 2中的拦截器、类型转换、国际化和标签等,HIbe rna{e的会话、0/R映射...

    Java Web开发实例大全(基础卷) 完整pdf扫描版[179MB]

    重点内容有操作XML文件、发送与接收邮件、数据库操作技术、SQL语句应用技术、复杂查询技术、数据库高级应用...Struts2框架应用、Struts2框架标签应用、Hibernate框架基础、Hibernate高级话题、Spring框架基础、Spring的...

    Java Web开发实例大全

    重点内容有操作XML文件、发送与接收邮件、数据库操作技术、SQL语句应用技术、复杂查询技术、数据库高级应用...Struts2框架应用、Struts2框架标签应用、Hibernate框架基础、Hibernate高级话题、Spring框架基础、Spring的...

    java当当网源码-open-source-learning:技术源码研究

    struts2,spring and so on,not including enterprise application project,like XXXX_system) ##struts2框架源码分析 Struts2源码剖析与架构指导 Struts2技术内幕 ##spring框架源码分析 Spring源码解读与设计详析 ...

Global site tag (gtag.js) - Google Analytics