- 通常在使用 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 更优雅些。 - 在 XSLT 中声明变量可以用 <xsl:variable> 和 <xsl:param>,它们的区别是可以通过 <xsl:param> 从外部向 XSLT 文件传参数,除此之外,在 xslt 内部使用时这两者的用法基本是一样的。下面只以 <xsl:variable> 为例子,例子中的 xsl:variable 替换成 xsl:param 也是能 run 的。
<xsl:variable> 的基本用法是:1<xsl:variable name="username" select="'Initial'"/> <!-- 不写 select 则默认为 '' --> 2 3<xsl:variable name="username" select="'New Value'"/> <!-- 赋值 --> 4 5<xsl:value-of select="$username"/> <!-- 显示变量值,变量名前加上 $ 符号 -->
Read More
紧接前一篇 应用 Castor 把 XML 转换成相应的 JavaBean(一),本例稍显复杂一些,也就是要使用到映射文件,更复杂的 JavaBean、XML 数据类型和结构,引入自定的 FieldHandler,还有存在 Namespace 的情形。
关于如何使用 Castor 来把 XML 映射成 JavaBean 请着重阅读这个链接 http://www.castor.org/reference/html/XML%20data%20binding.html 中的内容,里面有说明支持的类型,如何定义自己的类型 Handler 及配置 Handler 的属性,mapping.xml 文件怎么写及各部份的意义;还有 castor.properties 的配置,比其中的 suppressNamespaces 为 true 时可以忽略掉 Namespace,默认为 false.
直接看例子吧:
1. persons.xml,这里放了一个 Namespace,并定义了一个 Address 类型,还使得节点名与 Bean 的属性名不一致: Read More
之前有一篇用例子演示了 应用 JAXB 把 XML 转换成相应的 JavaBean,现在来看另一款 XML Data Binding 工具 Castor 怎么把 XML 映射成 JavaBean 的,相对于 JAXB 规范性的东西,Castor 的官方网站上关于 Castor 的使用文档我觉得要多些。作为一个 XML Data Binding 工具,Castor 同样提供了 Marshaller 和 Unmarshaller 的功能,它不依赖于注解,还是采用映射文件的方式,像 Hibernate 那样的映射文件。
从 Castor 的官方网站 http://www.castor.org/ 看到它的外围生态系统还是不错的,有 JDO、Maven、Spring、Eclipse Plugin 的支持,也有众多组件用到了它。和其他类似工具一样,Castor 也提供了 XML、Schema 生成 Class 类的功能,但照例这里还是看看先有类的情况,怎么从 XML 中获取相应值。
通常使用 Castor 从 XML 得到 JavaBean 需要一个映射文件,要是 JavaBean 的属性和 XML 中的节点名足够齐整规范就可以省去映射文件,先看不用映射文件的例子: Read More- 跨系统的调用目前无疑是 WebService 的天下,指的是通过 HTTP 请求方式获得 XML 或 JSON 数据的方式,RESTFul 也得到了很好的应用。规范意义上的 Soap 调用不知道还不多不多,反正当年用过的 Corba 鲜有耳闻了,就像很多人对 EJB2 不会有概念一样。
Java 在调用 WebService 获得了 XML 之后,接下来一种常见的处理方式就是把它转换成相应的 JavaBean,再丢给其他组件像 Jsp 标签,FreeMarker 等去就很流畅了。关于 XML 与 JavaBean 互相转换有两个操作叫做:Marshaller 和 Unmarshaller,还没见一个权威的翻译,大概就是编组与反编组,意义如同序列化与反序列化。也就是由 JavaBean 到 XML 叫做 Marshal,由 XML 到 Java 叫做 Unmarshal。
通常从 XML 到 JavaBean 的转换机会大的多,所以我主要也是研究了下如何把 XML 映射成 JavaBean,虽然现在的工具一样也都支持这两个方向的转换。有许多组件可以做到,像 JAXB(Java Architecture for XML Binding)、 Read More - Lucene 的 Field 在构造时内容参数可接受一个字符串或者字节数组,流等,不支持 Date 等类型。如果把 Date.toString() 转换成字符串来索引的话那么存储在索引中的样子就不确定(new Date().toString() 为 “Tue Mar 15 13:25:41 GMT+08:00 2011”),也给下次还原来为 Date 类型带来不便。在以前的版本 Lucene 专门提供了 DateField 来索引 Date 类型,但
现在不推荐使用了,而且也无法用,因为 Lucene 的 Document 在 add(Fieldable field) 时接受的是一个 Fieldable 类,然而 DateField 并未实现 Fieldable 接口,保留它是用来读取老的索引文件。
那 Lucene 要怎么索引日期类型呢?说到底,还是把它转换为字符串,搜索的时候从存储的字符串还原出来,辅助工具类是 DateTools, 它所提供的方法: Read More - Java 的泛型实例在声明时有点傻,比如像这样的语句 List<String> list = new ArrayList<String>(); 前明的 List<String> 已经提供了足够的信息让编译器知道 ArrayList 的参数类型,当然这是个简单的例子,如果复杂些,来点嵌套的话:
Map<String, List<Map<String, String>>> myMap = new HashMap<String, List<Map<String,String>>>();
那就够呛的,所以 Java 的这一语法要求也让 Scala 这样的语言所诟病,当然人家 Scala 是个趋近于动态性的语言,它认为上面的语句应该这么写:
val x = new HashMap[String, List[String, List[Map[String, String]]]() 或者是
val x: Map[String, List[String, List[Map[String, String]]] = new HashMap()
总之,只要一端的表态就行,其他事情交给编译器来推断。现在 JDK 也反省到了这一点,因为引入一个叫做菱形操作符(Diamond operator) 的东西,即两个尖括号 <>。让使得你像 Scala 一样只要在左边声明参数类型就行啦。这个操作符不免让我想起了在 perl 里有个 <=> 这样的操作符,好像叫做飞机,不是斗地主的飞机,它相当于 Java 里的 compareTo() 的功能。
于是泛型实例的声明就简单化成了 Map<String, List<Map<String, String>>> myMap = new HashMap<>(); 继续往下看: Read More