# Tuz
**Repository Path**: FishGoddess/Tuz
## Basic Information
- **Project Name**: Tuz
- **Description**: 统一轻量级资源(配置、对象、IOC、AOP)透明化容器
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: v1.0-rebuild
- **Homepage**: http://fishgoddess.cn/tuz/
- **GVP Project**: No
## Statistics
- **Stars**: 42
- **Forks**: 7
- **Created**: 2019-03-28
- **Last Updated**: 2024-10-22
## Categories & Tags
**Categories**: ioc-framework
**Tags**: None
## README
# Tuz 轻量级资源容器 [](https://mvnrepository.com/artifact/cn.com.fishin/Tuz) [](https://www.apache.org/licenses/LICENSE-2.0.html)
*注意:*
v1.0 和 0.8.0-FINAL 及以下的版本不兼容!!! API 有了一些调整,删减了累赘的功能,调整了类架构设计,
请使用 v1.0 及以上版本!!!
+ 资源属性管理容器: 将资源以键值对的形式加载到内存,实现资源的轻松加载使用
+ 资源透明化容器:同样的使用方式,不一样的加载器,可以达到资源使用的透明化
+ 轻量依赖注入容器: 将对象实现类直接注入到对象引用中,实现业务的实现解耦
+ 对象方法拦截容器: 使用动态代理进行方法的拦截器,实现业务的主次解耦
+ IO 操作工具箱: 提供了常用的 IO 操作方法,操作 IO 更方便
## 介绍 -- Introduce
Tuz 轻量级资源管理器,用于管理您的资源,
比如 .properties 和 JSON 文件,一次加载,随处可用,并且使用非常简单!
同时还支持 Redis 资源加载以及操作,在资源使用上完全透明化!
不仅如此,您还可以轻松扩展功能,只需要简单地实现一些接口即可!
内置的依赖注入插件可以让你轻松将接口和实现类解耦,彻底进行优雅的开发!
后续会加入更多资源管理的功能或插件,敬请期待!
Tuz, a small resource manager.
For example, you could load some .properties files, and use them everywhere!
You could extend it by implementing some interface!
Also, I have prepared some building plgins like DiPlugin.
It will be perfect as time going by!
---
+ #### Tuz 官网:[http://www.fishgoddess.cn/tuz/](http://www.fishgoddess.cn/tuz/)
+ #### 联系方式:fishgoddess@qq.com
+ #### 开源协议:[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html)
---
## 如何使用 -- How to use
+ 如果您正在使用 Maven 工程,那么这一切都变得很简单了,你只需要在你的 pom.xml 中引入:
+ If you are using Maven project, then everything becomes easier, you only need add some lines in your pom.xml:
```xml
cn.com.fishin
Tuz
1.0-FINAL
```
---
+ 当然,你也可能使用的是 Gradle 工程,这样你只需要在相应的构建管理配置文件里面加入:
+ Also, you may use a Gradle project, do the same thing as a Maven project:
```groovy
// https://mvnrepository.com/artifact/cn.com.fishin/Tuz
compile group: 'cn.com.fishin', name: 'Tuz', version: '1.0-FINAL'
```
其他构建工具或者仓库请参考 `Maven 中心仓库`的写法,如果您不知道网址是啥,请点击
上方的 `Maven Central` 图标或者点击 [这个链接](https://mvnrepository.com/artifact/cn.com.fishin/Tuz)。
See more info in [this website](https://mvnrepository.com/artifact/cn.com.fishin/Tuz).
---
+ 或者,您使用的是普通 Java 项目,需要手动引入 lib 框架。
我们建议您迁移到 Maven 工程,如果不想切换,
可以到 [Tuz - GitHub](https://github.com/FishGoddess/Tuz/releases) 下载你需要版本的 Jar 包文件以及文档和源码。
+ 我们提供了两种 jar 可供使用,一种是不带任何依赖的,一种是带了完整依赖的,请选择一种下载。
+ Or worse, you are using a traditional Java project with adding Jar manually,
then you should change your developing way or download a Jar from [Tuz - GitHub](https://github.com/FishGoddess/Tuz/releases).
+ We provide two versions of jar, one is small but not completed, one is completed but not small, you should choose one of them.
---
## 参考例子 -- Example
#### 1. 简单的资源管理,这里主要使用 .properties 文件
```java
public class TuzSimpleDemo {
public static void main(String[] args) throws Throwable {
// 加载资源文件
// API: load(Loadable resource) throws IOException
// test 是命名空间,后面一个是资源加载器
// "test.properties" 文件中有一个属性:number=16
Tuz tuz = Tuz.instance();
tuz.load(new ClasspathPropertiesLoader("test.properties", "test"));
// 当然,你也可以不指定命名空间,内部会自动生成一个命名空间
// 不过,为了性能和正确性,还是建议您使用自定义的命名空间
// 当你不指定命名空间时,就会使用文件名作为命名空间
//Tuz.load(new ClasspathPropertiesLoader("test.properties"));
//String number = Tuz.use("number", "test.properties");
// 或者,您也可以使用另外一个加载器去加载文件系统中的资源文件
//Tuz.load(new FileSystemPropertiesLoader("Z:/test.properties", "test"));
// 下面就是激动人心的时刻了!您可以在任意地方使用您的资源!
// 上面提到,初始化的资源文件中有一个属性:number=16
// 您可以在任意地方使用这个资源,像这样:
// API: use(String key, String namespace)
// 其中上面的代码中 number 是资源名字,test 是命名空间
String number = tuz.use("number", "test"); // ===> 返回 16
System.out.println(number);
// 同样,您可以不指定命名空间,但是这不被推荐
// 具体原因请看 cn.com.fishin.tuz.core.Tuz.use(java.lang.String)
// 关于用不用命名空间,完全取决于你对运行效率和开发效率的权衡
//String number = Tuz.use("number"); // ===> 返回 16
// 如果你需要更新重载这个资源文件,就调用这个方法
// 注意这里的 new ClasspathPropertiesLoader("test.properties", "test") 应该要和上面 load 的一致
//Tuz.reLoad(new ClasspathPropertiesLoader("test.properties", "test"));
// 如果你不再需要这个资源文件,就调用这个方法
// 注意这里的 new ClasspathPropertiesLoader("test.properties", "test") 应该要和上面 load 的一致
//Tuz.unLoad(new ClasspathPropertiesLoader("test.properties", "test"));
}
}
```
#### 2. 简单的依赖注入管理
```java
// 参考 test 文件夹下的 cn.com.fishin.demo.TuzSimpleDemo
public class TuzSimpleDemo2 {
public static void main(String[] args) throws Throwable {
// 传统的方式使用接口:
//xxxService service = new xxxServiceImpl();
// 这种方式并没有真正解耦,使用 Spring 这类框架可以达到解耦效果,但是需要引入大量框架
// 而使用 Tuz 可以轻松做到解耦,请看下面:
Tuz tuz = Tuz.instance();
tuz.load(new ClasspathPropertiesLoader("test.properties", "test"));
// 直接获取实现类,而不用注入实现类的细节
xxxService service1 = DiPlugin.useInstance("xxxService", "test", xxxService.class);
service1.say("Hello, tuz!");
// 你也许会觉得上面的方法有些参数多余了
// 对没错,就是 xxxService 和 xxxService.class
// 当我的 key 和类的名字一样的时候,其实这个 key 值是可以省略的
// 注意这里的 xxxService 在配置文件中的 key 就是 xxxService
xxxService service2 = DiPlugin.useInstance(xxxService.class);
service2.say("Hello, tuz!");
// 同样的,你可以不指定命名空间,但是,这样会导致多一次的查找!
//tuz.load(new ClasspathPropertiesLoader("test.properties"));
//xxxService service3 = DiPlugin.useInstance("xxxService", xxxService.class);
//service3.say("Hello, Tuz!");
}
}
```
#### 3. 简单的资源管理,这里主要使用 JSON 文件
```java
public class TuzSimpleDemo3 {
public static void main(String[] args) throws Throwable {
// 加载这个 JSON 文件
// 加载一次之后,你就可以在全局中随意使用啦
Tuz tuz = Tuz.instance();
tuz.load(new ClasspathJSONLoader("test.json"));
// 建议给这个文件起一个命名空间,如果没有手动指定,Tuz 在内部会使用文件名作为命名空间
// Tuz.load(new ClasspathJSONLoader("test.json", "test"));
// 当然,除了加载类路径下的资源文件,还可以加载文件系统中的资源
//Tuz.load(new FileSystemJSONLoader("E:\\JavaProject\\Tuz\\src\\test\\resources\\test.json"));
// 由于默认使用 UTF8 的字符编码,所以如果你的文件不是 UTF8 编码,就会出现乱码问题
// 这时候需要自己手动指定这个文件的字符编码
//Tuz.load(new ClasspathJSONLoader("test.json", StandardCharsets.ISO_8859_1));
/*
{
"status": 0,
"message": "success",
"ok": true,
"data": {
"title": {
"id": "001",
"name": "test",
"null": {}
},
"content": [
{
"id": "001",
"value": "hello 001",
"arr": [1, "非你莫属尽快发你说的", true]
},
{
"id": "002",
"value": "hello 002"
}
]
}
}
*/
// 如果你需要引用这个 JSON 中的值,就像下面这样引用即可
// 是不是很方便?哈哈哈 ^_^
System.out.println(tuz.use("status")); // ===> 0
System.out.println(tuz.use("message")); // ===> success
System.out.println(tuz.use("ok")); // ===> true
System.out.println(tuz.use("data")); // ===> {"title":{"null":{},"name":"test","id":"001"},"content":[{"arr":[1,"非你莫属尽快发你说的",true],"id":"001","value":"hello 001"},{"id":"002","value":"hello 002"}]}
System.out.println(tuz.use("data.title")); // ===> {"null":{},"name":"test","id":"001"}
System.out.println(tuz.use("data.title.id")); // ===> 001
System.out.println(tuz.use("data.title.name")); // ===> test
System.out.println(tuz.use("data.title.null")); // ===> {}
System.out.println(tuz.use("data.content")); // ===> [{"arr":[1,"非你莫属尽快发你说的",true],"id":"001","value":"hello 001"},{"id":"002","value":"hello 002"}]
System.out.println(tuz.use("data.content[0].id")); // ===> 001
System.out.println(tuz.use("data.content[0].value")); // ===> hello 001
System.out.println(tuz.use("data.content[0].arr")); // ===> [1,"bfdjhsb",true]
System.out.println(tuz.use("data.content[0].arr[0]")); // ===> 1
System.out.println(tuz.use("data.content[0].arr[1]")); // ===> bfdjhsb
System.out.println(tuz.use("data.content[0].arr[2]")); // ===> true
System.out.println(tuz.use("data.content[1].id")); // ===> 002
System.out.println(tuz.use("data.content[1].value")); // ===> hello 002
// 当然,如果你有多个 JSON 文件都有同样的键值,就需要加上命名空间了
// 这里的 test 就是命名空间,在调用 tuz.load 时就需要指定
//tuz.load(new ClasspathJSONLoader("test.json", "test"));
//System.out.println(tuz.use("status", "test")); // ===> 0
// 当然,如果你是在程序中使用到了一个 JSON 字符串,也可以使用 AbstractJSONLoader 加载使用
final String jsonString = "{\"title\":{\"null\":{},\"name\":\"test\",\"id\":\"001\"},\"content\":[{\"arr\":[1,\"非你莫属尽快发你说的\",true],\"id\":\"001\",\"value\":\"hello 001\"},{\"id\":\"002\",\"value\":\"hello 002\"}]}";
tuz.load(new AbstractJSONLoader(jsonString) {
@Override
public String namespace() {
return "program";
}
});
System.out.println(tuz.use("content[0].arr")); // ===> [1,"非你莫属尽快发你说的",true]
}
}
```
#### 4. 配置自定义的设置
```java
// 参考 test 文件夹下的 cn.com.fishin.tuz.demo.TuzConfigDemo
public class TuzConfigDemo {
public static void main(String[] args) throws Throwable {
// 我们先以 cn.com.fishin.tuz.demo.TuzSimpleDemo2 作为切入点
// 先看原本的例子:
Tuz tuz = Tuz.instance();
tuz.load(new ClasspathPropertiesLoader("test.properties"));
//xxxService service = DiPlugin.useInstance(xxxService.class);
//service.say("Hello, Tuz!");
// 默认情况下,创建的对象是单例模式的
// 参考 cn.com.fishin.tuz.core.TuzConfig
xxxService service1 = DiPlugin.useInstance(xxxService.class);
xxxService service2 = DiPlugin.useInstance(xxxService.class);
System.out.println(service1 == service2); // 返回 ===> true
// 由于 Tuz 有一个默认的配置,里面有一个属性
// 获取类的实例形式,默认是 true,也就是单例模式
//private boolean singleton = true;
// 你可以直接设置 Tuz 中的默认配置,但是不被推荐
tuz.getConfig().setSingleton(false);
// 这样获得的对象就是多例模式的
xxxService service3 = DiPlugin.useInstance(xxxService.class);
xxxService service4 = DiPlugin.useInstance(xxxService.class);
System.out.println(service3 == service4); // 返回 ===> false
// 上面说过,你可以直接设置 Tuz 中的默认配置,但是不被推荐
// 正确的做法是新创建一个配置对象
TuzConfig newConfig = new TuzConfig();
newConfig.setSingleton(true); // 设置为单例模式
// 设置配置
tuz.setConfig(newConfig);
// 这样获得的对象又是单例模式啦!
xxxService service5 = DiPlugin.useInstance(xxxService.class);
xxxService service6 = DiPlugin.useInstance(xxxService.class);
System.out.println(service5 == service6); // 返回 ===> true
}
}
```
#### 5. 配合 Spring/SpringBoot 来使用
```java
//@SpringBootApplication
public class TuzSpringBootDemo {
/*
* 这个类仅仅是演示与 SpringBoot 一起使用的例子,并不代表这是唯一用法
*
* 曾经,在使用 Spring 的时候,配置文件可以做到不改代码更改实现类的功能,
* 后来推出了 SpringBoot,的确简化了大量的配置文件操作,而且对于微服务来说,
* 更改实现类往往意味着服务的改变,所以当发生需要更改需求而更改代码的事情的时候,
* 我们往往可以理解和接受,而且就算是要做到从 .properties 配置文件中读取配置
* 从而达到不更改代码更改实现类的目的也不难,毕竟 Spring 读取配置文件已经很简单,
* 但是这些业务其实是重复而且有意义的,这时候 Tuz 就体现出很好的复用和业务性了
* */
// 你可以选择使用 static 块初始化 Tuz
// 当然你也可以选择在 main 方法中初始化
static {
try {
Tuz tuz = Tuz.instance();
tuz.load(new ClasspathPropertiesLoader("test.properties", "test"));
} catch (Throwable t) {
// Do something...
System.err.println(t.getMessage());
}
}
public static void main(String[] args) {
// 除了在 static {} 中初始化,你也可以在这里初始化,甚至可以配合参数 args
// 做到选择不同的环境去初始化 Tuz
//Tuz.load(new ClasspathPropertiesLoader("test.properties", "test"));
// 这是 SpringBoot 的用法,这里不作讨论
//SpringApplication.run(TuzSpringBootDemo.class, args);
}
// Spring 初始化 Bean
//@Bean
public xxxService getXxxService() {
// 直接返回 xxxService 即可
// 这样,你只需在 test.properties 中更改这个类的实现即可
// 不需要更改代码,也不需要写多余的代码
return DiPlugin.useInstance(xxxService.class);
}
}
```
#### 6. 使用拦截器拦截方法达到业务主次解耦
```java
public class ProxySimpleDemo {
/*
// 在 test 包中有使用这个 ProxyFactory.wrap 方法的案例
// 但是不建议直接使用这个方法,因为这个方法更多是一个工厂方法,提供内部使用
SimpleClass simpleClass = new SimpleClass();
SimpleClass simpleClass1 = (SimpleClass) ProxyFactory.wrap(simpleClass,
new InterceptorInvocationHandler(simpleClass, new Interceptor[]{
new TestInterceptor(),
new TestInterceptor(),
new TestInterceptor()
}));
simpleClass1.test();
*/
public static void main(String[] args) throws Throwable {
// 同样的,先加载资源文件,这样可以获取到具体的实现类
Tuz tuz = Tuz.instance();
tuz.load(new ClasspathPropertiesLoader("test.properties", "test"));
// 直接获取实现类,而不用注入实现类的细节
xxxService service = DiPlugin.useInstance(xxxService.class);
// 以上演示了最单纯的使用依赖注入的方法
// 但是这时候你来了新需求,需要在原本的基础之上加入两个需求:
// 1. 现在的系统加入了 redis 当缓存系统,在调用方法之前先在缓存系统找
// 找不到在从数据库找,并且将数据加载到缓存系统
// 2. 需要在业务方法上都加入日志记录功能
// 以上两点都是次要业务,不建议直接写入到当前已存在的业务中或者你无法改动现有代码
// 这时候你就需要使用面向切面的思想去更新你的业务类
// Tuz 中使用拦截器来达到这个目的
// 顶级拦截器接口:cn.com.fishin.tuz.interceptor.Interceptor
// 但是不建议直接使用这个拦截器,建议使用 cn.com.fishin.tuz.interceptor.DefaultInterceptor
// 然后选择性地复写特定方法来达到特定业务功能
// 不推荐直接使用这个工厂类,建议使用 cn.com.fishin.tuz.plugin.ProxyPlugin 中的方法来获得代理之后的对象
/*
xxxService xxxService = ProxyPlugin.useInstance(xxxService.class,
new Interceptor[]{
new CacheInterceptor(),
new LogInterceptor()
});
*/
// 注意:当这个类可以被继承时,使用 CGlib 来实现动态代理
// 如果不可以被继承,就使用 JDK 来动态代理,这时要求该类必须实现接口,而且使用接口来接收
xxxService serviceProxy = ProxyPlugin.useInstance(xxxService.class, new Interceptor[]{
new CacheInterceptor(), // 缓存拦截器
new LogInterceptor(), // 日志拦截器
});
// 这是一个对比
System.out.println("====================== 使用拦截器之前的业务 ===================");
service.say("没用拦截器...");
System.out.println("====================== 使用拦截器之前的业务 ===================");
System.out.println("====================== 使用拦截器之后的业务 ===================");
serviceProxy.say("使用拦截器...");
System.out.println("====================== 使用拦截器之后的业务 ===================");
// 这是第二个对比
System.out.println("====================== 使用拦截器之前的业务 ===================");
service.hung(18, 16);
System.out.println("====================== 使用拦截器之前的业务 ===================");
System.out.println("====================== 使用拦截器之后的业务 ===================");
serviceProxy.hung(81, 61);
System.out.println("====================== 使用拦截器之后的业务 ===================");
/*
// 执行结果:
====================== 使用拦截器之前的业务 ===================
没用拦截器...
====================== 使用拦截器之前的业务 ===================
====================== 使用拦截器之后的业务 ===================
日志记录正常操作 ===> public void cn.com.fishin.tuz.demo.xxxServiceImpl.say(java.lang.String)
在缓存中查找数据:[Ljava.lang.Object;@29774679
缓存中找不到数据!
使用拦截器...
加载数据 null 到缓存...
====================== 使用拦截器之后的业务 ===================
====================== 使用拦截器之前的业务 ===================
18 hung 16
====================== 使用拦截器之前的业务 ===================
====================== 使用拦截器之后的业务 ===================
日志记录正常操作 ===> public void cn.com.fishin.tuz.demo.xxxServiceImpl.hung(int,int)
在缓存中查找数据:[Ljava.lang.Object;@26653222
缓存中找到了数据!
====================== 使用拦截器之后的业务 ===================
*/
// 需要注意的是,由于使用的是 CGlib,所以被代理的类不能是 final 修饰的
// 也就是说必须要可以被继承,因为 CGlib 就是使用 ASM 产生子类和多态来达到动态代理的效果的
// 另外,动态代理产生的实例是不是单例的和 Tuz 的配置有关
// 详情请参考 cn.com.fishin.tuz.core.TuzConfig.isSingleton
// 默认情况下是单例模式的,也就是说不管你创建多少代理对象都是同一个
// 如果需要多例的模式,可以修改 Tuz 的配置,调用 cn.com.fishin.tuz.core.TuzConfig.setSingleton
}
}
```
#### 7. Redis 资源加载器
```java
public class TuzSimpleDemo4 {
public static void main(String[] args) throws Throwable {
// 使用 RedisLoader 来加载 Redis 上的资源
// 注意这个加载操作其实并不是真的加载
// 而只是创建一个连接池,每一次获取数据都从网络中获取
// 现在加入了缓存,将得到的值暂时缓存,可以大幅度提高短时间大量读取的性能
final String namespace = "192.168.32.131:6379";
Tuz tuz = Tuz.instance();
// 注意:
// 这里的 JedisRedisConnection 是需要您去实现的,由于每个人所使用的 redis 客户端不一样
// 所以这里没有去使用特定的一种客户端,让用户自己去实现,其实实现起来也很简单,就只需要实现特定的几个方法,
// 可以参考这里例子的 JedisRedisConnection 类
Loader redisLoader = new RedisLoader(namespace,
new JedisRedisConnection(new Jedis("192.168.32.131", 6379)));
tuz.load(redisLoader);
// 下面你就可以像使用普通资源一样使用 redis 上的资源了
// 你可以向资源池加入新资源
// 也就是向 redis 中存储数据,比如你可以添加一对数据
// 注意:这里指定命名空间为 192.168.32.129:6379
tuz.appendResource(redisLoader, "key", "哈哈哈");
// 当然,你可能想添加一堆数据
// 这里可以使用批量添加,内部使用 pipeline 管道来批量新增,
// 所以不用担心批量新增会造成大量网络堵塞
tuz.appendResource(redisLoader, manyEntries());
// 当你确保已经存在数据时,就可以正常使用了
System.out.println("key ===> " + tuz.use("key"));
System.out.println("key1 ===> " + tuz.use("key1"));
System.out.println("key2 ===> " + tuz.use("key2"));
// 如果你已经不需要使用这个资源库,就可以考虑卸载这个资源库了
tuz.unLoad(redisLoader);
System.out.println("key ===> " + tuz.use("key"));
}
// 准备一堆数据
private static Map manyEntries() {
Map entries = new HashMap<>(8);
for (int i = 0; i < 4; i++) {
entries.put("key" + i, "value" + i);
}
return entries;
}
}
```
#### 8. 更多用法开发中,你也可以轻松定制自己的插件
内置插件位于 `cn.com.fishin.tuz.plugin` 包下,常用的有 `DiPlugin` 依赖注入插件
您可以参考这个插件的实现来定制自己的插件!
你可以自定义自己的加载器,只需要简单实现 `cn.com.fishin.tuz.core.Loader` 接口即可
更多使用演示案例,请参见 test 模块下的 cn.com.fishin.tuz.demo
## 友情链接 -- as a friend
+ [王老板的奇思妙想](https://gitee.com/HappyChicken)
+ [黄少爷的财大气粗](https://gitee.com/Mackyhuang)
+ [软大师的吊儿郎当](https://gitee.com/mdaovo)
## 更新日志 -- update log
#### *2019-9-13:*
1. 进行大幅度地简化和重构!!!
2. 移除 FTP 上传的功能,这个功能非常累赘,因为它,我都不想用这个库了
3. 将 Tuz 设置为不允许继承的,删除了大量无用的方法
4. 将 Redis 连接彻底抽离出去,需要用户自己实现一个连接适配器,但好处是避免了多余的依赖
#### *2019-4-28:*
1. 设计简单的 LRU 缓存,给 redis 加载器使用
2. redis 加载器的一级缓存设计开发完毕,测试通过
3. 经验证,一级缓存让获取性能提升非常巨大,尤其是网络不畅的时候
#### *2019-4-26:*
1. 重新设计 LockTemplate 模板类,使用更加简洁
2. Tuz 类在写入资源的操作中抛出的异常变为 Throwable,注意注意!!!
3. 加入 Redis 资源加载器,可以非常简单透明地操作 redis 资源
#### *2019-4-25:*
1. 微调拦截器的执行顺序,同一级别的拦截器都会执行
2. 为 JDK9 以上版本做兼容,替换 Class.newInstance 方法为构造的 newInstance 方法
3. 更改为读写锁来保证线程安全,由于多个操作需要保证原子性,所以使用 HashMap 替代 ConcurrentHashMap
4. 为 redis 资源加载器做准备 ^_^
#### *2019-4-24:*
1. 新增 Tuz.unload 方法,当资源加载过多,耗费大量内存时就可以卸载掉不需要的资源了
2. 新增 Tuz.reload 方法,当资源被修改了需要更新时,就可以使用它更新资源
3. 新增 Tuz.unUse 方法,可以独立移除一个资源,更加细粒度的操作资源
4. 新增 Tuz.appendResource 方法,可以往一个命名空间添加资源值
#### *2019-4-22:*
1. 新增 JSON 资源加载器,现在 JSON 文件也可以很简单的加载并使用了
2. 新增 DiPlugin 的三个方法,可以临时指定单例模式或者多例模式
3. 新增 ProxyPlugin 的三个方法,可以临时指定单例模式或者多例模式
4. 以上两点改动可以做到不修改配置,灵活生成类的实例
5. 现在有两种 jar 可供使用,一种是不带任何依赖的,一种是带了完整依赖的
#### *2019-4-17:*
1. 代理工厂新增一个代理拦截器的方法
2. 当这个类可以被继承时,使用 CGlib 来实现动态代理
3. 如果不可以被继承,就使用 JDK 来动态代理,这时要求该类必须实现接口,而且使用接口来接收
#### *2019-4-14:*
1. 加入了动态代理工厂,你可以自己定制代理类
2. 使用代理工厂解决业务主次解耦的问题
3. 加入代理插件更方便用户实现代理模式以及拦截器模式
4. 不推荐直接使用代理工厂,推荐使用代理插件
5. 代理产生的类可以是单例模式也可以是多例模式的
#### *2019-4-7:*
1. FTP 功能完毕,修复了上一版本中乱码的问题
#### *2019-4-2:*
1. 新增多个 IO 处理方法,可以直接获取输入流
2. 新增 FTPHelper 工具,可以上传文件到 FTP 服务器
#### *2019-3-30:*
1. 修复了上一个版本中资源文件非英文字符集乱码的问题
2. 新增加了带有指定字符集的资源加载器,可以在加载资源时指定字符集
3. 废弃了 cn.com.fishin.tuz.loader.properties.InputStreamPropertiesLoader 加载器
4. 更改 NameSpaceHelper 的后缀生成策略
5. 新增 DiPlugin 一个方法,可以更简单获取实例
#### *2019-3-29:*
1. 加入了依赖注入插件,参考 cn.com.fishin.tuz.plugin.DiPlugin
2. 实现了文件系统资源加载器,可以从文件系统中加载资源文件了
3. 修改 cn.com.fishin.tuz.core.Loader 接口,使得它具有命名空间的功能
4. 增加了 API 文档和多个代码演示例子,还调整了包结构