- javadoc 可为我们的 Java 项目生成 API 文档,别人的应该是看得多了,自己的可能不好意思晾出来看。那 Java 源代码里的 @author, @see, @param 等应该是司空见惯了吧。除此之外我们还可以自定义自己的 tag,并让它们的内容按照我们需要的格式生成到 javadoc 文档中,或作他用。还记得没有 Maven 的时代我们是怎样用 XDoclet 生映射文件的吗?现在的 Taglet 定制想要做的事情大抵如此。
执行一下 javadoc 命令看看,一堆的参数可以指定,又有学问在里头,且看:
-tag <name>:<locations>:<header> Specify single argument custom tags
-taglet The fully qualified name of Taglet to register
-tagletpath The path to Taglets
和
-doclet <class> Generate output via alternate doclet
-docletpath <path> Specify where to find doclet class files
关于 doclet 部份这儿暂且不说,单讲 tag 部分的东西。
对于自定义 tag,简单的时候,用参数 -tag 都可以不写自己的 taglet 类,例如有这样一个代码: Read More - 寻求了很久关于如何在 Java 中实现多行字符串,即 Here Document。因为在测试中准备大的字符串数据是不得不用加号去拼接,甚至是麻烦。稍好就是用 http://www.htmlescape.net/javaescape_tool.html 把你输入的大段文字生成 Java 的字符串。
找过一些介绍 Java 实现 Here Document 的方法,首先大家无一不是把这个多行字符串塞在注释里,有些实现在运行还在依赖于 Java 源文件中的注释,这不太可取。聪明的做法应该要去打编译器的主意,让编译后体现在 Class 文件中,变量就被赋上了多行字符串值,这就是 JDK1.5 引入的 APT(Annotation Processing Tool),到 JDK1.6 后可操作性更强了,可以 javac 的时候带上 -processor 参数。
单单从语法特性上来讲,我觉得 Java 与现今流行的语言还是有差距,不过它一直在成长,像 JDK 1.5 和 1.7 这两个版本就带来了不少好东西。想要见识一下其他些个语言,如 Perl, PHP, Ruby, C++11 怎么实现 Here Document 还是请看 http://en.wikipedia.org/wiki/Here_document。
就连 Java 最亲密的战友 C# 都早实现了 Here Document,用 @ 符号: Read More - Java 的 Properties 加载属性文件后是无法保证输出的顺序与文件中一致的,因为 Properties 是继承自 Hashtable 的, key/value 都是直接存在 Hashtable 中的,而 Hashtable 是不保证进出顺序的。
总有时候会有关心顺序一致的需求,恰如有 org.apache.commons.collections.OrderdMap(其实用 LinkedHashMap 就是保证顺序) 一样,我们也想要有个 OrderdProperties。网上查了下还真有:
http://livedocs.adobe.com/jrun/4/javadocs/jrunx/util/OrderedProperties.html
http://www.openrdf.org/doc/alibaba/2.0-rc4/apidocs/org/openrdf/repository/object/composition/helpers/OrderedProperties.html
不过没找到源码,其实自己写一个 OrderedProperties 也不难,并不需要重头写起,只要继承自 Properties,覆盖原来的 put/keys,keySet,stringPropertyNames 即可,其中用一个 LinkedHashSet 来保存它的所有 key。完整代码如下: Read More - Tomcat 下日志文件 catalina.out 过大,几百兆或几个G,进而造成再也无法写入更多的日志内容,至使 Tomcat 无法处理请求。需然你可以在 $TOMCAT_HOME/logs 目录下看到有 catalina.2012-09-16.log 这样分日期的归档,但是主文件 catalina.out 却一直在膨胀。
你可以每次手工或定时(crontab/计划任务)清理 catalina.out 文件,或是规划好日志输出(终究也会有满的时候)。再就是有两种较好的解决方案去真正的切分 catalina.out 文件,让 catalina.out 只存有最新的日志。
一. 改用 Log4J 来输出 Tomcat 日志,借助 Log4J 的各种日志切分的功能。详情可参考: http://baalwolf.iteye.com/blog/1464093, http://tomcat.apache.org/tomcat-6.0-doc/logging.html。
二. Linux 下使用 cronolog 工具来切分 catalina.out
这里重点介绍这种方法,具体步骤如下: Read More - 前面通过 在 Java 中使用正则表达式进行后向引用($1,$2...) 讲了在 Java 中使用 JavaScript 和 JRegex 来实现正则表达式的后向分组引用 $1, $2 替换。
自 JDK 1.4 出现正则表达式以来我还真不知道 Java 的正则表达式是可以在替换时用 $1, $2 达成后向分组替换的,所以前一篇表述有些出入的。也就是要实现 JavaScript 中的1<script> 2 var src = "abc def"; 3 var des = src.replace(/(\w+)\s+(\w+)/, "$2 $1"); 4 document.write(des); 5 //输出为: def abc 6</script>
的功能,完全可以不求助于第三方的正则表达式组件库或是通过 ScriptEngine + JavaScript 来实现,在 Java 字符串的:
public String replaceFirst(String regex, String replacement)
public String replaceAll(String regex, String replacement)
两方法的第三个参数中是可以用 $1, $2 ... 来引用第一个参数的括号分组的,简单 Java 示例代码如下:1"abc def".replaceFirst("(\\w+)\\s+(\\w+)", "$2 $1"); //结果为 def abc 2"abc def aaa bbb".replaceAll("(\\w+)\\s+(\\w+)", "$2 $1"); //结果是 def abc bbb aaa
Java 的正则表达式原本还是很强大的,只怕不能被人发现。
2015-07-14: 补充一点,在正则表达式外是用 $1, $2 ... 来进行后向引用,如果是在正则表达式中就需要用 \1, \2 ... 的形式来进后向引用。下面例子,替换重复出现的两位数及之间的内容1"xx12abcd12345".replaceAll("(\\d{2}).+?\\1", ""); //结果为 xx345 - ReflectASM 使用字节码生成的方式实现了更为高效的反射机制。执行时会生成一个存取类来 set/get 字段,访问方法或创建实例。一看到 ASM 就能领悟到 ReflectASM 会用字节码生成的方式,而不是依赖于 Java 本身的反射机制来实现的,所以它更快,并且避免了访问原始类型因自动装箱而产生的问题。
下面三个图是 ReflectASM 与 Java 自身反射机制的性能对比,表现很不错的。


测试代码包含在项目文件中. 上面图形是在 Oracle 的 Java 7u3, server VM 下测试出的结果。
下面我们自己来做个测试,测试环境是 Mac OS X 10.8, 2.4G Core 2 Duo, 4G RAM, 64 位 JDK 1.6. Read More - 通常在使用 AspectJ 时都是基于识别方法的规则来进行方法拦截,例如切片里这样写
@Pointcut("execution(* *..StockService.getBaseInfo(..))")它拦截到的是以 StockService 结尾的,方法名为 getBaseInfo,参数任意,返回值任意的方法。而我这里要说的一种方式是基于自定义注解来拦截方法的,此处的注解不是指 @Aspect, @Pointcut, 或 @Before 那一堆东西,而是指你可以自定义一个注解,如 @cc.unmi.testaspectj.MonitorMethod,被它所注解的方法即被拦截,像:
@cc.unmi.testaspectj.MonitorMethod
public void foo();
这可以给我们很大的自由度来快捷控制哪些方法需要被拦截,加个上面的注解 @MonitorMethod 即可,而不像从前那般要想像用什么规则去匹配某个方法,用 || 连接起来,同时还要防止影响到别的不期望被拦截的方法。
需要的代码并不多,四步,创建自定义注解类 MonitorMethod, 需被拦截的方法加上 @MonitorMethod,方面类,测试类。 Read More - 在网页的 Document 中如果想要获得某个结点的 innerHTML 就行,而在 Java 处理 XML 文档时想要获得某个节点的的 innerXML 就没那么简单的,标准的 org.w3c.Element 和 org.w3c.Node 均未提供类似 innerXML 的方法。
幸好,我们常用的 dom4j 里的 org.dom4j.Node 有一个方法是 String asXML(),不过它的意义相当于是 outerXML,也就是说它返回的内容还包括节点本身。
比如有这么在个 XML 文档:1<BookList> 2 description of book 1 3 <Book id="1">Unmi</Book> 4</BookList>
Read More 本篇讲述如何在 JUnit 4 下正确测试异常,我会从 try..catch 的方式谈起,然后说到 @Test(expected=Exception.class), 最后论及 @Rules public ExpectedException 的实现方式,最终基本可确定用 @Rules 是最方便的。
我们在用 JUnit 测试方法异常的时候,最容易想到的办法就是用 try...catch 去捕获异常,需要断言以下几个条件:
1. 确实抛出的异常
2. 抛出异常的 Class 类型
3. 抛出异常的具体类型,一般检查异常的 message 属性中包含的字符串的断定所以常用的代码你可能会这么写:
1@Test 2public void passwordLengthLessThan6LettersThrowsException(){ 3 try{ 4 Password.validate("123"); 5 fail("No exception thrown."); 6 }catch(Exception ex){ 7 assertTrue(ex instanceof InvalidPasswordException); 8 assertTrue(ex.getMessage().contains("contains at least 6")); 9 } 10}这里被测试的方法是 Password.validate() 方法是否抛出了相应的异常,注意这里别漏 try 中的
fail("No Exception thrown.") Read More
- 自 JDK1.4 引入正则表达式的支持可称得上是次大改变,
可 Java 的正则表达式的能力还是很弱,别说和 Perl 比了,就是和 C# 和 JavaScript 的正则表达式比较来也逊色不少,不过现在 JVM 上有其他语言对正则表达式有所增强,像 Groovy, jRuby 和 Scala。
[修正一下] 第一句话的说法是有误的,其实 JDK 本身的正则表达式就支持替换时用 $1, $2 那样的后向分组引用,例如:
String s = "abc def".replaceAll("(\\w+)\\s+(\\w+)", "$2 $1"); //s 就是 "def abc",replaceFirst 也可以用 $1, $2 的替换。比如在 Java 中要实现正则表达式的分组,后向引用的方式进行替换挺复杂的,像 JavaScript 完成两个分组的调换:1<script> 2 var src = "abc def"; 3 var des = src.replace(/(\w+)\s+(\w+)/, "$2 $1"); 4 document.write(des); 5 //输出为: def abc 6</script>
在 Java 中可得用上 Pattern, Matcher,还要 find()/group() 等操作,于是我就会考虑在 Java 中完成类似的功能有没有曲线的方法,有的。你可以调用 JVM 上其他语言的功能,我这里用列了两种实现方式
JDK1.6 起可借助 ScriptEngineManager 使用 JavaScript 相应的函数,除此之外还可用第三方的库 JRegex,具体实现,请参见如下代码,还带了测试用例:1package cc.unmi.utils; 2import javax.script.Invocable; 3import javax.script.ScriptEngine; 4import javax.script.ScriptEngineManager; 5 6import jregex.Pattern; 7import jregex.Replacer; 8 9public class StringUtils { 10 public static String replaceStringByJS(String src, String jsRegex, String replacement) { 11 ScriptEngineManager manager = new ScriptEngineManager(); 12 ScriptEngine engine = manager.getEngineByName("JavaScript"); 13 String script = 14 "function rep(target, regex, replacement){\n" 15 + " var patt = new RegExp(regex,'g');\n" 16 + " return target.replace(patt, replacement);\n" 17 + "}"; 18 19 try { 20 engine.eval(script); 21 Invocable inv = (Invocable) engine; 22 String result = (String) inv.invokeFunction("rep", src, jsRegex, 23 replacement); 24 return result; 25 } catch (Exception e) { 26 throw new RuntimeException(e); 27 } 28 } 29 30 public static String replaceStringByJRegex(String src, String regex, String replacement) { 31 Pattern pattern = new Pattern(regex); 32 Replacer replacer = pattern.replacer(replacement); 33 String result = replacer.replace(src); 34 return result; 35 } 36 37 public static void main(String[] args) { 38 String link="<link url=\"http://unmi.cc\" >\"Home\"</link>" 39 +"<link url=\"http://unmi.cc/tag/java\" >\"Java\"</link>"; 40 String jsString = StringUtils.replaceStringByJS(link, 41 "<link\\s+url=\\\"(.+?)\\\".[^/]*?>\\\"(.+?)\\\"</link>", "<a href=\"$1\">$2</a>"); 42 System.out.println(jsString); 43 44 String jregexString = StringUtils.replaceStringByJRegex(link, 45 "<link\\s+url=\\\"(.+?)\\\".[^/]*?>\\\"(.+?)\\\"</link>", "<a href=\"$1\">$2</a>"); 46 System.out.println(jregexString); 47 } 48 49 /* 50 * 程序输出 51 * <a href="http://unmi.cc">Home</a><a href="http://unmi.cc/tag/java">Java</a> 52 * <a href="http://unmi.cc">Home</a><a href="http://unmi.cc/tag/java">Java</a> 53 */ 54}
起初我是用的第一种方法,后来找到了第二种方式,感觉用 JRegex 更优雅些。