struts2源码初读(一)初始化

    很久之前就有读一下struts2源码的想法了,可是一直没什么时间也静不下心来,最近淡定下来了晚上也没什么事,综合项目看看了struts2源码,目前还是个菜,写的不对的地方请大家谅解
    看了downpour的文章让我受益匪浅,文章中会引用到downpour的文章和图片,顺便给downpour推荐下新书《Struts2技术内幕》,看了样章个人感觉很不错,值得一看
-----------------------------------------------------------------------------------
downpour的《Struts2技术内幕》 新书样章和导读
-----------------------------------------------------------------------------------
看源代码之前了解下struts2逻辑结构,此处引用自downpour文章


downpour 写道

3.3.1.2 Struts2的初始化主线

Struts2的初始化主线发生在Web应用程序启动之初,由入口程序的init方法驱动执行完成。这条运行主线主要特点有:

仅在Web应用启动时执行一次

由于这条主线由Filter中的init方法驱动执行,执行完毕后,该主线结束。也就是说,这条主线本身不参与后任何的Http请求的处理过程,无论Struts2之后再收到多少Http请求,这条主线都不会重复执行。

Init方法的执行失败将导致整个Web应用启动失败

如果在init方法执行的过程中发生异常,整个Web应用将无法启动。这个特点从框架规范的角度规定了我们必须确保初始化过程的顺利执行。因为在这个过程中,所以框架内部定义的元素将被初始化,并支撑起整个Struts2进行Http处理的过程。

这两大特点本身其实来源于Filter这个Servlet规范的基本运行特性。然而,这两大特点却也为应用程序在框架的基础之上进行逻辑扩展提供了理论上的指导意见。在之后有关如何扩展框架的话题讨论中,我们可以看到所有的扩展方案都将基于这两大特点进行设计。

那么,Struts2的初始化主线到底做了点什么呢?对应于Struts2初始化的运行特点,Struts2的初始化主线也有两大主要内容:

框架元素的初始化工作

这一初始化工作包含了对框架内部的许多内置对象的创建和缓存。我们发现,对于框架初始化工作的基本要求,就是在整个框架的运行过程中仅被执行一次,这正好符合这条主线的基本特点。

控制框架运行的必要条件

框架的可扩展特性保证了我们可以在应用层面对框架的运行参数和执行模式进行定制化,而框架则有必要对这种定制化进行正确性校验。当这种校验失败时,Web应用的启动会失败。这也就是Struts2在框架级别所能够提供的运行期的检查。

初始化主线贯穿了Struts2对其内置对象的创建和缓存的过程,这一过程相当于把整个Struts2作为一个框架的运行环境完整地创建出来。这条主线的顺利运行,为之后的Http请求处理主线提供了必要的框架运行环境。

我们在这里所说的运行环境和Struts2自身的运行环境不同,它是指建立在Web服务器之上,框架自身运行所必须的内置对象的集合。为了更好地对这些内置对象进行管理,Struts2引入了框架级别“容器”的概念。因而Struts2的初始化主线,实际上最终转化为对这个“容器”的初始化过程。有关这个“容器”的定义和初始化过程的细节,我们将在第五章中为读者解开谜团。

3.3.1.3 Struts2的Http请求处理主线

Struts2的Http请求处理主线是Struts2的核心主线,包含了Struts2处理Http请求、进行必要的数据处理和处理数据返回的全部过程。这条主线将在任何满足web.xml中所指定的URL Pattern的Http请求发生时进行响应,由doFilter方法负责驱动执行。

如果我们回顾一下Struts2核心入口程序的流程图(图2-4),我们可以看到Struts2的Http请求处理主线又被一条分割线划分成了两个不同的执行阶段:

第一阶段 —— Http请求预处理

在这个阶段中,程序执行的控制权在Struts2手上。这个阶段的主要工作是针对每个Http请求进行预处理,为真正的业务逻辑执行做必要的数据环境和运行环境的准备。
程序代码在这个阶段有一个非常显著的特点:依赖于Web容器,并时时刻刻将与Web容器打交道作为主要工作。


第二阶段 —— XWork执行业务逻辑

在这个阶段,程序执行的控制权被移交给了XWork。Struts2在完成了Http请求的预处理之后,将Http请求中的数据封装成为普通的Java对象,并由XWork负责执行具体的业务逻辑。
程序代码在这个阶段的特点和第一阶段完全相反:不依赖于Web容器,完全由XWork框架驱动整个执行的过程。

从Struts2对于Http请求的处理过程中,我们可以看出Struts2的核心设计理念在于解耦。所谓解耦,实际上是尽可能地消除核心程序对外部运行环境的依赖,从而保证核心程序能够更加专注于应用程序的逻辑本身。在Struts2中,我们所说的外部运行环境就是Web容器。我们在这里可以看到,Struts2的核心设计理念与Struts2的运行环境居然是一个矛盾体!

老套路熟悉下Struts2框架和请求流程


Struts2请求流程
1、客户端发送请求
2、StrutsPrepareAndExecute通过ActionMapper来决定这个Request需要调用哪个Action
3、如果ActionMapper决定调用某个Action,FilterDispatcher把请求的处理交给ActionProxy,这儿已经转到它的Delegate--Dispatcher来执行
4、ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类
5、ActionProxy创建一个ActionInvocation的实例
6、ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用
7、Action执行完毕,ActionInvocation创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener


准备知识做完了,现在开始看struts2源码。说到浏览struts2源码当然要从struts2的入口类StrutsPrepareAndExecute开始
在web.xml中配置filter,filter-class是StrutsPrepareAndExecute类或其子类
    <filter>
		<filter-name>Struts2</filter-name>
		<filter-class>
com.application.struts2.filter.AppFilterDispatcher</filter-class>
		<init-param>
			<param-name>AppStartupOjbectName</param-name>
			<param-value>AppStartupService</param-value>
		</init-param>
	</filter>

StrutsPrepareAndExecute实现filter接口,所以必不可少的三个方法init,doFilter,destory,看单词就知道分别做了初始化,请求处理和销毁
public class AppFilterDispatcher extends StrutsPrepareAndExecuteFilter {
	@Override
	public void init(FilterConfig arg0) throws ServletException {
		super.init(arg0);
		String object_name = arg0.getInitParameter("AppStartupOjbectName");
		//获取Spring上下文
		WebApplicationContext springAppContext = WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext());
		//通过Spring上下文获取启动对象
		IStartupObject startupObject = (IStartupObject) springAppContext.getBean(object_name);
		if(startupObject != null){
			startupObject.initializeSystemDictionary(arg0.getServletContext());
			...
		}
	}
}

先看一下init方法
/**
	 * 初始化顺序为
	 * initLogging-->initDispatcher-->initStaticContentLoader-->cleanup
	 */
    public void init(FilterConfig filterConfig) throws ServletException {
        /**
         * InitOperations 初始化操作工具类
         */
    	InitOperations init = new InitOperations();
        try {
        	/**
        	 * FilterHostConfig   Filter相关信息,用于创建Dispatcher
        	 */
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            /**
             * 初始化日志,只要在web.xml的Filter中配置loggerFactory参数
             * 值为实现了com.opensymphony.xwork2.util.logging.LoggerFactory的类
             * struts2就可以使用这个日志类做日志
             */
            init.initLogging(config);
            /**
             * 用Filter参数创建Dispatcher对象
             *      Dispatcher dispatcher = createDispatcher(filterConfig)
             * 			    创建Dispatcher过程:从filterConfig获得初始化参数,然后封装成一个HashMap
             * 			 	通过ServletContext和HashMap创建Dispatcher对象
        	 * 		dispatcher.init()加载配置文件,包括xml的和零配置的配置
        	 * 				更新一些可选的配置参数,如编码、开发模式、是否加载资源文件等
             */
            Dispatcher dispatcher = init.initDispatcher(config);
            /**
             * 通过filterconfig和dispatcher初始化静态上下文
             */
            init.initStaticContentLoader(config, dispatcher);
            /**
             * 实例化对象
             */
            prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
            execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
            /**
             * 初始化包含的URL模式
             */
			this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
            postInit(dispatcher, filterConfig);
        } finally {
        	/**
        	 * 清除actionContext中的内容
        	 */
            init.cleanup();
        }

    }

init中比较重要的是Dispatcher dispatcher = init.initDispatcher(config)创建并初始化Dispatcher
/**
	 * InitOperations
	 * 初始化Dispatcher
	 */
	public Dispatcher initDispatcher( HostConfig filterConfig ) {
        Dispatcher dispatcher = createDispatcher(filterConfig);
        dispatcher.init();
        return dispatcher;
    }
	
	/**
	 * InitOperations
	 * 创建Dispatcher
	 */
	private Dispatcher createDispatcher( HostConfig filterConfig ) {
        Map<String, String> params = new HashMap<String, String>();
		/**
		 *  遍历web.xml文件中<filter>里配置的<init-param>
		 *  通过上面web.xml中的配置可以看到e为[AppStartupOjbectName, null,
                 *    null, null, null, null, null, null, null, null]
		 *  name为AppStartupOjbectName value为AppStartupService
		 */
        for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
            String name = (String) e.next();
            String value = filterConfig.getInitParameter(name);
            params.put(name, value);
        }
		/**
		 *  通过servlet上下文和filter参数创建Dispatcher对象
		 */
        return new Dispatcher(filterConfig.getServletContext(), params);
    }
	
	/**
	 * Dispatcher
     * 加载配置文件,包括xml的和零配置的配置
     * 更新一些可选的配置参数,如编码、开发模式、是否加载资源文件等
     */
    public void init() {

    	if (configurationManager == null) {
    		configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
    	}

        try {
        	/**
        	 * 将一个DefaultPropertiesProvider对象追加到
        	 * ConfigurationManager对象内部的ConfigurationProvider队列中 
        	 * DefaultPropertiesProvider的register()方法可以载入
                 * org/apache/struts2/default.properties中定义的属性
        	 */
            init_DefaultProperties(); // [1]
            /**
             * 读取web.xml的filter中配置的config参数
             * 如果没配置会找默认文件struts-default.xml,struts-plugin.xml,struts.xml
             */
            init_TraditionalXmlConfigurations(); // [2]
            /**
             * 加载自定义的struts配置属性(.properties)
             */
            init_LegacyStrutsProperties(); // [3]
            /**
             * 初始化自定义的Provider
		     *  读取web.xml的filter中配置的configProviders参数
             */
            init_CustomConfigurationProviders(); // [5]
            /**
             * 加载web.xml中filter配置的所有参数
             */
            init_FilterInitParameters() ; // [6]
            /**
             * 将一个BeanSelectionProvider类追加到ConfigurationManager对象内部的
             * ConfigurationProvider队列中
             * BeanSelectionProvider主要实现加载org/apache/struts2/struts-messages
             */
            init_AliasStandardObjects() ; // [7]
            /**
             * 调用上边创建的ConfigurationManager的getConfiguration()获得当前XWork配置对象
             */
            Container container = init_PreloadConfiguration();
            container.inject(this);
            /**
             * 检查配置重新加载
             */
            init_CheckConfigurationReloading(container);
            /**
             * 初始化weblogic相关配置
             */
            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);
        }
    }

Dispatcher的init方法加载了一系列配置文件,按照序号一个一个看

[1] init_DefaultProperties
private void init_DefaultProperties() {
        configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
    }
	
	/**
	 * DefaultPropertiesProvider
	 * 读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写
	 */
	public void register(ContainerBuilder builder, LocatableProperties props)
            throws ConfigurationException {
        
        Settings defaultSettings = null;
        try {
            defaultSettings = new PropertiesSettings("org/apache/struts2/default");
        } catch (Exception e) {
            throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);
        }
        
        loadSettings(props, defaultSettings);
    }


[2] init_TraditionalXmlConfigurations
private void init_TraditionalXmlConfigurations() {
		/**
		 *  读取web.xml的filter中配置的config参数,根据上面定义此处configPaths的值为null
		 */
        String configPaths = initParams.get("config");
		/**
		 * 如果我们没有定义config参数,赋值默认的值
		 * DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml"
		 */
        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(new XmlConfigurationProvider(file, false));
                } else {
                    configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
                }
            } else {
                throw new IllegalArgumentException("Invalid configuration file name");
            }
        }
    }


[5] init_CustomConfigurationProviders
private void init_CustomConfigurationProviders() {
		/**
		 *  初始化自定义的Provider
		 *  读取web.xml的filter中配置的configProviders参数,根据上面定义此处configProvs的值为null
		 */
        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);
                }
            }
        }
    }


[6] init_FilterInitParameters
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; }
			/**
			 * 加载web.xml中filter配置的所有参数
			 */
            public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
                props.putAll(initParams);
            }
        });
    }


[7] init_AliasStandardObjects
private void init_AliasStandardObjects() {
        configurationManager.addConfigurationProvider(new BeanSelectionProvider());
    }
	/**
	 * 主要加载org/apache/struts2/struts-messages
	 */
	... ...
	LocalizedTextUtil.addDefaultResourceBundle("org/apache/struts2/struts-messages");
	... ...


再回到StrutsPrepareAndExecuteFilter的init方法,init.initStaticContentLoader(config, dispatcher)通过filterConfig和
dispatcher初始化静态上下文,实例化请求处理阶段需要用到的实例化对象prepare和execute,并初始化包含的URL模式,之后就
回到我自定义的AppFilterDispatcher类中继续自定义的初始化工作。spring通过我在init-param参数注入bean,然后开始我自定
义的初始化函数。这里主要是初始化一些全局变量,如数据字典,模块配置等,不过这里的初始化和Struts2的初始化主线一样只
在启动服务是加载一次,如果数据库中做了修改需要重启服务才能看到变化。struts2初始化工作到这里也就结束了。

猜你喜欢

转载自ferry-passion.iteye.com/blog/1328999