编写更少量的代码:使用apache commons工具类库
Commons-configuration Commons-FileUpload Commons DbUtils Commons BeanUtils Commons CLI Commons Codec Commons Collections
Commons DBCP Commons HttpClient Commons IO Commons JXPath Commons Lang Commons Math Commons Net Commons Validator
在看项目代码的过程中你会发现某些代码完全可以直接用开源框架来减少代码量的,如一些带有util的工具类、一些常用的io操作等;
研究发现一般的应用程序每 1,000 行代码就包含 20 到 250 个 bug!这个度量被称作缺陷密度。因此可得出一个重要的结论:更少的代码意味着更少的缺陷。
个人认为在项目开发过程中最好能有这样的习惯:能用开源框架(开源框架基本都是众多程序员智慧的结晶,经得住考验)就尽量用,最大限度地减少编码量;即当编码处理一些业务逻辑时首先想想或找找有没相关的开源框架,有适合的就用。
重视Apache Commons
公司的各个应用下基本都有Apache Commons下的一些jar包,但在开发过程中习惯性用到这些工具包的童鞋可能比较少(经常使用到这些jar包的童鞋可以忽视),其实用好Apache Commons下工具集和帮助集可以减少好些编码量。 Apache Commons包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复编码。
commons-beanutils
commons-lang
commons-logging
commons-collections
commons-digester
commons-codec
commons-jxpath
- commons-configuration最新的版本是1.5,这个工具是用来帮助处理配置文件的,支持很多种存储方式
1. Properties files 2. XML documents 3. Property list files (.plist) 4. JNDI 5. JDBC Datasource 6. System properties 7. Applet parameters 8. Servlet parameters
最主要的作用是读取资源文件,每一种文件格式都有一个对应的类,如下
properties文件--PropertiesConfiguration类;
xml文件—XMLConfiguration;
.ini文件—INIConfiguration;
.plist文件—PropertyListConfiguration;
还可以从JNDI中读取properties—JNDIConfiguration;当然还可以使用system的properties--SystemConfiguration
- 用Properties读取配置文件
usergui.properties(放在类根路径下面):
colors.background = #FFFFFF colors.foreground = #000080 window.width = 500 window.height = 300 keys=cn,com,org,uk,edu,jp,hk
(1) 一般写法:
public static void readProperties() { InputStream in = null; try { in = new BufferedInputStream((new ClassPathResource( "usergui.properties")).getInputStream()); } catch (IOException e1) { e1.printStackTrace(); } Properties p = new Properties(); try { p.load(in); System.out.println(p.getProperty("colors.background")); } catch (IOException e) { } }
(2) 另一种ResourceBundle方式:
public static void readProperties() { Locale locale = Locale.getDefault(); ResourceBundlelocalResource = ResourceBundle.getBundle("usergui", locale); String value = localResource.getString("colors.background"); System.out.println("ResourceBundle: " + value); }
(3) 使用PropertiesConfiguration
public static void loadProperty() { try { PropertiesConfigurationconfig = new PropertiesConfiguration( "usergui.properties"); config.setProperty("colors.background", "#00000F");// 更改值 config.save(); config.save("usergui.backup.properties");// save a copy System.out.println(config.getProperty("colors.background")); System.out.println(config.getInt("window.width")); String[] keys = config.getStringArray("keys"); List key2 = config.getList("keys"); for (int i = 0; i < keys.length; i++) { System.out.println(keys[i]); } for (Object str : key2) { System.out.println(str.toString()); } } catch (Exception e) { } }
详细案例请参考 文件上传---普通文件和url文件
这个工具并不是现在流行的OR-Mapping工具(比如Hibernate),只是简化数据库操作,比如
QueryRunner run = new QueryRunner(dataSource);
// Execute the query and get the results back from the handler
Object[] result = (Object[]) run.query("SELECT * FROM Person WHERE name=?", "John Doe");
commons-dbutils是 Apache 组织提供的一个开源 JDBC 工具类库,对传统操作数据库的类进行二次封装,可以把结果集转化成List。
项目主页: http://commons.apache.org/dbutils/
文档地址: http://commons.apache.org/dbutils/examples.html
下载地址:http://commons.apache.org/downloads/download_dbutils.cgi
(1) org.apache.commons.dbutils
DbUtils : 提供如关闭连接、装载 JDBC 驱动程序等常规工作的工具类
QueryRunner : 该类简单化了 SQL 查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
QueryLoader : 属性文件加载器,主要用于加载属性文件中的 SQL 到内存中。
(2) org.apache.commons.dbutils.handlers
ArrayHandler:将ResultSet中第一行的数据转化成对象数组
ArrayListHandler将ResultSet中所有的数据转化成List,List中存放的是Object[]
BeanHandler:将ResultSet中第一行的数据转化成类对象
BeanListHandler:将ResultSet中所有的数据转化成List,List中存放的是类对象
ColumnListHandler:将ResultSet中某一列的数据存成List,List中存放的是Object对象
KeyedHandler:将ResultSet中存成映射,key为某一列对应为Map。Map中存放的是数据
MapHandler:将ResultSet中第一行的数据存成Map映射
MapListHandler:将ResultSet中所有的数据存成List。List中存放的是Map
ScalarHandler:将ResultSet中一条记录的其中某一列的数据存成Object
(3) org.apache.commons.dbutils.wrappers
SqlNullCheckedResultSet:该类是用来对sql语句执行完成之后的的数值进行null的替换。
StringTrimmedResultSet:去除ResultSet中中字段的左右空格。Trim()
- 例子
Commons BeanUtils是针对Bean的一个工具集。由于Bean往往是有一堆get和set组成,所以BeanUtils也是在此基础上进行一些包装。
使用示例:一个比较常用的功能是Bean Copy,也就是copy bean的属性。如果做分层架构开发的话就会用到,比如从PO(Persistent Object)拷贝数据到VO(Value Object)。
- 传统方法如下:
TeacherFormteacherForm=(TeacherForm)form; //得到TeacherForm Teacher teacher=new Teacher();//构造Teacher对象 //赋值 teacher.setName(teacherForm.getName()); teacher.setAge(teacherForm.getAge()); teacher.setGender(teacherForm.getGender()); teacher.setMajor(teacherForm.getMajor()); teacher.setDepartment(teacherForm.getDepartment()); //持久化Teacher对象到数据库 HibernateDAO= ; HibernateDAO.save(teacher);
使用BeanUtils后,代码就大大改观了,如下所示:
TeacherForm teacherForm=(TeacherForm)form; //得到TeacherForm Teacher teacher=new Teacher();//构造Teacher对象 BeanUtils.copyProperties(teacher,teacherForm); //赋值 HibernateDAO.save(teacher); //持久化Teacher对象到数据库
说明:这是一个处理命令的工具。比如main方法输入的string[]需要解析。你可以预先定义好参数的规则,然后就可以调用CLI来解析。
使用示例:
说明:这个工具是用来编码和解码的,包括Base64,URL,DES、SHA1、MD5、Soundx等等。
MD5
String str = "abc";
DigestUtils.md5Hex(str);
SHA1
String str = "abc";
DigestUtils.shaHex(str);
BASE64
//加密
String str= "abc"; // abc为要加密的字符串
byte[] b = Base64.encodeBase64(str.getBytes(), true);
System.out.println(new String(b));
//解密
String str = "YWJj"; // YWJj为要解密的字符串
byte[] b = Base64.decodeBase64(str.getBytes());
System.out.println(new String(b));
Commons Collections
说明:你可以把这个工具看成是java.util的扩展。
使用示例:举一个简单的例子
OrderedMap map = new LinkedMap();
map.put("FIVE", "5");
map.put("SIX", "6");
map.put("SEVEN", "7");
map.firstKey(); // returns "FIVE"
map.nextKey("FIVE"); // returns "SIX"
map.nextKey("SIX"); // returns "SEVEN"
说明:数据库连接池,类似的还有common-pool和c3p0等,具体以后再总结
详情请参考:HttpClient学习整理
说明:可以看成是java.io的扩展
使用示例:
- 读取Stream
a) 标准代码:
InputStream in = new URL( "http://jakarta.apache.org" ).openStream();
try {
InputStreamReaderinR = new InputStreamReader( in );
BufferedReaderbuf = new BufferedReader( inR );
String line;
while ( ( line = buf.readLine() ) != null ) {
System.out.println( line );
}
} finally {
in.close();
}
b) 使用IOUtils
InputStream in = new URL( "http://jakarta.apache.org" ).openStream();
try {
System.out.println( IOUtils.toString( in ) );
} finally {
IOUtils.closeQuietly(in);
}
- 读取文件
File file = new File("/commons/io/project.properties");
List lines = FileUtils.readLines(file, "UTF-8");
- 察看剩余空间
longfreeSpace = FileSystemUtils.freeSpace("C:/");
说明:那么JXpath就是基于Java对象的Xpath,也就是用Xpath对Java对象进行查询。
使用示例:
Addressaddress=(Address)JXPathContext.newContext(vendor).getValue("locations[address/zipCode=‘90210‘]/address");
上述代码等同于
Address address = null;
Collection locations = vendor.getLocations();
Iterator it = locations.iterator();
while (it.hasNext()){
Location location = (Location)it.next();
String zipCode = location.getAddress().getZipCode();
if (zipCode.equals("90210")){
address = location.getAddress();
break;
}
}
说明:这个工具包可以看成是对java.lang的扩展。提供了诸如StringUtils, StringEscapeUtils, RandomStringUtils, Tokenizer, WordUtils等工具类。
Lang.*下有很多Utils类,提供了若干static方法供调用,涵盖了字符串操作、字符操作、JVM交互操作、归类、异常和位域校验等等。
首先看看字符串处理类StringUtils
全部的静态方法,StringUtils继承自Object。属于null safe操作。何谓null safe,就是遇到是null的string对象,也会quietly的把它处理掉,而不会有NullPointerException异常,如果有这个异常,那么就是该类的bug了~~
看看StringUtils的各种方法对比吧
比较一下isEmpty和isBlank这两个方法。isEmpty在判断” “这样的字符串时返回的是false,而isBlank返回的是true。这就明显了吧,empty只对null和””有效,而blank对” ”也有效~~
Clean方法和trim方法都封装了String的trim方法,唯一的不同是clean将null处理为””,而trim将null处理为null。
以Strip开头的一系列方法基本可以看做trim方法的扩展,但是更强大的是可以处理前导和后续的各种stripChars,不局限于空白符了。
Equal系列的方法加入了null的判断,如果都是null那么也返回true。
IndexOf与String的indexOf类似,加入了null的判断而已,且多了一个ordinalIndexOf方法,可以找到第n个字符串出现的位置。还有一个indexOfIgnoreCase可以不考虑大小写的判断位置匹配。实际代码是调用了string的regionMatches方法,只不过在ignorecase参数上选择了true。
Contains的实现是一样的,只是加入了null的判断。剩下的indexOfAny和containsAny方法都很不错,完成了集合的匹配~~都包含一个字符数组的参数,可以检测字符串中是否包含字符数组中的任意个元素,算法没有特殊的地方,复杂度O(MN)吧~~相比较而言,contains系列的containsOnly和containsNone我倒是觉得更有用一些。
Substring系列的方法,除了substring封装了string的substring完成了null safe的检查外,还加入了left、right和mid方法。顾名思义,left可以得到最左边的若干个字符作为字串,其实就是调用了str.substring(0, len);而right同样是调用了str.substring(str.length() - len);另外还有一些比如substringBefore和substringAfter之类的,用法类似。substringBetween可以找到两个字串中间的子串。
Split是值得说道的一个改动,原本大量封装string的方法,split是个例外,大家知道,string的split方法用到的参数是一个正则式,虽然强大,但是有时候容易出错。而且string并没有提供简化版本。StringUtils提供的split改变了这一状况,开始使用完整的字符串作为参数,而不是regex。同时,对类似功能的jdk版本的StringTokenizer,在内部方法splitWorker中有段注释:Direct code is quicker than StringTokenizer.也就是说,这个是更快的一个工具了~~
对于split的反向操作join,用到了一个lang.text包下的StrBuilder类。主要实现即将若干个object数组一个个的append到buffer中。然后最后toString就好。
Delete和remove可以用来删除string中的内容,比如deleteSpaces可以除去字符串中的所有空白字符(" "t"r"n"b");remove更强大,可以removeStart(只匹配开头)和removeEnd(只匹配结尾),当然remove可以删掉字符串中的任意字串。
Replace,同理这里的replace不像string中的一样是基于正则的,用起来更简单。而且有replaceOnce和replaceEach等各种用法。还有一个辅助的overlay方法,可以直接替换~~
Chomp和chop我觉得是比较鸡肋的一个功能了,去除字符串的最后一个字符或者尾部串,这功能很必要么?
Repeat将已有字符串复制n次。
Pad是一个很有意思的方法,可以将一个已有字符串扩展n个大小,并且填充特定的字符。比如StringUtils.rightPad("bat", 5, ‘z‘) = "batzz"。Pad调用了string的concat方法。
Case conversion的操作就不用多讲了,upper和lower都是一样的。补充说的是,capitalize系列的方法真的很贴心。
补充一个容易让人误会的方法——countMatches,记录一个字符串str中某串sub出现的次数。为什么容易误会,“aaaa”中有多少“aa”呢?用该方法得到的答案是2~~~大家懂的
Is打头的一系列方法都非常强大,可以判断字符串是否符合某种格式,比如isAlpha判断是否都是字母,isNumeric判断是否都是数字等等等等。
Reverse这个功能出来后,最先想到的是当初笔试面试时候的一堆回文字符串翻转之类的考试都要囧了。
Abbreviate方法我觉得是相当实用的一个方法封装,我们在各种应用中都很常见的“一堆文字……”就是这个方法的最好应用。
Difference方法返回两个字符串的不同处,可以说是返回第二个字符串的第一个不同的位置开始的子串。indexOfDifference返回不同处的位置。
getCommonPrefix这个方法也很好,可以找到一组字符串的公共前缀。当然只是调用了indexOfDifference这个方法。
接着就是ArrayUtils了
ArrayUtils是一个对数组进行特殊处理的类。当然jdk中的Arrays是有一些功能的,Array也提供了一些动态访问java数组的方法,这里的ArrayUtils扩展提供了更多的功能。
第一个可以说的方法是toMap方法,该方法就是将一个二维数组转换为一个HashMap。当然对输入的参数要求比较严格,会抛出各种异常。NullToEmpty方法是一个防御性编程方法的代表,将null的数组直接变为一个空(长度为0)的数组。Subarray方法提供了基于拷贝的子数组生成方法。Reverse方法提供了一个数组翻转的算法实现。indexOf方法是一个查找指定对象的线性数组查找方法。还提供了一系列装箱拆箱方法(toPrimitive和toObject),就是将Integer之类的对象类型变成int,反之亦然。addAll方法提供了基于数组拷贝的数组合并,就是将数组1和数组2合并为一个新的数组返回。当然,add方法虽然只添加了一个元素,但是也是要数组拷贝的(数组的效率啊!!!)。同样的原理,remove(删除)方法也是基于数组拷贝的,以指定删除元素为界,分别两次拷贝它前后的子数组。
再来就是一些补充了
把一些看到的有意思的可能有用的接口方法提取出来。
RandomStringUtils类里有一个random方法,可以产生一个固定长度的随机字符串。用到了java.util.Random。其中的注释中提到了对Unicode中没法处理的surrogate的处理方法。如果不幸随机到那个位置(D800-DBFF, DC00-DFFF),那么算法中将进行count补充,即提供一次重新随机的机会。
另外一个比较有趣的类是StopWatch,这是一个秒表类,通过start方法开始计时,通过split方法截断每一次的分段计时,suspend方法可以暂停秒表,resume恢复计时。最后stop后可以通过getTime获得总共计时。当然在split后的分段计时可以用getSplitTime获取。技术实现上就是定义了几个状态,然后通过每次状态的转变和系统时间的差来表达计时效果。
说明:这个包提供的功能有些和Commons Lang重复了,但是这个包更专注于做数学工具,功能更强大。
Math 是一个轻量的,自包含的数学和统计组件,解决了许多非常通用但没有及时出现在Java标准语言中的实践问题.
我们可以参考其主页上的用户指导(User Guide ).或者直接研究其API,发现在commons-math中有以下几个包:
1.org.apache.commons.math Common classes used throughout the commons-math library.
2.org.apache.commons.math.analysis Implementations of common numerical analysis procedures, including root finding and function interpolation. 主要解决一些数学通常的分析方法,包括求方程的根,其中有对分算法,牛顿算法等等.以及函数的改写.
3.org.apache.commons.math.complex Complex number type and implementations of complex transcendental functions. 主要用来解决复数的计算.
4.org.apache.commons.math.distribution Implementations of common discrete and continuous distributions. 主要用来解决连续分布和不连续分布的计算.
5.org.apache.commons.math.fraction Fraction number type and fraction number formatting.主要讨论分数的格式和类型.
6.org.apache.commons.math.linear Linear algebra support. 线性代数中矩阵和行列式的算法.
7.org.apache.commons.math.random Random number and random data generators.随机数算法
8.org.apache.commons.math.special Implementations of special functions such as Beta and Gamma.一些特别的函数算法.
9.org.apache.commons.math.stat Data storage, manipulation and summary routines.
10.org.apache.commons.math.util Convenience routines and common data structures used throughout the commons-math library.
说明:这个包还是很实用的,封装了很多网络协议。
1. FTP
2. NNTP
3. SMTP
4. POP3
5. Telnet
6. TFTP
7. Finger
8. Whois
9. rexec/rcmd/rlogin
10. Time (rdate) and Daytime
11. Echo
12. Discard
13. NTP/SNTP
使用示例:
TelnetClient telnet = new TelnetClient();
telnet.connect( "192.168.1.99", 23 );
InputStream in = telnet.getInputStream();
PrintStream out = new PrintStream( telnet.getOutputStream() );
...
telnet.close();
说明:用来帮助进行验证的工具。比如验证Email字符串,日期字符串等是否合法。
使用示例:
DateValidator validator = DateValidator.getInstance();// Get the Date validator
Date fooDate = validator.validate(fooString, "dd/MM/yyyy");// Validate/Convert the date
if (fooDate == null) {
// error...not a valid date
return;
}
Commons Virtual File System
说明:提供对各种资源的访问接口。支持的资源类型包括
1. CIFS
2. FTP
3. Local Files
4. HTTP and HTTPS
5. SFTP
6. Temporary Files
7. WebDAV
8. Zip, Jar and Tar (uncompressed, tgz or tbz2)
9. gzip and bzip2
10. res
11. ram
这个包的功能很强大,极大的简化了程序对资源的访问。
使用示例:
- 从jar中读取文件
FileSystemManagerfsManager = VFS.getManager();// Locate the Jar file
FileObjectjarFile = fsManager.resolveFile( "jar:lib/aJarFile.jar" );
FileObject[] children = jarFile.getChildren();// List the children of the Jar file
System.out.println( "Children of " + jarFile.getName().getURI() );
for ( int i = 0; i <children.length; i++ )
{
System.out.println( children[ i ].getName().getBaseName() );
} - 从smb读取文件
StaticUserAuthenticatorauth = new StaticUserAuthenticator("username", "password", null);
FileSystemOptions opts = new FileSystemOptions();
DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(opts, auth);
FileObjectfo = VFS.getManager().resolveFile("smb://host/anyshare/dir", opts);
1、一个简单的文本邮件
我们的第一个例子是利用你本地的邮件服务器向"John Doe"发送一个基本邮件信息。
import org.apache.commons.mail.SimpleEmail;
...
SimpleEmail email = new SimpleEmail();
email.setHostName("mail.myserver.com");
email.addTo("jdoe@somewhere.org", "John Doe");
email.setFrom("me@apache.org", "Me");
email.setSubject("Test message");
email.setMsg("This is a simple test of commons-email");
email.send();
调用setHostName("mail.myserver.com")来设置发送信息的SMTP服务器地址。如果你不设置,将会使用系统属性"mail.host"。
2、发送带附件的邮件
为了向一个邮件添加附件,你需要使用MultiPartEmail这个类。这个类的工作方式和SimpleEmail类似,但是其重载了attach()方法使其可以向邮件添加附件。你可以通过附加或者内联来添加无限数量的附件,这些附件将采用MIME编码。
最简单的方式是使用EmailAttachment类引用你得附件来添加附件。
下面的例子我们将创建一个图片的附件。把图片附加在邮件中并发送它。
import org.apache.commons.mail.*;
...
// Create the attachment
EmailAttachment attachment = new EmailAttachment();
attachment.setPath("mypictures/john.jpg");
attachment.setDisposition(EmailAttachment.ATTACHMENT);
attachment.setDescription("Picture of John");
attachment.setName("John");
// Create the email message
MultiPartEmail email = new MultiPartEmail();
email.setHostName("mail.myserver.com");
email.addTo("jdoe@somewhere.org", "John Doe");
email.setFrom("me@apache.org", "Me");
email.setSubject("The picture");
email.setMsg("Here is the picture you wanted");
// add the attachment
email.attach(attachment);
// send the email
email.send();
你也可以使用EmailAttachment引用一个非本地的URL文件。当发送邮件时,URL文件会被自动下载下来附件在邮件中。
下面的例子演示如何把apache的徽标附件在邮件中发送给John 。
import org.apache.commons.mail.*;
...
// Create the attachment
EmailAttachment attachment = new EmailAttachment();
attachment.setURL(new URL("http://www.apache.org/images/asf_logo_wide.gif"));
attachment.setDisposition(EmailAttachment.ATTACHMENT);
attachment.setDescription("Apache logo");
attachment.setName("Apache logo");
// Create the email message
MultiPartEmail email = new MultiPartEmail();
email.setHostName("mail.myserver.com");
email.addTo("jdoe@somewhere.org", "John Doe");
email.setFrom("me@apache.org", "Me");
email.setSubject("The logo");
email.setMsg("Here is Apache‘s logo");
// add the attachment
email.attach(attachment);
// send the email
email.send();
3、发送HTML格式的邮件
发送HTML格式邮件要使用HtmlEmail类。这个类的工作方式很像使用附加方法设置html内容的MultiPartEmail类类似,如果接收者不支持HTML邮件将替换成普通的文本和内联图片。
在下面例子中,我们使用HTML格式内容和以俄国内联图像。
import org.apache.commons.mail.HtmlEmail;
...
// Create the email message
HtmlEmail email = new HtmlEmail();
email.setHostName("mail.myserver.com");
email.addTo("jdoe@somewhere.org", "John Doe");
email.setFrom("me@apache.org", "Me");
email.setSubject("Test email with inline image");
// embed the image and get the content id
URL url = new URL("http://www.apache.org/images/asf_logo_wide.gif");
String cid = email.embed(url, "Apache logo");
// set the html message
email.setHtmlMsg("<html>The apache logo - <img src=\"cid:"+cid+"\"></html>");
// set the alternative message
email.setTextMsg("Your email client does not support HTML messages");
// send the email
email.send();
首先,注意,调用embed函数返回一个字符串。这个字符串是一个随机的标识符用来引用一个图像标签中的图像。下一步,在这个例子中我们没有调用setMsg()函数。这个方法在HtmlEmail中仍然可用,但是如果你使用内联图像时就不应该使用,应该使用setHtmlMsg()函数和setTextMsg()函数。
4、调试
JavaMail API支持一个调试选项,如果你遇到了问题这个可能非常有用。你可以通过调用setDebug(true)来激活调试模式,调试信息将被输出到标准输出中。
5、验证
如果SMTP服务器需要验证,你可以在发送邮件前调用setAuthentication(userName,password)设置帐号和密码。这个将会创建一个DefaultAuthenticator实例,JavaMail API在发送邮件时会使用它。你的服务器必须支持RFC2554标准。