前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[享学Netflix] 四、Apache Commons Configuration2.x定位FileLocator和FileHandler

[享学Netflix] 四、Apache Commons Configuration2.x定位FileLocator和FileHandler

作者头像
YourBatman
发布2020-03-18 19:36:56
1.1K0
发布2020-03-18 19:36:56
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦

写好代码是个人素养,不是老板要求,更不是为了秀给同事看 代码下载地址:https://github.com/f641385712/netflix-learning

目录
  • 前言
  • 正文
    • FileBased
    • 文件定位
      • FileLocator
      • FileLocatorUtils(重要)
        • 1.x和2.x定位一个文件的区别
      • FileLocatorAware
      • FileLocationStrategy
        • 1.x和2.x的文件扫描策略
    • FileHandler(重要)
    • 使用示例
  • 总结
    • 声明

前言

上一篇讲述了Commons Configuration2.x它全新的事件-监听基础,一方面体会到了相较于1.x的改动之大,另一方面也能感受到2.x在可扩展性方面是有所增强的。

2.x作为现行的主流版本,因此本文仍着眼于它。本文将要讲述的是它的文件定位系统,其中最重要的两个API是FileLocatorFileHandler,需要引起关注。 第一篇文章就已经介绍了Commons Configuration它的配置来源可以有多种,虽然并不强要求必须来自文件,但其实平时我们99%情况下使用它,内容均来自文件(文件可在工程内、外、系统里、网络上等等),所以文件定位对它的重要性可见一斑。


正文

2.x在和文件相关方面也同样抽象出了很多新的API。文件定位在1.x中,只需ConfigurationUtils#locate( ... )一句代码就搞定,它拥有固定的定位策略,不可更改。

但2.x在这方面做足了功夫,虽然使用起来可能会让你觉得稍显麻烦,但给足了灵活性,这在我们需要热加载的时候,提供了强有力的个性化支撑


FileBased

基于文件,该接口提供基本的读、写的能力。

代码语言:javascript
复制
public interface FileBased {
	void read(Reader in) throws ConfigurationException, IOException;
	void write(Writer out) throws ConfigurationException, IOException;
}

public interface FileBasedConfiguration extends FileBased, Configuration {
}

这个接口有何作用,如下截图一看便知:

在这里插入图片描述
在这里插入图片描述

可以看到所有的实现类均是基于文件的Configuration


文件定位

文件定位之于Commons Configuration有多重要不用再强调,2.x它针对于此也抽象出了一堆新的API,需要我们必须了解。

说明:它的API有的确实让人头疼,设计得有好些不合理的地方,多多担待~


FileLocator

它是用于描述文件位置的类。此类的实例提供了查找和访问文件的信息。可以定义文件位置作为一个URL;它以一种独特的方式标识一个文件。

代码语言:javascript
复制
public final class FileLocator {
	
	// 文件名
	private final String fileName;
	private final String basePath;
	
	// URL
	private final URL sourceURL;
	private final String encoding; // 编码
	
	private final FileSystem fileSystem;
	// 它决定了文件的扫描策略
	private final FileLocationStrategy locationStrategy;

	// 唯一构造器赋值:必须通过自己的内部类Builder
	public FileLocator(final FileLocatorBuilder builder) { ... }
	... // 省略所有的get方法

	// 用于构建FileLocator实例的Builder,是public的
    public static final class FileLocatorBuilder {
    	... // 省略同上一模一样的属性

		// 唯一构造器,并且是Defualt,并且入参是FileLocator,有种你中有我,我中有你的感觉
		// 2.x版本里有大量的这种设计,我觉得这种设计比较差。。。。
		// 此构造器唯一使用地:FileLocatorUtils#FileLocatorUtils()方法
		FileLocatorBuilder(final FileLocator src) {
            if (src != null) {
            	// 这个方法就是把src所有属性赋值给builder,没啥好说的
                initBuilder(src);
            }
		}
	
		... // 省略所有的属性赋值方法

		// 创建一个FileLocator,构造器传入this
        public FileLocator create() {
            return new FileLocator(this);
        }
    }
	
}

此类是不能改变的,因此可以被安全的共享

通过这主要看看它Builder模式的实现,我个人觉得是很繁琐的(这种我中有你,你中有我的设计,个人感觉真的非常难用),使用起来很容易迷糊。 另外,Commons Configuration2.x中的builder模式可能不是你理解中的那种模式,需要适应起来。


FileLocatorUtils(重要)

提供与定位文件相关的辅助方法的实用工具类。 从FileLocatorFileLocatorBuilder的设计上可知:我们若想得到一个FileLocator实例,一定必须借助此工具类。

代码语言:javascript
复制
public final class FileLocatorUtils {

	// 绝大部分情况下,使用默认的文件系统
    public static final FileSystem DEFAULT_FILE_SYSTEM = new DefaultFileSystem();
    // 它是一个CombinedLocationStrategy,顺序决定了扫描顺序
    public static final FileLocationStrategy DEFAULT_LOCATION_STRATEGY = initDefaultLocationStrategy();
    // 这个顺序决定了扫描的顺序,比如次数是先本目录找
    // 再文件系统,在绝对路径,再家目录,**最后当前工程的classpath找**
    private static FileLocationStrategy initDefaultLocationStrategy() {
        final FileLocationStrategy[] subStrategies = new FileLocationStrategy[] {
                        new ProvidedURLLocationStrategy(),
                        new FileSystemLocationStrategy(),
                        new AbsoluteNameLocationStrategy(),
                        new BasePathLocationStrategy(),
                        new HomeDirectoryLocationStrategy(true),
                        new HomeDirectoryLocationStrategy(false),
                        new ClasspathLocationStrategy()
                };
        return new CombinedLocationStrategy(Arrays.asList(subStrategies));
    }

	...
	// 定义这些常量,是方便你使用Map为`FileLocator`的各个属性传值
	// 这写属性为何不public掉?这个库的有些API设计确实渣渣~~~~
	private static final String PROP_BASE_PATH = "basePath";
	private static final String PROP_ENCODING = "encoding";
	private static final String PROP_FILE_NAME = "fileName";
	...
	private static final String PROP_STRATEGY = "locationStrategy";

	// 尝试将指定的URL转换为file对象
	// 其实就是对路径进行容错处理,然后new File(fileName)而已
    public static File fileFromURL(final URL url) {
        return FileUtils.toFile(url);
    }

	// 可以看到想要一个新的fileLocator,可以没有sec的,大不了所有属性均为null喽
    public static FileLocator.FileLocatorBuilder fileLocator() {
        return fileLocator(null);
    }
    public static FileLocator.FileLocatorBuilder fileLocator(final FileLocator src) {
        return new FileLocator.FileLocatorBuilder(src);
    }

	// 使用Map为FileLocator的各个属性赋值,如 map.get(PROP_BASE_PATH)
	public static FileLocator fromMap(final Map<String, ?> map) { ... }
	// 把这个locator所有属性值放进map里
	public static void put(final FileLocator locator, final Map<String, Object> map) { ... }

	// fileName或者sourceUrl有值才算locator定义了位置
	public static boolean isLocationDefined(final FileLocator locator) { ... }
	// fileName basePath souceURL都有值,才返回true
	public static boolean isFullyInitialized(final FileLocator locator) { ... }

	// 此方法确保指向文件的FileLocator以一致的方式设置。不要有些缺胳膊少腿的
	// 让这个locator完整设置 -> 那三个属性都赋上
	// 这里会使用FileLocationStrategy#locate()去把url补充完整哟
	public static FileLocator fullyInitializedLocator(final FileLocator locator) { ... }


	// 请注意:这时候的FileLocator可能是缺胳膊少腿的,哪怕只有一个FileName也能找到你哦
	// 所以这个方法非常重要,还是很靠谱的一个方法,会被经常使用
    public static URL locate(final FileLocator locator) {
        if (locator == null) {
            return null;
        }
        return obtainLocationStrategy(locator).locate(obtainFileSystem(locator), locator);
    }

}

这个工具类是平时编码中使用非常多的,需要掌握常用API。

1.x和2.x定位一个文件的区别
代码语言:javascript
复制
@Test
public void fun2(){
    // 1.x
    URL file1 = ConfigurationUtils.locate("1.properties");
    System.out.println(file1);

    // 2.x
    URL file2 = FileLocatorUtils.locate(FileLocatorUtils.fileLocator().fileName("1.properties").create());
    System.out.println(file2);
}

1.x定位一个文件很easy,但到了2.x很明显麻烦了不少。我也费解,这么常用的功能为何不提取一个更为便捷的API呢???


FileLocatorAware

学过Spring,对Aware接口就一点不陌生了。

代码语言:javascript
复制
public interface FileLocatorAware {
	void initFileLocator(FileLocator locator);
}

initFileLocator代表把描述该文件的东西传递给你,唯一调用处在FileHandler里,帮你注入FileLocator文件,实现了此接口的均为Configuration的子类:

在这里插入图片描述
在这里插入图片描述

FileLocationStrategy

文件定位策略接口。


1.x和2.x的文件扫描策略

1.x是按照固定的顺序去查找、定位文件:

  1. user home
  2. 当前工厂classpath
  3. 系统classpath

而2.x让这变得更加的灵活:允许应用程序自定义文件定位过程,这就是这个接口的作用。它有如下实现类:

在这里插入图片描述
在这里插入图片描述

这些顺序你可以自由组合,甚至还可以自定义。比如:

代码语言:javascript
复制
List<FileLocationStrategy> subs = Arrays.asList(
        new ProvidedURLLocationStrategy(),
        new FileSystemLocationStrategy(),
        new ClasspathLocationStrategy());
FileLocationStrategy strategy = new CombinedLocationStrategy(subs);

这样最终定位strategy 策略翻译如下:

  1. 使用自己的URL定位(比如有完整的URL路径)
  2. 文件系统
  3. 当前工程classpath(比如仅仅只有一个名字)

FileHandler(重要)

管理关联着的FileBased对象的持久化动作的类。该类的实例可用于以方便的方式从不同位置加载和保存实现FileBased的接口的任意对象。

在构造时,就应该把FileBased传入进来。基本上,这个对象被分配关联了一个加载位置保存位置(当然你也可以在load()/save()的时候临时指定路径位置)。

代码语言:javascript
复制
public class FileHandler {

	private final FileBased content;
	// 当前文件的FileLocator
	private final AtomicReference<FileLocator> fileLocator;
	private final List<FileHandlerListener> listeners = new CopyOnWriteArrayList<>();

	// 构造器,为属性赋值
    public FileHandler() {
        this(null);
    }
    // 这时,FileLocator是空的,所有数据均为null
    public FileHandler(final FileBased obj) {
        this(obj, emptyFileLocator());
    }
    // 此处:FileLocator从FileHandler获取出来
    public FileHandler(final FileBased obj, final FileHandler c) {
        this(obj, checkSourceHandler(c).getFileLocator());
    }

	... // 相关get方法,以及对监听器的增删改查方法

	public String getFileName() {
		FileLocator locator = getFileLocator();
		if (locator.getFileName() != null)
			return locator.getFileName();
		if (locator.getSourceURL() != null)
			return FileLocatorUtils.getFileName(locator.getSourceURL());
		return null;
	}
	

	public void setFileName(final String fileName) { ... }
	public void setBasePath(final String basePath) { ... }
	// 它不为null的前提是:fileName或者sourceUrl不为null
	// 依赖的方法是FileLocatorUtils.fileFromURL/FileLocatorUtils.getFile
	public File getFile() {
		return createFile(getFileLocator());
	}

	// =============静态方法,用于快速构建一个FileHandler实例==============
	// 但是请注意:FileBased为null,所以用于构建一个FileLocator,方便构造器传值
	// 不得不再次吐槽:设计的什么垃圾API,构造器直接传FileLocator不香吗?
    public static FileHandler fromMap(final Map<String, ?> map) {
        return new FileHandler(null, FileLocatorUtils.fromMap(map));
    }

	// 判断关联的File是否存在,存在返回true
	public boolean locate() { ... }
	public void clearLocation() { ... .basePath(null).fileName(null).sourceURL(null); ... }
	
	...
	// 从FileLocator里把文件加载进来
	public void load() throws ConfigurationException { ... }
	// 从你指定的这个fileName去加载,然后填充到FileBased里面去
	public void load(final String fileName) throws ConfigurationException { ... }
	... // 省略其它重载方法

	 // 把FileBased内容写到关联的File里面去
	 public void save() throws ConfigurationException { ... }
	 public void save(final String fileName) throws ConfigurationException { ... }
	 ...
	
	// 在以上load()和save过程中,会触发监听器的各种事件,从而外部便可以监听到这些动作
}

FileHandler总结起来,起的作用是:对FileBased(肯定是一个Configaration实例)和FileLocator绑定上一个关系,然后方便的做load/save读写操作。


使用示例

不使用Configurations,把一个文件的内容读取到PropertiesConfiguration里来。

代码语言:javascript
复制
@Test
public void fun3() throws ConfigurationException {
    // Configurations configs = new Configurations();
    // FileLocator fileLocator = FileLocatorUtils.fileLocator().fileName("1.properties").create();
    // fileLocator = FileLocatorUtils.fullyInitializedLocator(fileLocator);
    // PropertiesConfiguration config = configs.properties(fileLocator.getSourceURL());

    PropertiesConfiguration config = new PropertiesConfiguration();

    // 把config和文件关联上
    Map<String, Object> map = new HashMap<>();
    map.put("fileName", "1.properties");
    FileHandler fileHandler = new FileHandler(config, FileHandler.fromMap(map));

    ConfigurationUtils.dump(config, System.out);
    System.out.println("\n==============上为load之前==============");
    fileHandler.load(); // 通过fileHandler也可以给Configuration赋值
    ConfigurationUtils.dump(config, System.out);
    System.out.println("\n==============上为load之后==============");
    fileHandler.load();
    ConfigurationUtils.dump(config, System.out);
    System.out.println("\n==============上为再load一次的结果==============");

    config.clear(); // 会发送ConfigurationEvent.CLEAR事件哦
    fileHandler.load();
    ConfigurationUtils.dump(config, System.out);
    System.out.println("\n==============上为clear清空后再load的结果==============");
}

结果打印:

代码语言:javascript
复制
==============上为load之前==============
common.name=YourBatman
common.age=18
common.addr=China
common.count=4
common.fullname=${common.name}-Bryant
java.version=1.8.123
==============上为load之后==============
common.name=[YourBatman, YourBatman]
common.age=[18, 18]
common.addr=[China, China]
common.count=[4, 4]
common.fullname=[${common.name}-Bryant, ${common.name}-Bryant]
java.version=[1.8.123, 1.8.123]
==============上为再load一次的结果==============
common.name=YourBatman
common.age=18
common.addr=China
common.count=4
common.fullname=${common.name}-Bryant
java.version=1.8.123
==============上为clear清空后再load的结果==============

说明:至于为何${}这种占位符打印的时候没有被解析,请参见第二篇文章,有详细解释

从结果中能得出如下几个结论:

  1. 不通过Configurations,也可以向xxxConfiguration(基于文件的FileBased实例)里写数据,并且还可以无限写
  2. 读到的新内容,仍旧附加在原来的基础上(写也是增量的写…)
  3. 若想重新读取,你可以先执行clear()后再去读一份最新的
  4. 对于load()和save等每一步操作,都会发送对应的事件:FileHandlerListener(它的内置实现仅有AutoSaveListener
    1. 该监听器没啥好说的,监听FileHandler的几个方法:loading、loaded、saving、saved、locationChanged代表中各种文件状态~

总结

关于Apache Commons Configuration2.x版本的文件定位系统就介绍到这了,相较于1.x它完全弱化了使用者对定位逻辑的认识(屏蔽掉了实现细节),2.x在这方面做了大量的文章,使得使用者甚至都可以定制FileLocationStrategy这种策略,当然牺牲的就是使用上的便捷性以及提高了理解上的难度,万事没有银弹,权衡才是关键。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 前言
  • 正文
    • FileBased
      • 文件定位
        • FileLocator
        • FileLocatorUtils(重要)
        • FileLocatorAware
        • FileLocationStrategy
      • FileHandler(重要)
        • 使用示例
        • 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档