`

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初始化工作到这里也就结束了。
  • 大小: 15.8 KB
  • 大小: 44.8 KB
1
0
分享到:
评论
2 楼 zhouchaofei2010 2012-10-09  
我也在看downpour的《Struts2技术内幕》,其中对容器(Container)是如何保证其为全局的单例的变量,不是很清楚,能交流下么?
1 楼 daomideweixiao 2012-04-10  
写的真不错。

相关推荐

    struts2 源码分析

    从 org.apache.struts2.dispatcher.FilterDispatcher 开始 Java 代码阅读,我们可以看到 FilterDispatcher 的 init 方法,它负责初始化 Dispatcher 对象,并创建了一个 ActionMapper 实例,以便对请求进行处理。...

    struts2源码分析总结

    本文将深入探讨Struts2的源码分析,特别是关于StrutsPrepareAndExecuteFilter的初始化过程,这是Struts2的核心组件之一,负责处理HTTP请求。 首先,我们来看`StrutsPrepareAndExecuteFilter`的初始化。这个过滤器...

    Struts2源码阅读

    `ActionProxy`创建并初始化`ActionInvocation`对象,`ActionInvocation`则负责执行Action,并管理拦截器(Interceptors)的调用。 在请求流程中,`ActionInvocation`会按照预定义的顺序调用拦截器,然后执行Action...

    struts2源码最新

    - **StrutsPrepareAndExecuteFilter**:这是Struts2的核心过滤器,负责初始化Struts2框架并处理所有的HTTP请求。 - **ActionContext**:存储了请求期间的上下文信息,包括值栈、session、request和response对象等。...

    Struts2源码分析

    `Dispatcher`类在Struts2中扮演着重要角色,它负责框架的初始化和Action的执行。`FilterDispatcher`则是一个Servlet Filter,根据配置文件来决定哪些请求需要经过Struts2处理。如果配置不当,可能会导致问题,如将...

    struts2源码

    struts2源代码及API帮助文档。...跟读struts2源码的时候注意两条主线:一条是初始化;另外一条是监听http请求。学习初始化过程是如何获取配置构造容器,其中的巧妙构思和设计模式的运用非常值得学习。

    struts2 源码绝对完整

    1. **org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter**:这是Struts2的核心过滤器,它初始化并调用Struts2的生命周期。 2. **com.opensymphony.xwork2.DefaultActionProxy**:ActionProxy...

    深入浅出Struts2源码-例子程序(完整版)

    8. **生命周期管理**:Struts2中的Action和ActionSupport类的生命周期,包括初始化、执行和清理,理解这些过程有助于优化性能。 9. **国际化与本地化**:Struts2支持多语言,开发者可以通过配置资源文件实现内容的...

    struts2部分源码分析

    Interceptor链是由用户自定义的拦截器和Struts2内置的拦截器组成,例如PrepareInterceptor会确保Action实例被正确初始化。Action执行完成后,Result对象会根据配置的类型和值来决定如何处理返回的结果。 此外,...

    Struts2权威指南源码(完整),不含JAR文件

    1. **初始化过程**:了解Struts2如何加载配置文件,初始化ActionMapping和Interceptor链。 2. **请求处理流程**:跟踪一个HTTP请求从进入Struts2到返回响应的整个过程,重点关注ActionInvocation、Interceptor的...

    struts2源码2

    Struts2是一个基于MVC(Model-View-Controller)设计模式的开源JavaEE框架,它在Web应用开发中被广泛使用。源码分析是深入理解框架工作原理、优化代码以及发现潜在问题的重要途径。让我们一起深入探讨Struts2的核心...

    Struts1源码解析

    ActionServlet是Struts1的核心组件,它的生命周期分为初始化、拦截请求和销毁三个阶段。在初始化阶段,`init()`方法执行了一系列关键步骤: 1. `initInternal()`方法初始化内部资源,如国际化设置。它包含了英文和...

    struts1.2源码研究

    ActionServlet的初始化过程是整个框架工作的基础,它负责加载配置、初始化模块并处理请求。通过深入理解Struts 1.2的源码,开发者可以更好地利用这个框架,解决实际项目中的问题,同时为自定义扩展和优化提供可能。

    struts2源码研究

    Struts2 源码分析主要涉及其在Tomcat启动过程中的初始化步骤以及请求处理流程。首先,我们来看Tomcat启动时Struts2框架如何准备和执行。 在Tomcat启动时,Struts2的Filter文件被加载,具体是`...

    Manning - Struts 2 in Action源码

    《Struts 2 in Action》是由Manning出版社出版的一本权威书籍,专注于讲解Struts 2框架的应用与实践。源码的提供使得读者能够深入理解Struts 2的工作原理,并有机会亲手实践书中所介绍的各种技术。 Struts 2是Java...

    Struts2 src 源码下载

    1. **StrutsPrepareAndExecuteFilter**:作为Servlet Filter,负责初始化Struts2环境并处理请求。 2. **ActionMapping**:映射HTTP请求到特定的Action。 3. **StrutsResult**和**Result**接口:定义了Action执行后的...

    struts2源码解析,个人感觉很不错

    3. **org.apache.struts2.dispatcher**:核心包之一,包含重要的类如`Dispatcher`,用于初始化框架和调用指定Action的`execute()`方法。 4. **org.apache.struts2.impl**:定义了三个类`StrutsActionProxy`、`...

    struts2的源码

    通过源码,我们可以看到这些对象如何创建、初始化、销毁,以及它们之间的协作机制。 7. **配置解析**:Struts2的配置文件(struts.xml)是应用的核心配置。源码展示了如何解析这些XML配置,将它们转化为内部的数据...

    浪曦struts2源码第四课

    2. **DefaultActionProxy**:负责创建ActionInvocation实例,并初始化拦截器链。 3. **ActionMapper**:根据请求URL映射到具体的Action,这部分源码展示了URL到Action的解析过程。 4. **InterceptorStack**:管理...

    Struts1.2源码解读

    北大青鸟的这份文档是为了帮助学习者入门和精通Struts所编写的,包含了对Struts源码的详细解析。 首先,了解Struts的核心控制器ActionServlet是必要的。ActionServlet继承自javax.servlet.http.HttpServlet类,负责...

Global site tag (gtag.js) - Google Analytics