一.Configuration介绍
二.Configuration分析
Configuration是一个配置信息类,它包含配置信息的加载,获取配置信息和加载配置信息等。类加载的过程是,先加载该类的静态代码块,再加载静态变量,然后才是构造方法。所以,我在分析的时候,先看静态代码块,然后再看构造方法,最后根据该类的主要功能去分析。
1.加载静态代码块
static{
//print deprecation warning if hadoop-site.xml is found in classpath
ClassLoader cL = Thread.currentThread().getContextClassLoader();
if (cL == null) {
cL = Configuration.class.getClassLoader();
}
if(cL.getResource("hadoop-site.xml")!=null) {
LOG.warn("DEPRECATED: hadoop-site.xml found in the classpath. " +
"Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, "
+ "mapred-site.xml and hdfs-site.xml to override properties of " +
"core-default.xml, mapred-default.xml and hdfs-default.xml " +
"respectively");
}
addDefaultResource("core-default.xml");
addDefaultResource("core-site.xml");
}
在静态代码块中,我们看到Configuration静态代码块加载的时候,加载了默认资源"core-default.xml"和"core-site.xml"。我们看addDefaultResource(...)方法。
public static synchronized void addDefaultResource(String name) {
if(!defaultResources.contains(name)) {
defaultResources.add(name);
for(Configuration conf : REGISTRY.keySet()) {
if(conf.loadDefaults) {
conf.reloadConfiguration();
}
}
}
}
这个方法中将配置文件名添加到集合defaultResources中。其中loadDefaults是一个flag,标示是否要加载默认配置文件(默认true)。然后将集合REGISTORY中的Configuration对象(只要该对象加载默认配置)进行reloadConfiguration(),即:重新加载配置文件。这里出现了一个集合REGISTORY,我们下面遇到再说。先看reloadConfiguration()方法。
public synchronized void reloadConfiguration() {
properties = null; // trigger reload
finalParameters.clear(); // clear site-limits
}
这个方法实际上是将Configuration的properties属性清空,同时,将集合finalParameters(该集合里存放了final修饰的变量)清空。
总之,加载静态代码块的结果是:将"core-default.xml"和"core-site.xml"文件名添加到defaultResources中,然后清空了properties属性和finalParameters集合。
2.构造方法
/** A new configuration. */
public Configuration() {
this(true);
}
/** A new configuration where the behavior of reading from the default
* resources can be turned off.
*
* If the parameter {@code loadDefaults} is false, the new instance
* will not load resources from the default files.
* @param loadDefaults specifies whether to load from the default files
*/
public Configuration(boolean loadDefaults) {
this.loadDefaults = loadDefaults;
updatingResource = new ConcurrentHashMap();
synchronized(Configuration.class) {
REGISTRY.put(this, null);
}
}
/**
* 克隆其他configuration的配置
*/
@SuppressWarnings("unchecked")
public Configuration(Configuration other) {
this.resources = (ArrayList) other.resources.clone();
synchronized(other) {
if (other.properties != null) {
this.properties = (Properties)other.properties.clone();
}
if (other.overlay!=null) {
this.overlay = (Properties)other.overlay.clone();
}
this.updatingResource = new ConcurrentHashMap(
other.updatingResource);
this.finalParameters = Collections.newSetFromMap(
new ConcurrentHashMap());
this.finalParameters.addAll(other.finalParameters);
}
synchronized(Configuration.class) {
REGISTRY.put(this, null);
}
this.classLoader = other.classLoader;
this.loadDefaults = other.loadDefaults;
setQuietMode(other.getQuietMode());
}
我们可以看到前两个构造方法是典型的折叠式构造方式,第一个无参构造中调用了第二个构造方法。在第二个构造方法中设置了loadDefaults为true,将Configuration实例本身放到REGISTRY集合中注册。第三个构造方法是,克隆其他Configuration的配置信息。
3. 获取配置信息
我们在outline中看到了很多getType(...)方法。我们来分析一下运行过程,以getInt为例:
public int getInt(String name, int defaultValue) {
String valueString = getTrimmed(name);
if (valueString == null)
return defaultValue;
String hexString = getHexDigits(valueString);
if (hexString != null) {
return Integer.parseInt(hexString, 16);
}
return Integer.parseInt(valueString);
}
public String getTrimmed(String name) {
String value = get(name);
if (null == value) {
return null;
} else {
return value.trim();
}
}
public String get(String name) {
String[] names = handleDeprecation(deprecationContext.get(), name);
String result = null;
for(String n : names) {
result = substituteVars(getProps().getProperty(n));
}
return result;
}
protected synchronized Properties getProps() {
if (properties == null) {
properties = new Properties();
Map backup =
new ConcurrentHashMap(updatingResource);
loadResources(properties, resources, quietmode);
if (overlay != null) {
properties.putAll(overlay);
for (Map.Entry
我们看到getInt --> getTrimmed --> get --> getProps,这是所有getType()方法调用要走的路径,最终调用到getProps()方法。在该方法中,如果properties为空,我们就要执行loadResources(...)加载资源,否则直接返回properties属性即可。
接下来我们看loadResources(...)方法。在该方法中,我们上文提到的loadDefaults就起作用了。如果loadDefaults=true,则加载集合defaultResources中的默认配置文件和"hadoop-site.xml"配置文件。否则,只加载集合resources中的配置文件。
4.加载资源的方式
从上面可以看到一个是default resource配置文件的list,另一个是configuration resources配置文件的list。我们要看看Configuration类是如何加载配置文件的。首先,我们在静态代码块中看到了addDefaultResources(...)方法,另外还有addResources(...)的多个重载方法。最终addResources()方法调用的是addResourceObject(...)方法,所以我们看addResourceObject(...)和addDefaultResources(...)两个方法就可以。
public void addResource(URL url) {
addResourceObject(new Resource(url));
}
private synchronized void addResourceObject(Resource resource) {
resources.add(resource); // add to resources
reloadConfiguration();
}
public synchronized void reloadConfiguration() {
properties = null; // trigger reload
finalParameters.clear(); // clear site-limits
}
public static synchronized void addDefaultResource(String name) {
if(!defaultResources.contains(name)) {
defaultResources.add(name);
for(Configuration conf : REGISTRY.keySet()) {
if(conf.loadDefaults) {
conf.reloadConfiguration();
}
}
}
}
加载资源的过程,就是分别调用addDefaultResources(...)方法将默认配置文件名添加到defaultResources集合、调用addResourceObject(...)方法将配置文件加如到resources集合中。同时,清空finalParameters并重置properties=null。
5.设置配置信息
通过outline可以看到很多setType(...)方法,我们以setInt()为例:
public void setInt(String name, int value) {
set(name, Integer.toString(value));
}
public void set(String name, String value) {
set(name, value, null);
}
public void set(String name, String value, String source) {
Preconditions.checkArgument(
name != null,
"Property name must not be null");
Preconditions.checkArgument(
value != null,
"The value of property " + name + " must not be null");
name = name.trim();
DeprecationContext deprecations = deprecationContext.get();
if (deprecations.getDeprecatedKeyMap().isEmpty()) {
getProps();
}
getOverlay().setProperty(name, value);
getProps().setProperty(name, value);
String newSource = (source == null ? "programatically" : source);
if (!isDeprecated(name)) {
updatingResource.put(name, new String[] {newSource});
String[] altNames = getAlternativeNames(name);
if(altNames != null) {
for(String n: altNames) {
if(!n.equals(name)) {
getOverlay().setProperty(n, value);
getProps().setProperty(n, value);
updatingResource.put(n, new String[] {newSource});
}
}
}
}
else {
String[] names = handleDeprecation(deprecationContext.get(), name);
String altSource = "because " + name + " is deprecated";
for(String n : names) {
getOverlay().setProperty(n, value);
getProps().setProperty(n, value);
updatingResource.put(n, new String[] {altSource});
}
}
}
setType --> set --> set,最终就是把属性项和值添加到properties变量和overlay变量中。overlay是用来覆盖properties的,因为每次加载配置文件的过程,都要重置properties=null。overlay可以看着properties的一种备份。