求解:String 转换 java.util.Date 抛 java.lang.IllegalArgumentillegalexceptionn

java调用存储过程的另类封装的开发日志(一) -
- ITeye技术网站
博客分类:
&&&&&&& 选择了存储过程,就意味着牺牲了可移植性和灵活性,换来的确是安全性和稳定性和高性能以及特定数据库的特性。不过事情往往都是相对的,正是由于plsql的灵活多变实现一个目标往往可以有很多种途径而导致说想要写出安全稳定高效并重的存储过程也不是一件容易的事。我个人觉得存储过程最方便的地方在于传统方式可能需要几次交互的信息,丢给存储过程一次就可以搞定。维护存储过程也比可能破坏设计模式和一致风格的java代码修改来说要容易的多。
&&&&&&& 调用存储过程的花样有很多种,比如传入参数可以是基本类型,对象类型,数组类型。传出参数亦然,而且甚至可以是空参数。于是封装这个过程就不是一件容易的事情,好在经过两个星期的改进我已经成功地封装了一套模版,在工作当中稳定地运行着,现在将整个设计思想和开发的过程记录下来,包括如何一步一步的思考然后还一步一步走的弯路。可能需要很多篇博客才能描写出来,希望起到一个抛砖引玉的作用,如果大家有什么好的思想和建议还望及时与我交流相互学习促进,只要有人支持我就会写下去。
&&&&&&& 在此之前找了一下网络上的资源,发现并没有符合我心中想法的框架,参考过springjdbc调用存储过程给了我一定的启发,但它可能考虑的东西太多导致让我觉得封装的程度还是不够理想特别对于复杂参数(对象,数组,对象数组)的传递以及多结果集的返回,我理想的是增删改查不管什么情况都是一句话调用。对于sql,hibernate已经可以做到了,但是存储过程似乎还没有,无奈只能自己动手了。
&&&&&&& 先有一张USERS表字段三个:ID(NUMBER),NAME(VARCHAR2),BIRTHDAY(TIMESMTP)。
&&&&&&& JAVA的实体BEAN
package test.
import java.util.D
public class User {
public int getId() {
public void setId(int id) {
public String getName() {
public void setName(String name) {
this.name =
public Date getBirthday() {
public void setBirthday(Date birthday) {
this.birthday =
这种设计是为了到时候测试基本类型而使用,我个人倾向于设计bean字段全用String,因为字符型是元字型,任何别的类型都可以由它转换而来,全字符型字段也方便我们写存储过程,为什么,以后说。扯远了。。
接着就是DAO和IMP了,接口就不写出来了,首先我们要封装的是最简单的一种情况,存储过程就一个传入参数基本类型,一个返回参数游标!
package test.bean.
import java.util.L
import test.bean.U
public class UserDaoImp implements UserDao{
public List&User& getUsers(String id) {
String sql="{CALL PKG_TEST.GETUSERS_FROM_CURSOR(?,?)}";
//这个ProcRunner类究竟是什么,现在先不关心,我们期望的效果就是这样
return (List&User&) new ProcRunner().execute(sql,User.class);
这是我们期待的封装效果,那么问题就来了,如果我们仅仅在sql中使用一个“?”我们怎么才能知道它是传入还是传出?于是这个sql还需要做一点点改进。
package test.bean.
import java.util.L
import test.bean.U
public class UserDaoImp implements UserDao{
public List&User& getUsers(int id) {
String sql="{CALL PKG_TEST.GETUSERS_FROM_CURSOR({1},{o})}";
return (List&User&) new ProcRunner().execute(sql,User.class);
之所以设计成{1},{2}...这种形式是因为假如这个getUsers方法参数是这样的
getUsers(int id,String name,String ...)我们就可以将多个参数设置进去,而一个
{o}代表着传出。什么?为什么不直接用 1,2,3,o?这是怕引起视觉混淆,当然这个问题其实不重要。
现在所有的重头戏就集中到了ProcRunner身上。根据我们的期望把他的架子搭出来
import java.sql.CallableS
import java.sql.C
import java.util.L
* 这个类的设计思路我觉得应该是这样子的。首先将我们自定义的sql解析出来
* 里面有我们指定的传入传入参数的设置和序列,它会根据我们这些符号做4件事
* 1:设置传入参数(如果需要)
* 2:注册传出参数(如果需要)
* 4:获取传出结果集放到集合中返回(List&bean&|null)
public class ProcRunner {
//在面对不需要返回值的情况下,我们就不用传递一个class让它装配了
//于是在这里设置了可变参数。
public List&?& execute(String sql,Class&?& ...clazz){
接下来还要考虑就是怎么样能够把
getUsers(int id)的参数注入到这个ProcRunner里面去?否则即便我知道了要讲第一个参数设置到这个地方,可这第一个参数是什么?这样我们就要动用动态代理来帮我们把方法的参数注入到ProcRunner里面去。动态代理能够很轻易的拿到getUsers方法的所有参数,它只需要找到ProcRunner的对象就可以注入进去,这样就意味着这个ProcRunner对象不能在getUsers里去申明了,但是在DaoImp里面申明,麻烦。直接做一个父类就好了将所有要用到的对象全部申明出来就好了。这样只需要让DaoImp去继承它,就可以直接使用了,至于初始化的任务就可以全权交给动态代理了
package test.bean.
public class ProxyObjects {
public ProcR
修改daoImp层代码
package test.bean.
import java.util.L
import test.bean.U
public class UserDaoImp extends ProxyObjects implements UserDao{
public List&User& getUsers(int id) {
String sql="{CALL PKG_TEST.GETUSERS_FROM_CURSOR({1},{o})}";
return (List&User&)pr.execute(sql,User.class);
修改ProcRunner,使之具有参数属性。
import java.sql.CallableS
import java.sql.C
import java.util.L
public class ProcRunner {
public Object[] getParas() {
public void setParas(Object[] paras) {
this.paras =
// 动态代理通过构造函数为paras赋值
public ProcRunner(Object[] paras) {
this.paras =
public List&?& execute(String sql, Class&?&... clazz) {
然后创建拦截器,在此我选用的是cglib作为动态代理,因为jdk的proxy只能对实现接口类代理,而cglib是以继承的方式实现无需接口,更灵活自由。但在使用上面大同小异。
拦截器如下
import java.lang.reflect.F
import java.lang.reflect.M
import net.sf.cglib.proxy.E
import net.sf.cglib.proxy.MethodI
import net.sf.cglib.proxy.MethodP
public class Interceptor implements MethodInterceptor {
public Object getInstance(Object target) {
this.target =
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Class&?& clazz=target.getClass().getSuperclass();
Field field_pr=clazz.getDeclaredField("pr");
ProcRunner pr=new ProcRunner(args);//创建对象并赋参数
field_pr.set(target, pr);//将建立的ProcRunner对象注入到target中
ret=method.invoke(target, args);
接下来从拦截器中创建daoimp就会被拦截器作用了,我确信参数已经被拦截器注入到了daoimp的procrunner对象pr中了。
现在所有的焦点就都集中到了ProcRunner身上了,他具有了我们要为存储过程设置的参数,还通过解析我们自定义的sql知道了这些参数设置的序列,实现它的思路就清晰了,还有一步准备工作就是怎么样把游标变成对象集合?完成这么一个工具类!
import java.lang.reflect.F
import java.sql.ResultS
import java.util.ArrayL
import java.util.L
public class BeanUtils {
public static List&Object& getBeans(ResultSet rs, Class&?& clazz)
throws Exception {
List&Object& ret = new ArrayList&Object&();
int attrsCount = rs.getMetaData().getColumnCount();// 游标有多少列
while (rs.next()) {
Object bean = clazz.newInstance();
for (int i = 1; i &= attrsC i++) {
// 每列给对应的属性赋值
String attr = rs.getMetaData().getColumnLabel(i);
Field field = clazz.getDeclaredField(attr);
field.setAccessible(true);
field.set(bean, rs.getObject(i));
ret.add(bean);
接下来就可以完整的写出ProcRunner类了,一气呵成完成它吧!
import java.sql.CallableS
import java.sql.C
import java.sql.ResultS
import java.sql.SQLE
import java.util.ArrayL
import java.util.I
import java.util.LinkedHashM
import java.util.L
import java.util.M
import oracle.jdbc.OracleT
import db.until.DbHelperJ
import mon.utils.BeanUtils.*;
public class ProcRunner {
enum ParamType{
CURSOR,ARRAY;
Object[]//DaoImp方法上面的参数
public Object[] getParas() {
public void setParas(Object[] paras) {
this.paras =
// 动态代理通过构造函数为paras赋值
public ProcRunner(Object[] paras) {
this.paras =
* 这个类的设计思路我觉得应该是这样子的。首先将我们自定义的sql解析出来 里面有我们指定的传入传入参数的设置和序列,它会根据我们这些符号做三件事
* 1:设置传入参数(如果需要) 2:注册传出参数(如果需要) 3:执行 4:获取传出结果集放到集合中返回(List&bean&|null)
public List&?& execute(String sql, Class&?&... clazz) throws SQLException {
List&?& ret =// 最终返回
// 注册参数需要在执行之后获取,所以需要一个集合来存放注册的序号和类型
Map&Integer, ParamType& map = new LinkedHashMap&Integer, ParamType&();
// 获取连接
conn = DbHelperJdbc.getInstance().getConn();
// 获取自定义参数序列 "1,2,3,o"之类的
orders = getOrdersFromSql(sql);
// 准备语句,需要讲我们设置的东西重新替换成'?'
cs = conn.prepareCall(sql.replaceAll("\\{\\w\\}", "?"));
// 1.设置传入传出参数!
for (int index = 0; index & orders. index++) {
int order = Integer.parseInt(orders[index]);
cs.setObject(index + 1, paras[order - 1]);
} catch (NumberFormatException e) {
// 转型失败说明应该是有o了说明应该是输出类型
// 2.注册传出参数!
cs.registerOutParameter(index + 1, OracleTypes.CURSOR);
map.put(index + 1, ParamType.CURSOR);
// 3.执行!
cs.execute();
// 4.获取结果集并通过传递进来的bean的class加载并放到list中返回(如果需要)!
if (map.size() & 0) {
ret=new ArrayList();
int clazz_index=0;//对于传进来的bean.class是数组,我们需要用来初始化收集
Iterator&Integer& it = map.keySet().iterator();
while (it.hasNext()) {
int reg_order=it.next();//注册时候的序号
ParamType reg_type=map.get(reg_order);//注册时候的类型
switch (reg_type) {
case CURSOR:
//游标怎么处理
//先暂时写到这里吧,看看整个执行过程对不对
rs=(ResultSet) cs.getObject(reg_order);
ret=getBeans(rs,clazz[clazz_index]);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (rs!=null)
rs.close();
if (cs != null)
cs.close();
if (conn != null)
conn.close();
// 首先我们观察传入的SQL,简化成"{X X.X({1},{2},{o})}"
// 我们实质上是想得到其中的"1,2,o"
public String[] getOrdersFromSql(String sql) {
// 直接截取中括号里面的内容
sql = sql.substring(sql.indexOf("(") + 1,
sql.indexOf(")"));
// 现在sql 为"{1},{2},{o}"再替换"{","}"为""
sql = sql.replaceAll("\\{|\\}", "");
// 现在sql为"1,2,o" 按","分割返回
return sql.split(",");
现在激动人心的时刻到了,测试是惊险的。
先往表里添加记录
INSERT INTO USERS VALUES('1','Billy',sysdate);
INSERT INTO USERS VALUES('2','Lily',sysdate);
INSERT INTO USERS VALUES('3','Rowland',sysdate);
存储过程:
CREATE OR REPLACE PACKAGE BODY PKG_TEST IS
PROCEDURE GETUSERS_FROM_CURSOR(I_ID IN NUMBER,O_RES OUT SYS_REFCURSOR) AS
OPEN O_RES FOR SELECT
ID AS "id",
NAME AS "name",
BIRTHDAY AS "birthday"
FROM USERS WHERE ID=I_ID;
END PKG_TEST;
import java.util.L
import test.bean.U
import test.bean.dao.UserD
import test.bean.dao.UserDaoI
mon.proxy.I
public class Test {
public static void main(String[] args) throws Exception {
UserDao ud=(UserDao) new Interceptor().getInstance(new UserDaoImp());
List&User& list=ud.getUsers(1);
System.out.println("beans count:"+list.size());
for(User user:list){
System.out.println("id="+user.getId());
System.out.println("name="+user.getName());
System.out.println("birthday"+user.getBirthday());
}
结果呢?报错了
java.lang.IllegalArgumentException: Can not set java.lang.Integer field test.bean.User.id to java.math.BigDecimal
这么看来应该是类型错误,如果我们仅仅只是rs.getObject就很容易出现这种问题,应该要使用rs.getInt去赋值bean的id,现在知道我说我倾向于在Bean时候使用字符类型了吧,有人会说那我java端要拿去做计算不方便,我说,既然选择了使用存储过程就应该把业务逻辑全权交给存储过程java端就只需要取出来展示一下,或者传进数据库存入一下。不应该出现java端还有复杂计算。针对一些展示格式上面特别的小的拼接和运算,那么交给前台js去处理就好了,js本身也是弱类型的。所以解决的思路有两个,那么在BeanUtil里拿到bean属性的时候去判断一下类型,这么一来你至少需要8个基本类型+字符型+Date+...,我是比较懒了,所以我直接把bean的属性都改成string算了。
那么相应的存储过程就需要全部返回字符型了
CREATE OR REPLACE PACKAGE BODY PKG_TEST IS
PROCEDURE GETUSERS_FROM_CURSOR(I_ID IN NUMBER,O_RES OUT SYS_REFCURSOR) AS
OPEN O_RES FOR SELECT
to_char(ID) AS "id",
NAME AS "name",
to_char(BIRTHDAY,'yyyy-mm-dd') AS "birthday"
FROM USERS WHERE ID=I_ID,ID;
END PKG_TEST;
再来测试一下,结果如下:
beans count:1
id=1
name=Billy
birthday=
测试完了一个参数的情况,我们测试一下两个参数看看吧
package test.bean.
import java.util.L
import test.bean.U
public class UserDaoImp extends ProxyObjects implements UserDao {
public List&User& getUsers(int id,String name) throws Exception {
return (List&User&) pr.execute("{CALL PKG_TEST.GETUSERS_FROM_CURSOR({1},{2},{o})}", User.class);
PROCEDURE GETUSERS_FROM_CURSOR(I_ID IN NUMBER,I_NAME IN VARCHAR2,O_RES OUT SYS_REFCURSOR) AS
OPEN O_RES FOR SELECT
to_char(ID) AS "id",
NAME AS "name",
to_char(BIRTHDAY,'yyyy-mm-dd') AS "birthday"
FROM USERS WHERE ID=ID AND NAME=I_NAME;
测试代码只需要改动一个地方
List&User& list=ud.getUsers(1,"Bily");
看看结果?
beans count:0
什么?没报错?又没查出来?什么情况?赶紧查查数据库,哎呀我去!数据库里是"Billy"
实在是太粗心了。改成Billy之后正常。
我们再来测试一下无参的情况
package test.bean.
import java.util.L
import test.bean.U
public class UserDaoImp extends ProxyObjects implements UserDao {
public List&User& getUsers()throws Exception {
return (List&User&) pr.execute("{CALL PKG_TEST.GETUSERS_FROM_CURSOR({o})}", User.class);
存储过程只需要将传入参数删掉,where语句删掉就不贴出来了。结果如下
beans count:3
id=1
name=Billy
birthday=
id=2
name=Lily
birthday=
id=3
name=Rowland
birthday=
OK,大功告成。看来我们达到了目标了,接下来的任务就是不断地对ProcRunner进行开发,使之兼容各种类型的传入传出。这一篇解决了游标,下一篇咱们来解决数组。
总结:其实整个过程并没有使用太深奥超前的技术,尽量不借助第三方包而完成整个过程。最麻烦的地方在于多样的类型,如果想要真正封装成一套框架的话,那么对于细节的处理就不能像我这么大条。好在终于是完成了它。今后使用存储过程就显得很方便了。
浏览: 16844 次
来自: 杭州
上档次!!
我先来顶了。以后也许用的到spring mvc 表单映射date类型字段的问题 - 街角的天空 - ITeye技术网站
博客分类:
在mvc中如果表单属性的类型是日期型时,从页面绑定字符串数据会出错
Failed to convert property value of type [java.lang.String] to required type [java.util.Date] for property 'expert.birthdate'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property 'birthdate': no matching editors or conversion strategy found
解决方法
1.控制器继承 extends SimpleFormController
2.重写initBinder方法
@InitBinder
protected void initBinder(HttpServletRequest request,
ServletRequestDataBinder binder) throws Exception {
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(fmt, true);
binder.registerCustomEditor(Date.class, dateEditor);
super.initBinder(request, binder);
注意SimpleDateFormat日期格式与页面日期格式要一致!
浏览: 101153 次
来自: 北京
引用[flash=200,200][url][img][lis ...
求helloword!Java replaceAll()方法报错Illegal group reference - java小强 - ITeye技术网站
博客分类:
replaceAll(regex, replacement)函数,由于第一个参数支持正则表达式,replacement中出现“$”,会按照$1$2的分组模式进行匹配。当编译器发现“$”后跟的不是整数的时候,就会抛出“非法的组引用”的异常。
例如,如下代码会报错:
public class Test {
public static void main(String[] args) {
String str = "123ABC456";
String re = "#7T$/#";
System.out.println(str.replaceAll("ABC", re));
报错内容:
Exception in thread "main" java.lang.IllegalArgumentException: Illegal group reference
at java.util.regex.Matcher.appendReplacement(Unknown Source)
at java.util.regex.Matcher.replaceAll(Unknown Source)
at java.lang.String.replaceAll(Unknown Source)
.vogue.Test.main(Test.java:6)
解决办法:
一个是JDK提供的方法,对特殊字符进行处理:对要替换的字符做处理代码如下:
re = java.util.regex.Matcher.quoteReplacement(re);
把特殊字符转为特定字符,然后交给接收方处理:例如将$替换为{ZF4}},当然,如果是一家公司做还可以:
String[] strArr = ex.split("\\$");
StringBuffer sb = new StringBuffer();
for(int i=0;i&strArr.length-1;i++){
sb = sb.append(strArr[i]).append("{ZF4}");
sb.append(strArr[strArr.length-1]);
ex = sb.toString();
请您到ITEYE看我的原创:
或支持我的个人博客,地址:
$进行转义后也可以实现&&&&&&&&&&&& String str = "123ABC456";&& &&&&&&&&&& String re = "#7T\\$/#";
& System.out.println(str.replaceAll("ABC", re));ps:很喜欢看你分享的知识点 发现如果替换的str字符有$,re也有$,也可能出问题,还是要总结下规律的,解决问题先
cuisuqiang
浏览: 2224113 次
来自: 北京
浏览量:2032854
为什么我下载你的mypushlet.rar 怎么出不来 报40 ...
请问一下前端是怎么处理的啊
挺好 成功了 谢谢
java实现操作excel文件,poi实现感觉有点麻烦。Pag ...
为什么我的都按上面复制的,也没报错就是访问不了呢Java的日期API真烂 - 四火的BLOG - ITeye技术网站
博客分类:
记得在我刚学Java的时候,真是搞不清楚Date和Calendar这两个类,后来我渐渐知道,原来不能全怪我啊,Java日期API之烂是公认的(不妨参见,Tiago Fernandez做过一个投票,就是要选举最烂的Java API,结果Java日期API排行第二,仅次于臭名远扬的EJB2,嘿嘿)。
蛋疼的java.sql.Date
只有Date和Calendar搞定一切吗?那还好啊。当然不是!光Date就有java.util.Date和java.sql.Date,而且关系是java.sql.Date extends java.util.Date。为了把前者转成后者,我写了这样的代码:
Date date = new Date();
java.sql.Date d = new java.sql.Date(date.getTime());
居然不支持Date参数的构造器,我只好传入long类型的时间。接下去,我尝试把当前小时数取出来:
System.out.println(d.getHours());
悲剧出现了:
Exception in thread "main" java.lang.IllegalArgumentException
at java.sql.Date.getHours(Date.java:177)
一看源码,坑爹啊:
public int getHours() {
throw new java.lang.IllegalArgumentException();
在java.util.Date里面好好的方法怎么变成这个鸟样了?
方法注释给出了说明:
This method is deprecated and should not be used because SQL Date values do not have a time component.
也就是说,java.sql.Date是SQL中的单纯的日期类型,哪会有时分秒啊?我觉得它根本不应该设计成java.util.Date的子类。如果你把java.sql.Date通过JDBC插入数据库,你会发现时分秒都丢失了,因此如果你同时需要日期和时间,你应该使用Timestamp,它也是java.util.Date的子类。
另外还有一个java.util.Date的子类叫Time,java.sql包下面的Date、Time和Timestamp可以放在一起记忆。Date只包含年月日信息、Time只包含时分秒信息,而Times则包含时间戳的完整信息。
现在知道人家抛出IllegalArgumentException的用心良苦了吧……
坑爹的year和month
看看Date类的构造器:
public Date(int year, int month, int day)
长得并不奇葩嘛。
好,现在我要输出2012年的1月1号了:
Date date = new Date(2012,1,1);
System.out.println(date);
结果,你傻眼了:
Thu Feb 01 00:00:00 CST 3912
等等,这是啥?3192年?
原来实际年份是要在你的年份参数上加上个起始年份1900。
更坑爹的是,月份参数我不是给了1吗?怎么输出二月(Feb)了?
Date里面的月份居然是用0~11表示的,换句话说,一月用0来表示,二月用1来表示。如果不用常量或者枚举,很容易踩到坑里去,对不对?
后来发现Go语言的time.Date方法,对于月份做了个恶心但是不容易坑人的处理(看奇葩的月份参数啊):
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location)
我甚至怀疑Google这样处理是在用极端的方法鄙视Java(另,据我所知,JavaScript好像也是这样的,月份从0开始)……
尝试Joda吧
最开始的时候,Date既要承载日期信息,又要做日期之间的转换,还要做不同日期格式的显示,职责较繁杂,从JDK 1.1 开始,这三项职责分开了:
使用Calendar类实现日期和时间字段之间转换;
使用DateFormat类来格式化和分析日期字符串;
而Date只用来承载日期和时间信息。
原有Date中的相应方法已废弃。不过,无论是Date,还是Calendar,都用着太不方便了,这是API没有设计好的地方。
比如Calendar的getInstance方法,并未提供一个指定年月日和时分秒的重载方法,每次要指定特定的日期时间,必须先获取一个表示当前时间的Calendar实例,再去设值,比如:
Calendar c = Calendar.getInstance();
c.set(2012, 0, 1, 11, 11, 11);
System.out.println(c.getTime());
注意上面代码中对于年份的传值——是的,和Date不一样的是,Calendar年份的传值不需要减去1900(当然月份的定义和Date还是一样),这种不一致真是让人抓狂!
Sun Jan 01 11:11:11 CST 2012
有很多开源库都在努力弥补Java的这一问题,比如,获取Calendar对象和设置时间完全可以合成一步完成:
DateTime dateTime = new DateTime(2012, 1, 1, 11, 11, 11, 0);
而且,一月份总是可以传1来表示了。
再如,如果要给上述时间增加3天再按格式输出的话,使用Joda更加便捷:
System.out.println(dateTime.plusDays(3).toString("E MM/dd/yyyy HH:mm:ss");
有兴趣的话请阅读,并下载Joda-Time使用。
众所周知Java的规范就是多、而且啰嗦,这帮老大们(Export Group中除了有Oracle的人,还有IBM、Google和RedHat的人)终于再也无法忍受Java那么烂的日期API了,于是就有了(感兴趣的请移步),官方的描述叫做“This JSR will provide a new and improved date and time API for Java.”,目前的阶段还在“Early Draft Review 2”,有得等。
JSR-310将解决许多现有Java日期API的设计问题。比如Date和Calendar目前是可变对象,你可以随意改变对象的日期或者时间,而Joda就将DateTime对象设计成String对象一样地不可变,能够带来线程安全等等的好处,因此这一点也将被JSR-310采纳。
很多JSR规范都是在程序员的诋毁和谩骂声中萌芽的,然后会有开源项目来尝试解决Java的这些弊端,最后就轮到JSR就去抄他们的实现。除了新的日期API,再比如JCache(),你知道它抄了多少EhCache的东西么……
文章系本人原创,转载请注明作者和出处()
注:本博客已经迁移到个人站点
,欢迎大家访问收藏,本ITEye博客在数日后将不再更新。
浏览: 720112 次
来自: 北京
浏览量:260191
浏览量:58882
其实不能说程序员懒惰,只能说程序员不喜欢做重复的事情。
完善的网络爬虫应该具有下面的特点
分布式部署,由调度服务器统 ...
没怎么明白呢
那对比与Apache提供的BeanUils(当然,这仅是对简单 ...

我要回帖

更多关于 illegalargument异常 的文章

 

随机推荐