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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值