Jakarta Commons 笔记,Part I

Chapter #05: Collections

java.util.*部分的UML类图:

关于遍历。LoopingIterator, ArrayListIterator, ResettableIterator。对于数组(非collections),有 ArrayIterator,ObjectArrayIterator。

关于遍历collection时有条件的筛选。FilterIterator+Predicate:
Iterator it = new FilterIterator(sampleCollection.iterator, new XxxPredicate());
while (it.hasNext()) { …… }

基于上述条件对collections进行操作:
CollectionUtils.filter(sampleCollection, new XxxPredicate())
这会改变sampleCollection的内容
相关的一些方法有:CollectionUtils.select, CollectionUtils.selectRejected

遍历时跳过重复,使用:UniqueFilterIterator

commons-collection 提供了 Bag 的概念。它着重于 有多少个 某件这一概念。
interface org.apache.commons.collections.Bag 继续了 java.util.Collection,本身有 HashBag 和 TreeBag 的实现。但是它的 removeAll, containsAll, add, remove, retainAll 方法不严格遵循 Collection 的约定。

关于 org.apache.commons.collections.Buffer,已在 Java5 里由 java.util.Queue 基本取代
但是我没发现有 BoundedFifoBuffer 的类似实现
创建优先队列,PriortyBuffer/PriorityQueue,两者都是带上一个 Comparator 。

“阻塞式”是一个比较有意思的概念,作用是有活了马上开始干。见代码:

public class Blocking implements Runnable {
//    Queue queue = new LinkedBlockingDeque();
    Buffer queue = BlockingBuffer.decorate(new BoundedFifoBuffer());
 
    @SuppressWarnings({ "static-access", "unchecked" })
    public static void main(String[] args) throws InterruptedException {
        Blocking ths = new Blocking();
        Thread t = new Thread(ths);
        t.start();
        ths.queue.add("Hello");
        t.sleep(500);
        ths.queue.add("Black Lee");
    }
 
    public void run() {
        while (true) {
            try {
                String msg = (String) queue.remove();
                System.out.println(msg);
            } catch (Exception e) {
            }
        }
    }
}
中间注释掉的那行,也是可以使用的。

MultiMap,同一个键存多个值,不觉得这有神马用途。。我会把这多个值放在List里面去,再塞进Map。
BidiMap,比较有意思。可以通过键访问值,也可通过值访问键:DualHashBidiMap, DualTreeBidiMap, TreeBidiMap。(书上说只有这三个实现,我一看JavaDoc,有10来个实现。。)
CaseInsensitiveMap可以让用String作为键的Map不关心大小写。
书中介绍的特定类型的Collection可以不看,Java5的泛型已搞定。

限制Map的值:Map map = PredicatedMap.decorate(new HashMap(), keyPredicate, valuePredicate).
限制List的值:List list = PredicatedList.decorate(new ArrayList(), objectPredicate);
类似的限制:PredicatedCollection, PredicatedSet…

转换Collection里面的所有元素:
CollectionUtils.transform(sampleCollection, new org.apache.commons.collections.Transformer());复杂点的话,用ChainedTransformer完成。

LRU缓存(Least Recently Used)最近最少使用:Map map = new LRUMap(5);
LazyMap是个很强大的工具:Map map = LazyMap.decorate(sampleMap, new Transformer()),在取值时,若key不存在,Transformer会根据key去取值,然后再返回。

统计Collection中对象的出现次数
CollectionUtils.countMatches(sampleCollection, new Predicate()),它用Predicate去统计。

集合间操作
CollectionUtils.union(a, b):A+B
CollectionUtils.intersecion(a, b):A且B
CollectionUtils.disjunction(a, b):(A+B)-(A且B)
CollectionUtils.subtraction(a, b):A-B

MapUtils中的autobox:

Map map = new HashMap();
int j = MapUtils.getIntValue(map, "a");
System.out.println(j);
int i = map.get("a");
System.out.println(i);

结果:j=0,取i时抛空指针异常

Chapter #06 XML
这一章不想看太多,只想把简单的XML读和写搞定就了事,毕竟这仅仅是个工具而已。
读取用disgest封装到对象:
xml文件内容:

<users>
  <user id="1">
    <name>Black Lee</name>
  </user>
  <user id="2">
    <name>Master</name>
  </user>
</users>

对方的Java类:

public class User {
    private int id;
    private String name;
    public User() {}
    public User(int i, String s) {
        id = i; name = s;
    }
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String toString() {
        return "User#" + id + ", Name:" + name;
    }
}

再整一个描述文件:

<digester -rules>
	<pattern value="users/user">
		<object -create-rule classname="xml.User"/>
		<set -next-rule methodname="add" paramtype="java.lang.Object"/>
		<set -properties-rule/>
		<bean -property-setter-rule pattern="name"/>
	</pattern>
</digester>

最后是Java读取类:

public static void main(String[] args) throws IOException, SAXException {
    List users = new ArrayList();
    DigesterDemo demo = new DigesterDemo();
    URL rule = demo.getClass().getResource("./rule.xml");
    Digester digester = DigesterLoader.createDigester(rule);
    digester.push(users);
    InputStream xml = demo.getClass().getResourceAsStream("./users.xml");
    Object root = digester.parse(xml);
    System.out.println(root);
    System.out.println(users.get(0));
}

运行结果:
[User#1, Name:Black Lee, User#2, Name:Master]
User#1, Name:Black Lee

至于书里还介绍的,怎么处理命名空间的那些,就不操练了。

写回去,用的是commons-betwixt。
给User.java旁边加一个文件,user.betwixt:

<info primitiveType="attribute">
  <element name="user">
    <attribute name="id" property="id"/>
    <adddefaults />
  </element>
</info>

写回去的Java代码:

public static void main(String[] args) throws IOException, SAXException, IntrospectionException {
    User u1 = new User(1, "Black Lee");
    User u2 = new User(2, "Master");
    BeanWriter writer = new BeanWriter();
    writer.enablePrettyPrint();
 
    BindingConfiguration cfg = new BindingConfiguration();
    cfg.setMapIDs(false);
    writer.setBindingConfiguration(cfg);
 
    List users = new ArrayList();
    users.add(u1); users.add(u2);
    writer.write("users", users); // 指定root node的名称
    writer.flush();
    System.out.println(writer.toString());
}

控制台输出:

  <users>
    <user id="1">
      <name>Black Lee</name>
    </user>
    <user id="2">
      <name>Master</name>
    </user>
  </users>
org.apache.commons.betwixt.io.BeanWriter@1430b5c

Chapter#07 应用程序基础设施

  • 处理命令行输入,用 Commons CLI(Command Line Interface),主要用CommandLineParser。
  • 处理配置文件,用Commons Configuration,晕,现在这一块好像都交给Spring做了,自己已经很少写了,略过。
  • 处理日志都交给Log4j,也不用管太多了,log4j.properties基本是一份通用,只不过有时候需要指定不同的输出而已,而且现在也是多用文件来存储,JDBCAppender似乎极少使用。

Chapter#08 Math
这一章讲的,多是数学计算类的,通读了一遍,感觉平常会用到的非常少,大致的记在脑子里就是了。
不过这个《估计程序的剩余处理时间》还是比较有趣的。用的是org.apache.commons.lang.time.StopMatch+org.apache.commons.math.stat.multivariate.SimpleRegression联合处理。

Chapter#08 模板
其实主要是介绍Velocity和FreeMarker的,这两个的应用场景大多是代替JSP成为视图层的解决方案。
VM我没用过,FM倒是用了相当一段时间,不过书里也没介绍得很深入,毕竟那需要更多的章节。略过。

Chapter#09 I/O与网络
大概是看到后面,想记录的东西就越来越少了。

  • 关于从Input到Output的操作,用CopyUtils.copy方法。或者用FileUtils.copyFile(src, dest),及FileUtils.xxxxxxToFile()方法。
  • 关于各个对象的close操作,用IOUtils.closeQuietly方法,只是觉得这个方法挺怪的,它接收InputStream/OutputStream/Reader/Writer四个类型的对象作为参数,直接把参数换成Closeable不是更好?这个方法要是换成closeAllQuietly(Closeable…tobeClosed)的话,还能多行变一行,更简洁。
  • FileUtils.byteCountToDisplaySize方法有点类似于linux下的“ls -lh”,不过没那个强大,不会输出小数点。
  • 删除目录:FileUtils.deleteDirectory,清除目录:FileUtils.clearDirectory,目录大小:FileUtils.sizeOfDirectory
  • 文件touch操作(没用过),FileUtils.touch(File f)
  • 选择文件:File.listFiles方法接收FileFilter和FilenameFilter,commons-io提供了这几个Filter:SuffixFileFilter,PrefixFileFilter,NameFileFilter,DirectoryFileFilter,还可以用(And/Or/Not)FileFilter来组合前述的各个Filter。这些Filter都是org.apache.commons.io.filefilter.IOFileFilter的实现,这个类实现了java.io.FileFilter和java.io.FilenameFilter这两个接口。
  • 计算流量:Counting(Input/Output)Stream提供的getCount方法。
  • 用new TeeOutputStream(output1, output2).write(xxx)来实现同时输出两份。
  • 更牛逼的FileFilenameFilter:new GlobFilenameFilter(“*.xml”),new Perl5FilenameFilter(“regex”)。
  • 网络相关的:FTPClient,SMTPClient,POP3Client。

Chapter#11 HTTP和WebDAV
HttpClient平常用得很多,几个点过一遍,没什么好记的。
整了一个Google登录的代码,有点儿小奇怪,在注释掉的setFollowRedirects(true)那里,不注释的话会抛异常。。

    public static void googleLogin(HttpClient client, NameValuePair[] nvps) throws IOException,
        HttpException {
    String uri = "https://www.google.com/accounts/ServiceLoginAuth";
    HttpMethod method = new PostMethod(uri);
    for (int i = 0; i < nvps.length; i++) {
        NameValuePair nvp = nvps[i];
        if (nvp.getName().equalsIgnoreCase("email")) nvp.setValue("xxxxxx@gmail.com");
        if (nvp.getName().equalsIgnoreCase("passwd")) nvp.setValue("xxxxxx");
    }
    method.setQueryString(nvps);
//  method.setFollowRedirects(true);// 应该是Post请求不能直接进行重定向
    int status = client.executeMethod(method);
    System.out.println(status);
    if (status == 302) {
        Header loc = method.getResponseHeader("location");
        System.out.println(loc);
        method.releaseConnection();
        method = new GetMethod(loc.getValue());
        method.setFollowRedirects(true);
        status = client.executeMethod(method);
        System.out.println("Goto: " + loc.getValue() + ", Get:" + status);
        System.out.println(method.getResponseBodyAsString());
    }
    if (status == 200) System.out.println(method.getResponseBodyAsString());
    }
 
    public static NameValuePair[] googleGetLoginPage(HttpClient client) throws IOException, HttpException, Exception {
    String uri = "https://www.google.com/accounts/ServiceLoginAuth";
    HttpMethod method = new GetMethod(uri);
    int status = client.executeMethod(method);
    System.out.println(status);
    HttpState state = client.getState();
    Cookie[] cookies = state.getCookies();
    for (Cookie cookie : cookies) {
        System.out.println("Set-Cookie: " + cookie.getName() + "\t" + cookie.getValue());
    }
    Header[] headers = method.getResponseHeaders();
    for (Header header : headers) {
        System.out.println("Header: " + header.getName() + "\t" + header.getValue());
    }
    HtmlCleaner cleaner = new HtmlCleaner();
    TagNode html = cleaner.clean(method.getResponseBodyAsStream());
    TagNode form = (TagNode) (html.evaluateXPath("//form[@id='gaia_loginform']")[0]);
    Object[] objs = form.evaluateXPath("//input");
    NameValuePair[] nvps = new NameValuePair[objs.length];
    for (int i = 0; i < objs.length; i++) {
        TagNode node = (TagNode) objs[i];
        nvps[i] = new NameValuePair(node.getAttributeByName("name"), node.getAttributeByName("value"));
    }
    return nvps;
}

日志打印出来,后续其实有两次重定向:

[DEBUG] header - &gt;&gt; "POST /accounts/ServiceLoginAuth?dsh=7609767927588628425&amp;GALX=ldpKR85s3P0&amp;Email=xxxxxx%40gmail.com&amp;Passwd=xxxxx&amp;PersistentCookie=yes&amp;rmShown=1&amp;signIn=Sign+in&amp;asts= HTTP/1.1[\r][\n]"
[DEBUG] header - &gt;&gt; "User-Agent: Jakarta Commons-HttpClient/3.1[\r][\n]"
[DEBUG] header - &gt;&gt; "Host: www.google.com[\r][\n]"
[DEBUG] header - &gt;&gt; "Cookie: $Version=0; GALX=ldpKR85s3P0; $Path=/accounts[\r][\n]"
[DEBUG] header - &gt;&gt; "Content-Length: 0[\r][\n]"
[DEBUG] header - &gt;&gt; "[\r][\n]"
[DEBUG] header - &lt;&lt; "HTTP/1.1 302 Moved Temporarily[\r][\n]"
[DEBUG] header - &lt;&lt; "HTTP/1.1 302 Moved Temporarily[\r][\n]"
[DEBUG] header - &lt;&lt; "Content-Type: text/html; charset=UTF-8[\r][\n]"

具体的路径是
1), POST /accounts/ServiceLoginAuth,这个是首次登录提交时POST的用户名密码等
2), GET /accounts/CheckCookie?chtml=LoginDoneHtml
3), GET m/accounts/ManageAccount
想了想,错误会发生,应该是POST请求不能直接重定向。

WebDAV没用过,书上介绍的也太过简陋,略过。。。

Chapter#12 搜索和筛选
用JXPath筛选Collection里的Java对象,这个蛮新鲜,不过有XPath的基础,表象看起来挺简单的:
String xpath = "someObject/sampleList[name='wtf']";
跟取xml的xpath没什么两样。

这本书看完了。

anyShare分享到:
          

没准儿您会对以下内容感兴趣: