TCP 连接的 TIME_WAIT 过多 导致 Tomcat 假死

via: http://www.cnblogs.com/digdeep/

最近系统二次开发之后,发现使用的 Tomcat 7 会经常假死。前端点击页面无任何反应,打开firebug,很多链接一直在等待服务器的反应。查看服务器的状态,CPU占用很少,最多不超过10%,一般只有2%,3%左右,内存占用倒是接近80, 90%。一开始怀疑是tomcat内存配置不够,但是打开 jvisualvm.exe 分析,发现Tomcat 占用的堆内存没有什么问题。因为是假死,所以最后怀疑到 tomcat的 链接数和 数据库的链接数的配置估计太小了。netstat -na 结果页显示很多time_wait.

查看各种状态的网络连接的数量:

1)Linux 使用命令:netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’

上面的命令可以查出各种状态的网络连接的数量
2)windows使用命令:

netstat -n |find /i “time_wait” /c

netstat -n |find /i “close_wait” /c

netstat -n |find /i “established” /c

windows下没有awk,所以要一个一个状态的统计它们的数量。

结果是:

1)TIME_WAIT: 状态的连接达到了 709

sql server占用的TIME_WAIT最多,还有nginx, tomcat都有一些处于 TIME_WAIT状态。

2)并且最大的端口达到了 65327 ,六万多,几乎接近端口的最大值 65535.

因为是 Windows server 2008,不同Linux下的TCP的调优。

解决方法:将 TcpTimedWaitDelay 调到 30S,让 TIME_WAIT 状态的维持最多30S,默认是4分钟。

如何查看或设置TcpTimedWaitDelay

cmd中运行 regedit 命令,找到 HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/ Services/TCPIP/Parameters 注册表子键

看看有没有  TcpTimedWaitDelay 项,有的话直接修改,没有的话创建一个并创建名为 TcpTimedWaitDelay 的新 REG_DWORD 值。 将此值设置为十进制 30,其为十六进制 0x0000001e。该值将等待时间设置为 30 秒。 停止并重新启动系统。 缺省值:0xF0,它将等待时间设置为 240 秒(4 分钟)。 建议值:最小值为 0x1E,它将等待时间设置为 30 秒。

修改之后,重启系统,在观察,TIME_WAIT在100左右徘徊。效果还是立竿见影的。几天来一直再也没有出现Tomcat假死的情况。

 

当然也可以同时 增大 MaxUserPort 的数值(2008最大值好像是 65535):

MaxUserPort :确定在应用程序从系统请求可用用户端口时,TCP/IP 可指定的最高端口号。默认是65535,可以调到10万.

如何查看或设置: 使用 regedit 命令访问 HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/ Services/TCPIP/Parameters 注册表子键并创建名为 MaxUserPort 的新 REG_DWORD 值,比如设置成200000。

参考:http://www.cnblogs.com/tianzhiliang/articles/2400176.html

 

TIME_WAIT 相关的网络原理,参见:http://www.cnblogs.com/digdeep/p/4869010.html

java enum的使用以及字符串其字符串之间的转换

via:http://blog.csdn.net/xieyuooo/

文章简单,相信在很多网站都能搜索到Java enum枚举的使用方式;可能有些东西我当时在刚开始用的时候没找到,所以我写了这篇文章,例如:

大多数地方写的枚举都是给一个枚举然后例子就开始switch,可是我想说,我代码里头来源的数据不太可能就是枚举,通常是字符串或数字,比如一个SQL我解析后首先判定SQL类型,通过截取SQL的token,截取出来可能是SELECT、DELETE、UPDATE、INSERT、ALTER等等,但是都是字符串,此时我想用枚举就不行了,我要将字符串转换成枚举怎么转呢,类似的情况还有从数据库取出数据根据一些类型做判定,从页面传入数据,根据不同的类型做不同的操作,但是都是字符串,不是枚举,悲剧的是我很少看到有人写到这个东西;所以我把它写下来,希望有人能用到。

首先为什么要用枚举?我们在什么时候用枚举比较好,用枚举有啥优势?

我认为哈,当你在一些一个范畴类,并可列举,不变化的类型,用以指导程序向不同的地方路由,用枚举是较好的选择;

听起来有点绕,不过有个例子也许可以明白,例如:

我们可以列举下日常工作日所做的事情:

上班、开会、吃饭、睡觉等

我们可以列举医院五官科需要检查人的部位:

眼睛、鼻子、耳朵、嘴巴等

这些都是可以被列举的,且每种事情我们要用不同的方式去做

当然你可以说:

1、可以用动态方法分派,通过配置文件或annotation;

2、可以使用常量来达到类似的效果;

3、直接通过字符串的equals来表达,用if else来表达

如果用配置加方法分派来做,是灵活,便于修改;但是如果在很多不经常修改的参数上,我们用这中方式往往增加配置的负担,并且当你需要看系统逻辑的时候,需要需要一遍看配置一遍看代码;不过,如果参数是可动态变换的信息,用配置是正确的选择;

而常量的使用,通常在switch case的时候都是数字,字符串在java中是不能做switch case的,使用常量的目的比case 1、case 2 …这种增加了可读性;但是字符串数据也麻烦,除非再映射一次,那没那个必要,其实枚举也差不多是帮你映射了一次,只是它将代码封装了而已吧了,既然他弄好了,而且语法上支持,干嘛不用呢!其次,常量虽然增加了可读性,不过他没有范畴和管理类型的概念,即一个枚举的定义会定义个范畴,可以很好的将这个范围所需要的东西列举出来,而常量通常是些自己定义的一些池,放在一些公共类中或随机定义,都是比较零散的,并且枚举在switch的时候就明确定义好了就在锁列举的范围内case,既可以控制好系统,增加可读性,并且可以随时查看这个范畴的枚举信息到底有那些,达到类似看配置文件的作用;不过还是回到那句话,如果参数是可变的,那么就不适合做枚举,枚举是一定是可列举的,或者说当前系统考虑范围是可以被枚举的,例如上面的医院五官科,可能还有很多没有列举到,但是当前医院只处理几个部位,不处理其他的,就是这个道理;什么是可变的呢,例如URL参数来分派到对应方法,不可能大家加一段逻辑就去加一个枚举,加一个case,此时用【配置 动态方法分派】更好,当然配置可以用文件或annotation而已。

还有最土的就是,通过字符串equals,用if else来实现,呵呵,这个并没有什么不好,只是这个写比较零散,其次,字符串匹配的equals每次匹配都需要对比每个字符,如果你的代码中大量循环,性能并不是很好,其余的看看上面的描述就更加清楚了;

其次,枚举提供一种类型管理的组件,让面向对象的体系更加完善,使得一些类型的管理既可配置化,并可以管理,在使用枚举的地方都可以沿着枚举的定义找到那些有处理过,那些没处理过,而上述几种很难做到;例如,数据库的操作类型定义了10种,那么再判定的过程中就可以讲枚举像配置文件一样看待,而又非常简单的来管理。

最后,枚举绝对是单例的,对比的性能和数字性能相当,既可以得到可读性,也可以得到性能。

我们先定义个简单枚举(这里只是个例子,就简单定义3个变量了):

public enum SqlTypeEnum {  
    INSERT ,   
    UPDATE ,  
    DELETE ,  
    SELECT  
}

此时解析SQL后,获取出来一个token,我们要获取这个token的枚举怎么获取呢?

这样获取:

String token = "select";  
SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf(token.toUpperCase());

如果没获取到,java会抛出一个异常哦:IllegalArgumentException No enum const class SqlTypeEnum.XXX

我做大写处理的原因是因为枚举也是大写的(当然如果你的枚举是小写的,那你就小写,不过混写比较麻烦哈),其实valueOf就是调用了枚举的底层映射:

java enum的使用以及字符串其字符串之间的转换

调用的时候会调用这个方法:

java enum的使用以及字符串其字符串之间的转换

所以内部也是一个HashMap,呵呵!

拿到这个信息后,就可以做想要的操作了:

switch(sqlTypeEnum) {  
  case INSERT:处理insert逻辑;break;  
  case DELETE:处理delete逻辑;break;  
 ....  
}

OK,有些时候可能我们不想直接用INSERT、UPDATE这样的字符串在交互中使用,因为很多时候命名规范的要求;

例如定义一些用户操作类型:

1、保存用户信息

2、通过ID获取用户基本信息

3、获取用户列表

4、通过ID删除用户信息

等等

我们可能定义枚举会定义为:

public enum UserOptionEnum {  
    SAVE_USER,  
    GET_USER_BY_ID,  
    GET_USER_LIST,  
    DELETE_USER_BY_ID  
}

但是系统的方法和一些关键字的配置,通常会写成:

saveUser、getUserById、getUserById、deleteUserById

当然各自有各自的规则,不过中间这层映射,你不想做,就一方面妥协,要么枚举名称全部换掉,貌似挺奇怪的,要么方法名称全部换掉,更加奇怪,要么自己做映射,可以,稍微麻烦点,其实也不麻烦?

我们首先写个将枚举下划线风格的数据转换为驼峰的方法,放在一个StringUtils里面:

public static String convertDbStyleToJavaStyle(String dbStyleString , boolean firstUpper) {  
        dbStyleString = dbStyleString.toLowerCase();  
        String []tokens = dbStyleString.split("_");  
        StringBuilder stringBuilder = new StringBuilder(128);  
        int length = 0;  
        for(String token : tokens) {  
            if(StringUtils.isNotBlank(token)) {  
                if(length == 0 && !firstUpper) {  
                    stringBuilder.append(token);  
                }else {  
                    char c = token.charAt(0);  
                    if(c >= \'a\' || c <= \'z\') c = (char)(c - 32);  
                    stringBuilder.append(c);  
                    stringBuilder.append(token.substring(1));  
                }  
            }  
              length;  
        }  
        return stringBuilder.toString();  
    }

重载一个方法:

public static String convertDbStyleToJavaLocalStyle(String dbStyleString) {  
        return convertDbStyleToJavaStyle(dbStyleString , false);  
    }

然后定义枚举:

public enum UserOptionEnum {  
    SAVE_USER,  
    GET_USER_BY_ID,  
    GET_USER_LIST,  
    DELETE_USER_BY_ID;  
  
    private final static Map ENUM_MAP = new HashMap<String, UserOptionEnum>(64);  
  
  
    static {  
        for(UserOptionEnum v : values()) {  
            ENUM_MAP.put(v.toString() , v);   
        }  
    }  
  
    public staticUserOptionEnum fromString(String v) {  
        UserOptionEnum userOptionEnum = ENUM_MAP.get(v);  
        return userOptionEnum == null ? DEFAULT :userOptionEnum;  
    }  
  
    public String toString() {  
        String stringValue = super.toString();  
        return StringUtil.convertDbStyleToJavaLocalStyle(stringValue);  
    }  
}

OK,这样传递一个event参数让如果是:saveUser,此时就用:

String event = "saveUser";//假如这里得到参数  
UserOptionEnum enum = UserOptionEnum.fromString(event);

其实就是自己做了一个hashMap,我这加了一个fromString,因为枚举有一些限制,有些方法不让你覆盖,比如valueOf方法就是这样。

其实没啥好讲的了,非要说,再说说枚举加一些自定义变量吧,其实枚举除了是单例的外,其余的和普通类也相似,它也可以有构造方法,只是默认情况下不是而已,也可以提供自定义的变量,然后获取set、get方法,但是如果有set的话,线程不是安全的哦,要注意这点;所以一般是构造方法就写好了:

public enum SqlTypeEnum {  
   INSERT("insert into"),  
   DELETE("delete from")  
   ......省略;  
  
   private String name;//定义自定义的变量  
  
   private SqlTypeEnum(String name) {  
      this.name = name;  
   }  
  
   public String getName() {  
       return name;  
   }  
  
   public String toString() {  
       return name   " 我靠";//重写toString方法  
  }  
  //一般不推荐  
  public void setName(String name) {  
        this.name = name;  
  }  
}

 

调用下:

SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf("INSERT");  
System.out.println(sqlTypeEnum);  
System.out.println(sqlTypeEnum.getName());

不推荐也调用下:

sqlTypeEnum.setName("我靠");  

在另一个线程:

SqlTypeEnum sqlTypeEnum = SqlTypeEnum.valueOf("INSERT");  
System.out.println(sqlTypeEnum);  
System.out.println(sqlTypeEnum.getName());

发现结果被改了,呵呵!

 

怎么通过java去调用并执行shell脚本以及问题总结

背景

我们在开发过程中,大部分是java开发, 而在文本处理过程中,主要就是脚本进行开发。 java开发的特点就是我们可以很早地进行TDDL, METAQ 等等地对接; 而脚本开发的特点就是在进行批处理的时候非常方便。 前阵子我遇到这么一个需求场景: 对抓取的数据进行打包, 后来又遇到我要通过脚本进行抓取,比如nodejs下基于phantomjs的casperjs爬虫。

解决方法

对于第一个问题:java抓取,并且把结果打包。
那么比较直接的做法就是,java接收各种消息(db,metaq等等),然后借助于jstorm集群进行调度和抓取。 最后把抓取的结果保存到一个文件中,并且通过调用shell打包, 回传。 也许有同学会问, 为什么不直接把java调用odps直接保存文件,答案是,我们的集群不是hz集群,直接上传odps速度很有问题,因此先打包比较合适。(这里不纠结设计了,我们回到正题)

java调用shell的方法

通过ProcessBuilder进行调度

这种方法比较直观,而且参数的设置也比较方便, 比如我在实践中的代码(我隐藏了部分业务代码):

ProcessBuilder pb = new ProcessBuilder("./" + RUNNING_SHELL_FILE, param1,
                                               param2, param3);
        pb.directory(new File(SHELL_FILE_DIR));
        int runningStatus = 0;
        String s = null;
        try {
            Process p = pb.start();
            try {
                runningStatus = p.waitFor();
            } catch (InterruptedException e) {
            }

        } catch (IOException e) {
        }
        if (runningStatus != 0) {
        }
        return;

这里有必要解释一下几个参数:
RUNNING_SHELL_FILE:要运行的脚本
SHELL_FILE_DIR:要运行的脚本所在的目录; 当然你也可以把要运行的脚本写成全路径。
runningStatus:运行状态,0标识正常。 详细可以看java文档。
param1, param2, param3:可以在RUNNING_SHELL_FILE脚本中直接通过1,2,$3分别拿到的参数。

直接通过系统Runtime执行shell

这个方法比较暴力,也比较常用, 代码如下:

p = Runtime.getRuntime().exec(SHELL_FILE_DIR + RUNNING_SHELL_FILE + " "+param1+" "+param2+" "+param3);
p.waitFor();

我们发现,通过Runtime的方式并没有builder那么方便,特别是参数方面,必须自己加空格分开,因为exec会把整个字符串作为shell运行。

可能存在的问题以及解决方法

如果你觉得通过上面就能满足你的需求,那么可能是要碰壁了。你会遇到以下情况。

没权限运行

这个情况我们团队的朱东方就遇到了, 在做DTS迁移的过程中,要执行包里面的shell脚本, 解压出来了之后,发现执行不了。 那么就按照上面的方法授权吧

java进行一直等待shell返回

这个问题估计更加经常遇到。 原因是, shell脚本中有echo或者print输出, 导致缓冲区被用完了! 为了避免这种情况, 一定要把缓冲区读一下, 好处就是,可以对shell的具体运行状态进行log出来。 比如上面我的例子中我会变成:

ProcessBuilder pb = new ProcessBuilder("./" + RUNNING_SHELL_FILE, keyword.trim(),
                                               taskId.toString(), fileName);
        pb.directory(new File(CASPERJS_FILE_DIR));
        int runningStatus = 0;
        String s = null;
        try {
            Process p = pb.start();
            BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
            BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            while ((s = stdInput.readLine()) != null) {
                LOG.error(s);
            }
            while ((s = stdError.readLine()) != null) {
                LOG.error(s);
            }
            try {
                runningStatus = p.waitFor();
            } catch (InterruptedException e) {
            }

记得在start()之后, waitFor()之前把缓冲区读出来打log, 就可以看到你的shell为什么会没有按照预期运行。 这个还有一个好处是,可以读shell里面输出的结果, 方便java代码进一步操作。

也许你还会遇到这个问题,明明手工可以运行的命令,java调用的shell中某一些命令居然不能执行,报错:命令不存在!

比如我在使用casperjs的时候,手工去执行shell明明是可以执行的,但是java调用的时候,发现总是出错。 通过读取缓冲区就能发现错误日志了。 我发现即便自己把安装的casperjs的bin已经加入了path中(/etc/profile, 各种bashrc中)还不够。 比如:

export NODE_HOME="/home/admin/node"
export CASPERJS_HOME="/home/admin/casperjs"
export PHANTOMJS_HOME="/home/admin/phantomjs"
export PATH=$PATH:$JAVA_HOME/bin:/root/bin:$NODE_HOME/bin:$CASPERJS_HOME/bin:$PHANTOMJS_HOME/bin

原来是因为java在调用shell的时候,默认用的是系统的/bin/下的指令。特别是你用root权限运行的时候。 这时候,你要在/bin下加软链了。针对我上面的例子,就要在/bin下加软链:

ln -s /home/admin/casperjs/bin/casperjs casperjs;
ln -s /home/admin/node/bin/node node;
ln -s /home/admin/phantomjs/bin/phantomjs phantomjs;

这样,问题就可以解决了。

如果是通过java调用shell进行打包,那么要注意路径的问题了

因为shell里面tar的压缩和解压可不能直接写:

tar -zcf /home/admin/data/result.tar.gz /home/admin/data/result

直接给你报错,因为tar的压缩源必须到路径下面, 因此可以写成

tar -zcf /home/admin/data/result.tar.gz -C /home/admin/data/ result

如果我的shell是在jar包中怎么办?

答案是:解压出来。再按照上面指示进行操作。
(1)找到路径

String jarPath = findClassJarPath(ClassLoaderUtil.class);
        JarFile topLevelJarFile = null;
        try {
            topLevelJarFile = new JarFile(jarPath);
            Enumeration<JarEntry> entries = topLevelJarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                if (!entry.isDirectory() && entry.getName().endsWith(".sh")) {
                    对你的shell文件进行处理
                }
            }

对文件处理的方法就简单了,直接touch一个临时文件,然后把数据流写入,代码:

FileUtils.touch(tempjline);
tempjline.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempjline);
IOUtils.copy(ClassLoaderUtil.class.getResourceAsStream(r), fos);
fos.close();

有这个这个东东,相信大家会减少踩坑,而且大胆地使用java和脚本之间的交互吧。 java可以调用shell,那么shell再调用其他就方便了。 记得一点, 不要过度地依赖缓冲区进行线程之间的通信。原因自己去学习吧。

via: https://yq.aliyun.com/articles/2362

tomcat 异常-java.util.prefs.BackingStoreException

今天Linux部署Tomcat的时候,tail 日志的时候,发现频繁有下面的错误出来:

Couldn’t flush system prefs: java.util.prefs.BackingStoreException: Couldn’t get file lock.

解决办法是: 在执行程序的用户的home目录下:

#注意切换到执行程序的用户下执行下面的操作
mkdir -p ~/.java/.systemPrefs
mkdir ~/.java/.userPrefs
chmod -R 755 ~/.java

然后在执行程序之前增加如下参数:

JAVA_OPTS="-Djava.util.prefs.systemRoot=/home/user/.java Djava.util.prefs.userRoot=/home/user/.java/.userPrefs"

例如tomcat的话,可以建立setenv.sh ,并且添加上述参数 执行即可

参考:

https://confluence.atlassian.com/confkb/could-not-lock-user-prefs-unix-error-code-2-670958391.html

http://stackoverflow.com/questions/23960451/java-system-preferences-under-different-users-in-linux

Could not generate DH keypair 的问题

项目中出现下述异常

javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair,

具体问题是:在访问https 的时候出现的。原因是

Java 7 and earlier limit their support for DH prime sizes to a maximum of 1024 bits,

Java 7 以及早期版本是只支持 1024 位的 DH Parma的,解决该异常:

  1. 升级你使用的Java版本到 java 8
  2. 调整https使用的dhparam 指定1024位

修改服务器使用1024位dhparam

Apache.

  1. apache 2.4.7 之后默认使用 2048以上了,如果你要指定1024 ,就在证书后面添加1024位的dh param 参考这里:a. http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslcertificatefile b.http://httpd.apache.org/docs/current/ssl/ssl_faq.html#javadh
    证书的格式大致如下:

    -----BEGIN CERTIFICATE----- 
    ** a lot of lines of gibberish ** 
    -----END CERTIFICATE----- 
    -----BEGIN DH PARAMETERS----- 
    ** another bunch of lines of gibberish ** 
    -----END DH PARAMETERS-----

    Nginx

几个有用的命令

openssl dhparam 1024 -out somefile 生成1024位dh param

Orika 教程以及与Spring的集成

via: http://malsolo.com/  又来挖坑了,找了一圈,只有这篇文字比较详细。得空翻译一下

I love good software, and Orika is a really interesting project. In this post I’m going to talk about this Java bean mapping framework that I’ve used recently and I highly recommend.

Orika (formerly hosted at google code) claims to be a simpler, lighter and faster Java bean mapping. And it really is.

It allows you to convert objects by copying its attributes from one to another. It performs this by using Java introspection, instead of XML configuration or something like that. It uses code generation to create the mappers, and it has an interesting strategy for its core classes that allows you to optimize the performance.

It is completely configurable, for instance, the java code generator by default is Javassist, but you can use EclipseJdt or even use another provider by implementing the appropriate interface.

Finally, it’s a very well documented project, with clear explanations and useful code.

I really like it.

If you want to try it, just add the next dependency to your maven project:

Or download the library along with three required libraries:

  • javassist (v 3.12.0+)
  • slf4j (v 1.5.6+)
  • paranamer (v 2.0+)

Main concepts

There are two core classes, the first one is MapperFactory, the class to configure the mappings and to obtain the second core class: MapperFacade, that actually provides the service of a Java bean mapping.

MapperFactory

By using a fluent API, you can construct a MapperFactory via its main implementation,DefaultMapperFactory and its static class helpers intended for building theMapperFactory.

Then you can obtain a ClassMapBuilder from the MapperFactory in order to declare the mapping configuration in a fluent-style, that is: mapping fields (bi-directional by default, or in one direction if you’d rather), excluding fields, specifying constructors and customizing mappings of Lists, Sets, nested fields and so on.

There is a default mapping that maps fields with matching names between two classes, and you can also register the mapping in the MapperFactory (the ClassMapBuilder is built with the very MapperFactory as atribute). Both operations have the appropriate methods to be called.

Let’s see an example:

We have two classes: PersonDTO and Person, and we want to convert one object for the former class to an object of the latter one, so let’s configure the MapperFactory:

The ClassMapBuilder provides interesting mappings for excluding fields, mapping fields in one direction only (as I mentioned above, mapping is by default bi-directional) and so on. See the Declarative Mapping Configuration and Advanced Mapping Configurations for more options.

MapperFacade and BoundMapperFacade

The MapperFacade performs the actual mapping work, for doing so, you have to obtain the MapperFacade from the MapperFactory and then use its map methods. There are two general options, map(objectA, B.class), which will create a new instance of B.class and map the property values of objectA onto it, or map(objectA, objectB), which copies properties from objectA onto objectB, being both of them not null.

Let’s see an example:

The BoundMapperFacade is intended to be used with a pair of types to map and no further customization needed. It provides improved performance over use of the standardMapperFacade and it has the same usage plus a mapReverse method for mapping in the reverse direction.

See below an example:

If you’re primarily concerned with the mapping of a particular type to another, and your object graph has no cycles, use BoundMapperFacade because it has better performance, since you avoid the overhead of looking up an appropriate mapping strategy for the fields of the objects to be mapped, that happens when you call the map method.

Performance

The most expensive operations are instantiation and initialization of the MapperFactory, and the MapperFacade which is obtained from it.

Both of them are thread-safe objects, thus singletons or static access for them are a very recommended approach.

For instance, you can consider an static acces to the MapperFactory:

And then use this class for registering class maps and access to the MapperFacade in a customize way:

In this case I’m using two new classes: Customer and CustomerDTO.

Finally, just use this helper classes:

How it works and Customization

The best way to understand how Orika works is to read the FAQ, but the source code is very clear and easy to follow, just in case you want to take a deeper look to the details.

“Orika uses reflection to investigate the metadata of the objects to be mapped and then generates byte-code at runtime which is used to map those classes […]”

“Orika mapping is configured via Java code at runtime, via a fluent-style ClassMapBuilder API. An instance is obtained from a MapperFactory which is then used to describe the mapping from one type to another. Finally, the specified ClassMap is registered with the MapperFactory for later use in generating a byte-code mapper.”

To configure Orika, the author recommends to extend the ConfigurableMapper class, and I will use this approach for using Orika with Spring (see below)

If you want to configure the code generation, compilation and so on, see how to do this using the MapperFactory in the next topic.

MapperFactory Configuration

The core class MapperFactory is instantiated as you’ve seen above, by building the provided implementation: DefaultMapperFactory. It uses a set of Strategy and factory objects for resolving constructors, for compiling code (actually, they belong to its inner static class MapperFactoryBuilder that is extended by its other inner static class that you use to instantiate the MapperFactory: the Builder), for resolving properties, and specially for building class maps.

So you can customize all of them after calling Builder() and before the build() method:

Custom Mappers

A Mapper<A, B> copies the properties from one object onto another and it’s used internally by Orika (MapperFacade, MapperGenerator and MapperFactory use it, but it isn’t really interesting for this post, actually)

If you want to take control of how properties of one object instance are copied to another object instance (not null), just extends the abstract class CustomMapper<A,B>.

Then, you can register your custom mapper in the MapperFactory via its methodregisterMapper(Mapper<A, B> mapper).

It’s intended to be used when the mapping behavior configured via the ClassMapBuilder API doesn’t suit your purposes, but only to copy properties, that is, the destination object already exists. If you want to control instantiation, use a CustomConverter or ObjectFactory which both return an instance. See below.

If you need to map nested members of the given object, you can doing so by using the current MapperFacade that it’s accessible via a protected mapperFacade variable.

I’ve used converters in a Spring applications as components that can be scanned and used to configure the MapperFactory by extending ConfigurableMapper as I mentioned above and I’ll show below.

Object Factories

An ObjectFactory<D> instantiates objects. You can implement this interface and register your implementation in the MapperFactory via their method registerObjectFactory (there are a couple of them)

I’ve never used this option.

Custom Converters

A Converter<S, D> combines both ObjectFactory and Mapper together, returning a new instance of the destination type with all properties copied from the source instance.

When you want to take complete control over the instantiation and mapping, just extendCustomConverter<S, D> to create the new instance and to provide it the properties from the source object.

Then, you can register your custom converter in the MapperFactory by obtaining itsConverterFactory via the getConverterFactory() method and then using one of itsregisterConverter() methods.

I’ve found this option useful when you want to create some hard conversion between classes that has the same meaning in your code but are not really related in terms of Java. Well, it’s a conversion, let’s say, from a date represented in a String and a date that has to be a Calendar. You have to use some formatter for doing so.

It’s also very easy to configure converters as Spring components if you need so.

Using Orika with Spring

Since the Orika core classes MapperFactory and the MapperFacade obtained from it are very expensive to instantiate and initialize, but at the same time are thread-safe, they should be shared as singletons. That is, they are good candidates for being Spring beans with singleton scope.

Let’s start with using spring-framework in a maven project,just add the next dependency to the pom.xml file:

The Spring application

Let’s create a couple of beans to obtain a Person and a PersonDTO, let’s call them service and repository respectively:

And their implementations, the one for the service will use the repository interface, and the one for the repository will be a simple in-memory implementation.

Note that the service will need to convert from the DTO to the domain object, so let’s inject a mapper.

As you can see, I used setter injection (via autowiring) to provide a MapperFactory in order to obtain from it a MapperFacade. I’m using the suggestion of having the MapperFactory as singleton, that it’s easy to achieve with Spring.

Since MapperFactory has a particular way to be instantiated, the best option is to create a factory for exposing it, a FactoryBean:

This approach has been selected just in case I want to use a BoundMapperFacade, but it’s possible to use a MapperFacade directly by creating a MapperFacadeFactory.

Now, let’s configure the Spring application

This is enough for the project to run:

Person{id=0, name=Name 0, surnames=null, address=null}

But pay attention, the mapper didn’t work, because there are differences between the two classes (tow attributes with different names, and the same concept, address, with different types)

Plugging in Orika Mappers and Converters with Spring

Orika provides custom converters and mappers, so the only thing we need is to configure them as spring beans in the application context. Let’s see how to do this.

First, we need a custom mapper. We annotate it as component to be discovered (via component scanning) by Spring.

Finally, we have to plug in mappers and converters that belongs to the Spring application context, into the MapperFactory. If we extend ConfigurableMapper we will have a MapperFactory created for us, and later on, the chance to access the application context itself to look up for the custom mappers and converters that are Spring components to register them in the MapperFactory.

Let’s show the code:

Some important things to note here:

    • Our class SpringConfigurableMapper is a @Component

When Spring instantiates it, it will call its constructor that is inherited fromConfigurableMapper and it calls an init method that creates a MapperFactory and allows you to configure via the overriden method.

So, the first method called is configure(MapperFactory).

Furthermore, don’t create your own MapperFactory as I did above, because it won’t be the one you’ll use to register the mappers and converters, on the contrary, this class will create a new MapperFactory.

    • Our class SpringConfigurableMapper has the ApplicationContext @Autowired

It’s the same as if you implement ApplicationContextAware, but I’d rather this approach.

Besides, it implies that the method will be called after all the beans have been created. Since you already have a MapperFactory, you can then search for custom components and converters that are spring beans and register them in the MapperFactory.

    • Our class extends ConfigurableMapper that implements MapperFacade

So, you have to slightly change the PersonServiceImpl and it’s better to not have a MapperFacadeFactory or you will have an autowiring exception because there would be two beans of the MapperFacade class:

Now, if you run the Orika with Spring main class, OrikaSpringTest, everything will run as expected:

Person{id=85, name=Name 85, surnames=[S., Surname 85], address=Address{street=My street 85, city=City 85, zipCode=code 85}}

 

Resources

Java 多线程

随着多核芯片逐渐成为主流,大多数软件开发人员不可避免地需要了解并行编程的知识。而同时,主流程序语言正在将越来越多的并行特性合并到标准库或者语言本身之中。我们可以看到,JDK 在这方面同样走在潮流的前方。从JDK 5到JDK 7,越来越多的线程相关的新API加入到了标准库中,为不同场景下对线程实现与调度提供了完善的支持。我们可以充分利用这些类库,来提高开发效率,改善程序性能。

Java中使用线程

Java的多线程基于以下两个重要的机制:

  1. 父线程结束,如果子线程正在运行,子线程不会跟着结束。
  2. 每个线程拥有一个线程栈,会维护引用,用于GC。

Java提供了以下两种常用的方式来使用多线程:

  1. 继承Thread
  2. 实现Runnable接口

继承Thread

class MyThread extends Thread {
    @Override
    public void run() {
    
    } 
}

Thread t = new MyThread();
t.start();

 

实现Runnable接口

class MyThread implements Runnable {
    @Override
    public void run() {
    
    }
}

MyThread mt = new MyThread();
Thread t = new Thread(mt);
t.start();

 

需要注意的是:一个线程只能start()一次,如果多次调用start()方法,会出现Java.lang.IllegalStateException

两种方式的区别在于继承Thread方式是每次都会创建一个MyThread对象,而如果采用实现Runnable接口的方式,可以使用同一个MyThread对象来创建多个线程,便于线程间共享内存。因此,设计线程间的数据共享时,一般应当采用实现Runnable接口的方式。

Callable与Future

java.util.concurrent包中还提供了一个Callable接口来实现线程,使用Callable需要与Future类配合使用。

public class TEMP {
    public static void main(String[] args) throws Exception {
         FutureTask<Integer> task = new FutureTask<>(new FF());
         new Thread(task).start();
         System.out.println("main thread");
         System.out.println(task.get());
    }
}

class FF implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Thread.sleep(2000);
        System.out.println("ff thread");
        return new Random().nextInt(100);
    }
}

 

程序运行的输出为:

main thread
ff thread
34

 

Callable接口与Runnable接口类似,不同的是,call方法是可以有返回值的,并且可以抛出异常。如果没有System.out.println(task.get()),那么主线程在运行完System.out.println("main thread")就会结束,但是因为有task.get(),因此,主线程会等待Callable线程执行完毕。

Callable-Future类似的一个方法是回调(callback)。在子线程结束时调用父线程的方法来通知父线程。

一般Callable/Future与ExecutorService配合使用。使用ExecutorService来运行Callable线程:

ExecutorService exec = Executors.newCachedThreadPool();
// exec = Executors.newFixedThreadPool(3 /* nThread */);
exec.submit(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        // ...
        return null;
    }
});
// exec.shutdown();

 

ExecutorService也可以用来执行实现Runnable线程:

exec.submit(new Runnable() {
    @Override
    public void run() throws Exception {
        // ...
    }
});

 

使用ExecutorService时,可以通过FutureTask来获取Callable线程call方法的返回值:

ExecutorService exec = Executors.newCachedThreadPool();
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        // ...
        return null;
    }
});
exec.submit(task);
Integer result = task.get();

 

ExecutorService还可以取消(cancel)一个Task(FutureTask)。

简单的线程同步

Java提供了强制原子性的内置锁机制:synchronized块。一个synchronized块有两部分:所对象的引用以及这个锁保护的代码块。内置锁在Java中扮演了互斥锁(mutex)的角色。内置锁匙可重入的(Reentrant)。通常使用synchronized关键字来进行线程间的同步。synchronized有两种用法:修饰方法和修饰代码块。

修饰方法

可以使用synchronized关键字来修饰类中的方法,从而达到线程间同步的目的。示例:

class A {
    public synchronized void Func() {
        // ...
    }
}

 

synchronized关键字修饰方法表明该方法在运行期间将会对this加锁,也就是说,当方法Func运行时其他方法无法操作this对象。需要注意的是,synchronizedstatic关键字是可以共存的。

修饰代码块

synchronized关键字除了用来修饰方法,还可以用来修饰代码块:

class A {
    Integer i;
    // ...
    public void func() {
        // ...
        synchronized(i) {
            // ...
        }
    }
}

 

这表明在synchronized中的代码块运行期间将会占有锁this.isynchronized修饰方法等价于下列写法:

class A {
    public void Func() {
        synchronized(this) {
            // ...
        }
    }
}

 

可以看到,synchronized修饰代码块与修饰方法类似,但控制粒度更细,同步开销也较小。但是,在尽量减小同步代码块规模时应该注意原子操作中的语句必须在同一个同步代码块中。

静态方法同步

要同步静态方法,需要一个作用于整个类对象的锁。这个锁就是类自身。举例:

class A {
    public static void func() {
        synchronized(B.class) {
            // ...
        }
    }
}

 

可重入锁

另一个需要注意的地方是Java的同步锁匙可重入锁,例如,递归调用run方法或者调用的函数有同样的synchronized锁并不会造成死锁。示例如下:

class KK extends Thread {
    static Integer x = 10;
    @Override
    public void run() {
        synchronized (x) {
            if(x > 5) {
                x -= 1;
                System.out.println(x);
                run();
            }
        }
    }
}

 

wait, notify, notifyAlljoin是常用的线程控制调度方法。

共享对象可见性和volatile

一种常见的错误观念认为只有写入共享变量是才需要同步,其实并非如此。volatile是一个声明,volatile关键字表示内存可见,volatile声明的变量不会被copy到线程的本地内存(缓存、缓冲区、寄存器,Cache等)中,而是直接在主存上进行操作。每次使用前从主存中读取新值,修改后立即写入主存,这用于防止线程间因寄存器和缓存引起的肮脏数据的读取和使用。

进一步的,JVM的规范里并没有要求volatile关键字修饰的变量一定不能被copy到线程的本地内存中,而是要求对其读写需要遵循happens-before语义,类似于互斥锁。同时,volatile还涉及到编译器的指令重排和CPU的重排序等等(影响变量之间的访问顺序)。

有些时候,可能一个线程只需要一个变量的值,这时也有可能需要使用volatile的方式来防止出现同步错误。但需要注意的是,volatile不能和final一起共同修饰一个变量,因为final变量是不可改的。

Lock/Condition机制

Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。不过某些锁支持共享资源的并发访问,如:ReadWriteLock(读写锁),在线程安全控制中,通常使用ReentrantLock(可重入锁)。使用该Lock对象可以显示加锁、释放锁。

使用Lock的一般框架:

Lock lock = new ReetrantLock();
lock.lock();
try {
    // ...
}
finally {
    lock.unlock();
}

 

注意,try中的语句块是可能抛出异常的,因此,如果可能有异常抛出,那么lock.unlock()必须在finally语句块中,以确保JVM会释放锁。通常认为:Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock更灵活的结构,有很大的差别,并且可以支持多个Condition对象。

Condition的功能更类似于传统多线程技术中的Object.wait()(Condition.await())和Object.notifyAll()(Condition.signal()),用于实现线程间通信,一个Lock锁可以支持多个Condition。

一个使用Lock/Condition机制的例子:

class AA implements Runnable {
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        lock.lock();
        System.out.println("get lock");
        // ...
        System.out.println("release lock");
        lock.unlock();
    }
}

 

在这里面,可以配合使用Condition完成较复杂的线程管理和调度。在Java多线程之Condition接口的实现一文中,作者使用Condition/Lock机制来实现了经典的“生产者-消费者”调度模型。在很多情景下,Condition提供了一种更加高效的、更有针对性的线程调度和同步方式。

BlockingQueue的使用

BlockingQueue,阻塞队列。BlockingQueue是一种特殊的Queue,若BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态直到BlocingkQueue进了新货才会被唤醒。同样,如果BlockingQueue是满的任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有新的空间才会被唤醒继续操作。

BlockingQueue提供的方法主要有:

  1. add(anObject): 把anObject加到BlockingQueue里,如果BlockingQueue可以容纳返回true,否则抛出IllegalStateException异常。
  2. offer(anObject): 把anObject加到BlockingQueue里,如果BlockingQueue可以容纳返回true,否则返回false。
  3. put(anObject): 把anObject加到BlockingQueue里,如果BlockingQueue没有空间,调用此方法的线程被阻断直到BlockingQueue里有新的空间再继续。
  4. poll(time): 取出BlockingQueue里排在首位的对象,若不能立即取出可等time参数规定的时间。取不到时返回null。
  5. take(): 取出BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的对象被加入为止。

根据不同的需要BlockingQueue有4种具体实现:

  1. ArrayBlockingQueue

规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小。其所含的对象是以FIFO(先入先出)顺序排序的。

  1. LinkedBlockingQueue

大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制。若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所含的对象是以FIFO(先入先出)顺序排序的。LinkedBlockingQueue和ArrayBlockingQueue比较起来,它们背后所用的数据结构不一样,导致LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于ArrayBlockingQueue。

  1. PriorityBlockingQueue

类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。

  1. SynchronousQueue

特殊的BlockingQueue,对其的操作必须是放和取交替完成的。

BlockingQueue特别适用于线程间共享缓冲区的场景。BlockingQueue的四种实现也能够满足大多数的缓冲区调度需求。

Fork/Join

Fork/Join机制是JDK 7新增加的多线程框架,如果一个应用能被分解成多个子任务,并且组合多个子任务的结果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决。下图可大概说明Fork/Join模式的结构:

Fork/Join模式

Fork/Join框架的几个核心类:

  1. ForkJoinPool: 线程池实现。实现了Work-stealing(工作窃取)算法,线程会主动寻找新创建的任务去执行,从而保证较高的线程利用率。程序运行时只需要唯一的一个ForkJoinPool。
  2. ForkJoinTask: 任务类,有两个子类实现具体的Task:
    1. RecursiveAction: 无返回值;
    2. RecursiveTask: 有返回值。

使用Fork/Join

首先,我们需要为程序创建一个ForkJoinPool:

Creates a ForkJoinPool with parallelism equal to java.lang.Runtime.availableProcessors,

private static ForkJoinPool pool = new ForkJoinPool();

 

这儿ForkJoinPool的构造函数有重载方法,可以通过参数设置线程数量(并行级别,parallelism level)以及ThreadFactory。

Task类需要实现compute方法,ForkJoinTask的代码框架:

If (problem size > default size){
    task s = divide(task);
    execute(tasks);
} 
else {
    resolve problem using another algorithm;
}

 

无返回值Task的示例

通过并行,将一个数组中每个元素的值都置为其索引值,即令a[i] = i

class Task {
    // Creates a ForkJoinPool with parallelism equal to
    // java.lang.Runtime.availableProcessors,
    private static ForkJoinPool pool = new ForkJoinPool();
    private static final int default_size = 10;

    public void solve() {
        System.out.println("available processor number: " +
                java.lang.Runtime.getRuntime().availableProcessors());
        int[] a = new int[100];
        SubTask task = new SubTask(a, 0, 100);
        System.out.println("task start!");
        pool.invoke(task);
        System.out.println("task finish!");
    }

    class SubTask extends RecursiveAction {
        int[] a;
        int l, r;

        public SubTask(int[] a, int l, int r) {
            this.a = a;
            this.l = l;
            this.r = r;
        }

        @Override
        protected void compute() {
            System.out.println("Thread id: " + Thread.currentThread().getId());
            if (r - l > BB.default_size) {
                int mid = (l + r) / 2;
                invokeAll(new SubTask(a, l, mid + 1), new SubTask(a, mid, r));
            }
            else {
                for (int i = l; i < r; ++i) {
                    a[i] = i;
                }
            }
        }
    }
}

 

有返回值的Task

如果子任务有返回值,只需要改成继承RecursiveTask类,然后compute方法返回对应类型的返回值即可。例如:

class SubTask extends RecursiveTask<Integer> {
    public Integer compute() {
        // ...
    }
}

SubTask task = new SubTask(...);
Integer result = task.get()

 

在fork子任务时,只需要:

SubTask t1 = new SubTask(...);
SubTask t2 = new SubTask(...);
invokeAll(t1, t2);
try {
    result = t1.get()+t2.get();  
} catch (InterruptedException | ExecutionException e) {  
    e.printStackTrace();  
}
return result;

 

异步执行Task

上面两个示例都是同步执行的,invoke与invokeAll都是阻塞当前线程的。当Task线程运行时会阻塞父线程,而在很多场合,我们需要Task线程异步执行。这是需要使用到execute或者submit方法。execute方法直接执行task,而submit方法是将task提交到任务队列里边去。而shutdown方法则表示线程池不再接收新的task(ForkJoinPool是有守护线程的)(shutdown之后再submit后产生RejectedExecutionException)。ForkJoinPool线程池提供了execute()方法来异步启动任务,而作为任务本身,可以调用fork()方法异步启动新的子任务,并调用子任务的join()方法来取得计算结果。通过通过task.isDone()方法来判断任务是否结束。

public boolean allDone(List<SubTask> tasks) {
    for(SubTask task: tasks) {
        if(!task.isDone()) {
            return false;
        }
    }
    return true;
}

 

当使用Fork/Join框架时,如果主线程(main方法)先于task线程结束了,那么task线程也会结束,而不会等待执行完。这也是与Java中传统的Thread/Runnable有区别的地方。至于原因,应该是主线程(main方法)结束导致ForkJoinPool的守护线程结束了。此外,ForkJoinPoolawaitTermination方法也值得注意。execute/submit/fork/join也可以与invoke/invokeAll配合使用,来调整线程间的阻塞关系。

JDK 7 中的 Fork/Join 模式一文中,作者使用Fork/Join模式实现了并行快速排序算法,很值得参考。其实Fork/Join与Callable/Future/ExecutorService挺像的,ExecutorService与ForkJoinPool的API也很类似,ForkJoinPool比ExecutorService多出了Work-stealing(工作窃取)算法的调度,线程池和服务(Service)的概念对应地也能很好。在Fork/Join机制中,Task也是可以取消(cancel)的。

invoke与fork差异

这两个方法有很大的区别,当使用同步方法,调用这些方法(比如:invokeAll()方法)的任务将被阻塞,直到提交给线程池的任务完成它的执行。这允许ForkJoinPool类使用work-stealing算法,分配一个新的任务给正在执行睡眠任务的工作线程。反之,当使用异步方法(比如:fork()方法),这个任务将继续它的执行,所以ForkJoinPool类不能使用work-stealing算法来提高应用程序的性能。在这种情况下,只有当你调用join()或get()方法来等待任务的完成时,ForkJoinPool才能使用work-stealing算法。

Work-stealing

工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。核心思想在于以下两点:

  1. 将一个任务分解为多个互不依赖的子任务。
  2. 当一个线程完成自己队列中的任务后,将其他线程的任务队列里的任务取出来执行。

Work-stealing算法示意图

Disruptor机制

单核情形下提高CPU利用率。

参考

  1. Java 理论与实践: 正确使用 Volatile 变量
  2. 用happen-before规则重新审视DCL
  3. Java多线程之Condition接口的实现
  4. ReentrantLock(重入锁)以及公平性
  5. JDK 7 中的 Fork/Join 模式
  6. java 7 fork-join framework and closures
  7. 聊聊并发(八)——Fork/Join框架介绍
  8. Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制
  9. Fork and Join: Java Can Excel at Painless Parallel Programming Too!
  10. Java线程之fork/join框架
  11. Java中不同的并发实现的性能比较
  12. Java并发的四种风味:Thread、Executor、ForkJoin和Actor

via: http://sighingnow.github.io

解决IDEA使用jetty跑项目js、css文件被占用无法修改的问题

用IDEA开发web项目使用maven的jetty插件跑的时候经常遇到项目启动后,无法编辑js文件和css文件。
最初以为是Idea的问题,但是这么严重的一个问题怎么就没有人注意呢?
后来又上网查了好多资料,原来才发现不是IDEA的问题,是jetty本身的问题:原因是如果NIO被支持的话,Jetty会使用内存映射文件来缓存静态文件,其中包括.js文件。在Windows下面,使用内存映射文件会导致文件被锁定。
解决方案是不使用内存映射文件来做缓存。

到maven本地仓库的org/eclipse/jetty/jetty-webapp/下,找到对应版本的jetty插件修改webdefault.xml
将:

<init-param>
    <param-name>useFileMappedBuffer</param-name>
    <param-value>true</param-value>
</init-param>

 

改为:

<init-param>
    <param-name>useFileMappedBuffer</param-name>
    <param-value>false</param-value>
</init-param>

 

即可搞定!
也可以将此文件拷贝到项目中,在jetty插件配置中引入:

<plugin>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <version>7.5.1.v20110908</version>
  <configuration>
    <connectors>
      <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
        <port>8088</port>
        <maxIdleTime>60000</maxIdleTime>
      </connector>
    </connectors>
    <systemProperties>
      <systemProperty>
        <name>org.eclipse.jetty.util.URI.charset</name>
        <value>UTF-8</value>
      </systemProperty>
    </systemProperties>
    <webAppConfig>
      <defaultsDescriptor>src/main/resources/webdefault.xml</defaultsDescriptor>
    </webAppConfig>
  </configuration>
</plugin>

 

OWNER:Java配置文件解决方案

OWNER是一个Java库,目标是最大限度的减少应用程序中处理Java properties的代码。

自动草稿

主要功能

OWNER的设计遵循以下座右铭: 功能丰富的API,新功能添加的同时不忘保持极简的基本用法。

  • 加载策略:OWNER通过匹配接口类名和properties文件名自动解析并映射;也可以通过注解定制properties文件名。
  • 导入properties:另外一种加载properties文件到映射接口的方法。
  • 参数化properties:另外一个实用功能,给接口方法提供参数,通过参数配置。
  • 类型转换:支持从String类型到基本类型和枚举类型的转换。
  • 变量扩展:引用properties中的其他属性。
  • 热加载:支持热加载。
  • 可访问和可变:可以继承Config的子接口Accessible或者Mutable实现属性的可访问和可变。
  • 调试:支持调试功能。
  • 禁用功能:可禁用引起问题的功能。
  • 配置ConfigFactory:ConfigFactory也是可配置的。
  • XML支持:支持XML配置。
  • 事件支持:OWNER实现了功能丰富的事件系统,使你知道热加载的发生和属性变化。
  • 单例模式:配置信息在一个应用中是单例的。

官方文档

官方网站:http://owner.aeonbits.org/
开源地址:https://github.com/lviggiano/owner