Struts2源码阅读(三)_DispatcherConfigurationProvider

首先强调一下struts2的线程程安全,在Struts2中大量采用ThreadLocal线程局部变量的方法来保证线程的安全,像Dispatcher等都是通过ThreadLocal来保存变量值,使得每个线程都有自己独立的实例变量,互不相干.

接下来就从Dispatcher开始看起,先看其构造函数:

//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方     public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {     this.servletContext = servletContext;     //配置在web.xml中的param参数     this.initParams = initParams;     } 

我们再看在FilterDispatcher创建Dispatcher的:

protected Dispatcher createDispatcher(FilterConfig filterConfig) {     Map<String, String> params = new HashMap<String, String>();     for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {     String name = (String) e.nextElement();     String value = filterConfig.getInitParameter(name);     params.put(name, value);     }         return new Dispatcher(filterConfig.getServletContext(), params);     
} 

分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.
将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.
最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.
下面将分析这七层功夫是怎样一步步练成的.

 XxxProvider类图:

首先是init_DefaultProperties()

创建Dispatcher之后,来看init()方法
init()方法是用来Load用户配置文件,资源文件以及默认的配置文件.

主要分七步走,看下面注释

public void init() {     if (configurationManager == null) {     //设置ConfigurationManager的defaultFrameworkBeanName.     //这里DEFAULT_BEAN_NAME为struts,这是xwork框架的内容,Framework可以是xwork,struts,webwork等     configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);     }     //读取properties信息,默认的default.properties,     init_DefaultProperties(); // [1]     //读取xml配置文件,默认的struts-default.xml,struts-plugin.xml,struts.xml    init_TraditionalXmlConfigurations(); // [2]     //读取用户自定义的struts.properties     init_LegacyStrutsProperties(); // [3]     //自定义的configProviders     init_CustomConfigurationProviders(); // [5]     //载入FilterDispatcher传进来的initParams     init_FilterInitParameters() ; // [6]     //将配置文件中的bean与具体的类映射     init_AliasStandardObjects() ; // [7]     //构建一个用于依赖注射的Container对象     
//在这里面会循环调用上面七个ConfigurationProvider的register方法     
//其中的重点就是DefaultConfiguration的#reload()方法     Container container = init_PreloadConfiguration();     container.inject(this);     init_CheckConfigurationReloading(container);     init_CheckWebLogicWorkaround(container);     if (!dispatcherListeners.isEmpty()) {     for (DispatcherListener l : dispatcherListeners) {     l.dispatcherInitialized(this);     }     }     }   
分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.
将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManager的List里面.
最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.
下面将分析这七层功夫是怎样一步步练成的.

首先是init_DefaultProperties()

private void init_DefaultProperties() {     configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());     
}     
//接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法     
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);     
}  

PropertiesSettings类图:


//PropertiesSettings构造方法       //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写     public PropertiesSettings(String name) {     URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());     if (settingsUrl == null) {     LOG.debug(name + ".properties missing");     settings = new LocatableProperties();     return;     }     settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));     // Load settings     InputStream in = null;     try {     in = settingsUrl.openStream();     settings.load(in);     } catch (IOException e) {     throw new StrutsException("Could not load " + name + ".properties:" + e, e);     } finally {     if(in != null) {     try {     in.close();     } catch(IOException io) {     LOG.warn("Unable to close input stream", io);     }     }     }     }     //loadSettings主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props     //这个props是register的一个入参.     protected void loadSettings(LocatableProperties props, final Settings settings) {     // We are calling the impl methods to get around the single instance of Settings that is expected     for (Iterator i = settings.listImpl(); i.hasNext(); ) {     String name = (String) i.next();     props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));     }     }  

ClassLoaderUtils.getResource( resourceName,  callingClass ): 查找指定资源

    public static URL getResource(String resourceName, Class callingClass) {URL url = null;url = Thread.currentThread().getContextClassLoader().getResource(resourceName);if (url == null) {url = ClassLoaderUtils.class.getClassLoader().getResource(resourceName);}if (url == null) {url = callingClass.getClassLoader().getResource(resourceName);}return url;}
为什么要按照这个顺序?前面的加载为什么可能会失败?

LocatableProperties.load( inputStream ):  将文件内容加载到 LocatableProperties 对象内。

    public void load(InputStream in) throws IOException {Reader reader = new InputStreamReader(in);PropertiesReader pr = new PropertiesReader(reader);while (pr.nextProperty()) {String name = pr.getPropertyName();String val = pr.getPropertyValue();int line = pr.getLineNumber();String desc = convertCommentsToString(pr.getCommentLines());Location loc = new LocationImpl(desc, location.getURI(), line, 0);setProperty(name, val, loc);}}

LocatableProperties.setProperty( name, value, location ):  如何实现一个属性名称对应一个属性值,而且有对应一个Location

LocatableProperties 内部有一个Map<String,Location> propLocations;

Properties 类本身又类似一个Map,这样通过两个Map 来实现内部存储。

    public Object setProperty(String key, String value, Object locationObj) {Object obj = super.setProperty(key, value);if (location != null) {Location loc = LocationUtils.getLocation(locationObj);propLocations.put(key, loc);}return obj;}


的十大书店 

的十大书店 

的十大书店 


再来看第二步:init_TraditionalXmlConfigurations() 

private void init_TraditionalXmlConfigurations() {     //首先读取web.xml中的config初始参数值        //如果没有配置就使用默认的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml",        //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了        //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可      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)) {     //XmlConfigurationProvider负责解析xwork.xml     configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));     } else {     //其它xml都是由StrutsXmlConfigurationProvider来解析     configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));     }     } else {     throw new IllegalArgumentException("Invalid configuration file name");     }     }     
} 

对于其它配置文件只用StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProvider接口。
类XmlConfigurationProvider负责配置文件的读取和解析,
首先通过init()中的loadDocuments(configFileName);利用DomHelper中的
public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) 将configFileName配置文件通过SAX解析方式按照DtdMappings解析成Document对象.
然后通过Provider的register()方法加载"bean"和"constant"属性,再通过loadPackages()加载package及package中的属性
addAction()方法负责读取<action>标签,并将数据保存在ActionConfig中;
addResultTypes()方法负责将<result-type>标签转化为ResultTypeConfig对象;
loadInterceptors()方法负责将<interceptor>标签转化为InterceptorConfi对象;
loadInterceptorStack()方法负责将<interceptor-ref>标签转化为InterceptorStackConfig对象;
loadInterceptorStacks()方法负责将<interceptor-stack>标签转化成InterceptorStackConfig对象。
而上面的方法最终会被addPackage()方法调用,addPackage又会被Provider的loadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。

protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {     PackageConfig.Builder newPackage = buildPackageContext(packageElement);     if (newPackage.isNeedsRefresh()) {     return newPackage.build();     }     // add result types (and default result) to this package     addResultTypes(newPackage, packageElement);     // load the interceptors and interceptor stacks for this package     loadInterceptors(newPackage, packageElement);     // load the default interceptor reference for this package     loadDefaultInterceptorRef(newPackage, packageElement);     // load the default class ref for this package     loadDefaultClassRef(newPackage, packageElement);     // load the global result list for this package     loadGlobalResults(newPackage, packageElement);     // load the global exception handler list for this package     loadGobalExceptionMappings(newPackage, packageElement);     // get actions     NodeList actionList = packageElement.getElementsByTagName("action");     for (int i = 0; i < actionList.getLength(); i++) {     Element actionElement = (Element) actionList.item(i);     addAction(actionElement, newPackage);     }     // load the default action reference for this package     loadDefaultActionRef(newPackage, packageElement);     PackageConfig cfg = newPackage.build();     configuration.addPackageConfig(cfg.getName(), cfg);     return cfg;     }       loadConfigurationFiles解析读取xml中的内容     private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {           ...       
//通过DomHelper调用SAX进行解析xml     
doc = DomHelper.parse(in, dtdMappings);     
...     Element rootElement = doc.getDocumentElement();     NodeList children = rootElement.getChildNodes();     int childSize = children.getLength();     for (int i = 0; i < childSize; i++) {     Node childNode = children.item(i);     if (childNode instanceof Element) {     Element child = (Element) childNode;     final String nodeName = child.getNodeName();     if ("include".equals(nodeName)) {     String includeFileName = child.getAttribute("file");     //解析每个action配置是,对于include文件可以使用通配符*来进行配置        //如Struts.xml中可配置成<include file="actions_*.xml"/>       if (includeFileName.indexOf('*') != -1) {     ClassPathFinder wildcardFinder = new ClassPathFinder();     wildcardFinder.setPattern(includeFileName);     Vector<String> wildcardMatches = wildcardFinder.findMatches();     for (String match : wildcardMatches) {     //递归Load子file中的<include/>     docs.addAll(loadConfigurationFiles(match, child));     }     } else {     docs.addAll(loadConfigurationFiles(includeFileName, child));     }     }     }     }     docs.add(doc);     loadedFileUrls.add(url.toString());     ...     return docs;     }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/497923.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php点击链接代码,php 获取超链接中文本的代码

本文分享的这段代码&#xff0c;使用php正则从超链接中提取文本。例如&#xff0c;可以从Link中&#xff0c;获取文本内容&#xff1a;Link。代码如下&#xff1a;/*** 从超链接中提取文本* param string $url* return string* edit www.jbxue.com*/function getUrlLinkText($u…

谷歌宣布在北京成立AI中国中心:李飞飞和李佳共同领导

来源&#xff1a;澎湃新闻概要&#xff1a;谷歌正式宣布谷歌AI中国中心&#xff08;Google AI China Center&#xff09;在北京成立。谷歌正式宣布谷歌AI中国中心&#xff08;Google AI China Center&#xff09;在北京成立。12月13日&#xff0c;在上海举行的谷歌开发者大会&a…

2008php多版本共存,ECS Windows 2008 IIS如何同时配置多版本的php

ECS Windows 2008 IIS如何同时配置多版本的phpWindows可以通过IIS下安装PHP Manager工具让IIS可以同时支持多版本的php&#xff0c;具体步骤如下所示&#xff1a;1.首先需要下载PHP Manager软件(下载地址&#xff1a;http://phpmanager.codeplex.com/releases/view/69115)&…

Struts2源码阅读(四)_DispatcherConfigurationProvider续

接下来第三步:init_LegacyStrutsProperties()调用的是调用的是LegacyPropertiesConfigurationProvider通过比较前面DefaultPropertiesProvider与调用的是LegacyPropertiesConfigurationProvider.发现DefaultPropertiesProvider继承自后者,但重写了register()方法,主要是生成Pro…

AI 与区块链:两大热门技术,会碰撞出什么样的火花?

来源&#xff1a;36氪概要&#xff1a;区块链和AI可以说是当今最热门的两个技术方向了。区块链和AI可以说是当今最热门的两个技术方向了。在一般人看来&#xff0c;这两大技术似乎没有什么交叉的地方&#xff0c;因为区块链和AI分别属于是技术谱系的两个极端&#xff1a;一个是…

php5.5升级到php5.6,从php5.5.9升级到php5.6之后,相当于php5enmod?

在ubuntu服务器中,我曾经为php5.5.9安装模块,例如带有apt-get的mcrypt,然后使用sudo php5enmod mcrypt启用了它.现在,我已经升级到php5.6,并且错过了一个在Laravel 4.2中与PHPThumb一起使用的模块,该模块需要GID或Imagick来修改图像.所以我做了 &#xff1a;$sudo apt-get inst…

伯克利人工智能研究院最新研究:协作型工业机器人如何更智能?

原文来源&#xff1a;BAIR作者&#xff1a;Changliu Liu、Masayoshi Tomizuka「雷克世界」编译&#xff1a;嗯~阿童木呀、我是卡布达在现代工厂中&#xff0c;工人和机器人是两大主要劳动力。出于安全考虑&#xff0c;这两者通常被限制在金属笼中的机器人分离开来&#xff0c;而…

Struts2源码阅读(五)_FilterDispatcher核心控制器

Dispatcher已经在之前讲过&#xff0c;这就好办了。FilterDispatcher是Struts2的核心控制器&#xff0c;首先看一下init()方法。 public void init(FilterConfig filterConfig) throws ServletException { try { this.filterConfig filterConfig; initLogging(); …

Php如何过360拦截,PHP常见漏洞修复文件-360漏洞修复插件

主要对常见的漏洞进行拦截&#xff0c;如&#xff1a;SQL注入漏洞、检测POST数据、XSS漏洞防护等&#xff0c;效果非常不错&#xff0c;值得拥有。1、下载:360漏洞修复插件2、解压后&#xff0c;上传整个文件夹至服务器根目录3、if(is_file($_SERVER[DOCUMENT_ROOT]./360safe/3…

谷歌开源 TFGAN,让训练和评估 GAN 变得更加简单

作者&#xff1a;思颖概要&#xff1a;训练神经网络的时候&#xff0c;通常需要定义一个损失函数来告诉网络它离目标还有多远。三年前&#xff0c;蒙特利尔大学 Ian Goodfellow 等学者提出「生成式对抗网络」&#xff08;Generative Adversarial Networks&#xff0c;GANs&…

Struts2源码阅读(六)_ActionProxyActionInvocation

下面开始讲一下主菜ActionProxy了.在这之前最好先去了解一下动态Proxy的基本知识.ActionProxy是Action的一个代理类&#xff0c;也就是说Action的调用是通过ActionProxy实现的&#xff0c;其实就是调用了ActionProxy.execute()方法&#xff0c;而该方法又调用了ActionInvocatio…

py语言和php,php和python什么区别

python语言的风格Python在设计上坚持了清晰划一的风格&#xff0c;这使得Python成为一门易读、易维护&#xff0c;并且被大量用户所欢迎的、用途广泛的语言。设计者开发时总的指导思想是&#xff0c;对于一个特定的问题&#xff0c;只要有一种最好的方法来解决就好了。这在由Ti…

计算机产业深度报告:云计算与人工智能开启新一轮技术变革周期

来源&#xff1a;乐晴智库概要&#xff1a;每一次的技术迭代都将行业推向新的高度&#xff0c;同时也对产业生态和企业兴衰产生重大影响。纵观整个IT产业的发展史&#xff0c;从1960年代到现在的2010年代&#xff0c;科技行业历经了大型机时代、小型机时代、PC时代、互联网时代…

自动分页,返回时跳回指定页

实现原理&#xff1a; displaytag 自动分页时&#xff0c;只需要提供一个“集合”(name 属性) 和翻页对应的 requestURI 属性&#xff08;也是返回整体的集合&#xff09; 执行翻页时 displaytag 会自动计算出页数&#xff0c;形如&#xff1a; http://localhost:8080/bpp/ma…

java 界面艺术字,Java 在Word文档中添加艺术字

与普通文字相比&#xff0c;艺术字更加美观有趣也更具有辨识度&#xff0c;常见于一些设计精美的杂志或宣传海报中。我们在日常工作中编辑Word文档时&#xff0c;也可以通过添加艺术字体来凸显文章的重点,美化页面排版。这篇文章将介绍如何使用FreeSpire.Doc for Java在word文档…

AI校招程序员最高薪酬曝光!腾讯80万年薪领跑,还送北京户口

来源&#xff1a;100offer概要&#xff1a;如果说 2016 年是互联网 AI 领域井喷的元年&#xff0c;2017 年整个 AI 领域全面爆发&#xff0c;来潮汹涌的趋势相较 2016 年可以说是有过之而无不及。如果说 2016 年是互联网 AI 领域井喷的元年&#xff0c;2017 年整个 AI 领域全面…

集合对象-“块数据”操作--其实是同一对象引用

例如&#xff1a; Set set1 new HashSet(); set1.add( object1 ); set1.add( ... ); set1.add( objectn ); Set set2 new HashSet( set1 ); 或者 Set set3 new HashSet( set1 ); set3.addAll( set1 ); set2 与 set3 中存储的都是 set1 元素的 “引用” 代码如…

vscode php断点,VSCode中设置断点调试PHP(示例代码)

所需文件xampp 集成服务器(本文使用Apache2.4MySQLPHP7.4.3)vscodeXdebugphp-debug 插件配置Xdebug1. 下载Xdebug插件 (直接去 https://xdebug.org/download.php下载php对应版本的插件)如果不知道如何选取版本&#xff0c;则如下Step 1&#xff1a;获取本地php版本信息 (利用ph…

2017英国AI形势报告:认知鸿沟、新商业模式和当下的挑战

原作 David Kelnar MMC投资研究中心老大Root 编译自 MMC Venture量子位 出品 | 公众号 QbitAI来源&#xff1a;36氪概要&#xff1a;AI技术今年所获得媒体、资本极度的关注&#xff0c;短时间内已经给民众带来认知上剧烈的冲击&#xff1a;或是由未知产生恐惧&#xff0c;或是对…

仓储物流参考资料

一种集成化仓储管理系统研究 http://www.docin.com/p-47000094.html 基于单据流程管理的仓储管理系统的研究 http://articles.e-works.net.cn/BPM/Article65651.htm