jstl学习经验(旧)

为了在公司里边推广jstl,而总结的一些jstl使用技巧:

jstl可以很方便的处理NullPointException,访问越界等常见问题

<c:set var=”SPExtraInfoList” value=”${queryresult[3]}”/>
<c:out value=”${queryresult[3]}”/>

这2个表达式不会因为没有第四个元素而报错

这2个表达式一般情况下表现是一样的,只是用c:out可以带上default的参数来处理NullPointException

<td><c:out value=”${spservice.m_SpID}”/></td>
<td>${spservice.m_SpID}</td>

即使担心其他Exception,也可以通过c:catch来做防御

<c:catch><c:out value=”${values[9]/1000.00}”/></c:catch>

fn:length的使用

用来判断集合类型或String的长度,不过只是用来判断obj是否为空的话,用le语言${empty obj}更方便(同样支持多种类型)

关于fn:split

用这个方法切割会丢失空字符串,原因是这个标签使用StringTokenizer来处理而不是split方法。

<% String[] values = ((CEntityString)pageContext.getAttribute(service)).EntityString.split(,);
   pageContext.setAttribute(values, values);  %>

这是比较丑的例子,对格式依赖比较严重,所以最好就使用比较清晰的结构

关于if-else和三元表达式

jstl做判断分支是比较麻烦的,只提供了c:otherwise的方式,只好将就点先用着吧。如

<c:choose><c:when test=”${’1′ eq spextrainfo[0]}”></c:when><c:otherwise>< /c:otherwise></c:choose>

jstl改变操作

jstl里边要做访问操作是比较方便的,不过涉及改变就挺麻烦的,如对某个字符串加个头,然后在Map里边检索,这样就不能在一个表达式里边写完,需要先c:set一下,如

<c:set var=”bzType” value=”BizType${spservice.m_SpBizType}”/>
<c:out value=”${BizTypeMap[bzType]}”/>

ibatis进阶(旧)

Ibatis比较少人使用的配置语法

简单来说,ibatis3虽然没有ognl,不过也支持基本的表达式(看起来有点像el表达式的样子) 上次有个问题,说到两个表单数据(两个javabean),入同一个表,传参就应该不成问题了

java代码1:

   Map map = new HashMap();
   ComplexBean bean = new ComplexBean();
   bean.setMap(new HashMap());
   bean.getMap().put("id", new Integer(1));
   map.put("bean", bean);
   Account account = new Account();
   account.setId(2);
   Account anotherAccount = new Account();
   anotherAccount.setId(3);
   map.put("accounts", new Account[] {account, anotherAccount});
   Integer id = (Integer) sqlMap.queryForObject("mapBeanMap", map);

ibatis配置1:

 <select id="mapBeanMap"
   parameterClass="map"
   resultClass="int" >
   select count(ACC_ID) from Account where ACC_ID in (#bean.map.id#,#accounts[0].id#,#accounts[1].id#)
 </select>

java代码2:

   Map map = new HashMap();
   ComplexBean bean = new ComplexBean();
   bean.setMap(new HashMap());
   Account account = new Account();
   account.setId(2);
   Account anotherAccount = new Account();
   anotherAccount.setId(3);
   bean.getMap().put("accounts", new Account[] {account, anotherAccount});
   map.put("bean", bean);

ibatis配置2:

 <select id="mapBeanMap2"
   parameterClass="map"
   resultClass="int" >
   select count(ACC_ID) from Account where ACC_ID in
   <iterate close=")" open="(" conjunction="," property="bean.map.accounts">
     #bean.map.accounts[].id#
   </iterate>
 </select>

ibatis与泛型

当使用复杂配置并且参数带有泛型的时候,使用比较标签有可能导致如下错误:There is no READABLE property named ‘XXX’ in class ‘java.lang.Object’.这是因为进行比较的时候,ibatis是通过反射获取类型而不是先计算值的,这样泛型的时候会获取到Object类而不能得到真实的类型,自己简单打个补丁先:

Index: src/com/ibatis/sqlmap/engine/mapping/sql/dynamic/elements/ConditionalTagHandler.java
===================================================================
--- src/com/ibatis/sqlmap/engine/mapping/sql/dynamic/elements/ConditionalTagHandler.java (revision 1079874)
+++ src/com/ibatis/sqlmap/engine/mapping/sql/dynamic/elements/ConditionalTagHandler.java (working copy)
@@ -72,14 +72,13 @@

     if (prop != null) {
       value1 = PROBE.getObject(parameterObject, prop);
-      type = PROBE.getPropertyTypeForGetter(parameterObject, prop);
     } else {
       value1 = parameterObject;
-      if (value1 != null) {
-        type = parameterObject.getClass();
-      } else {
-        type = Object.class;
-      }
+    }
+    if (value1 != null) {
+     type = value1.getClass();
+    } else {
+     type = Object.class;
     }
     if (comparePropertyName != null) {
       Object value2 = PROBE.getObject(parameterObject, comparePropertyName);

关于inlineParameterMap

例如#name#(标准配置),#name:NUMBER#(以:分割),#myVar:javaType=int#都是有效的 其中以:分割的有两种方式,#name:jdbcTypeName#,#name:jdbcTypeName:nullvalue#(如果后面还有则会被加到nullvalue上去) 这是老配置方法,个人不推荐使用。 最后一种是新的配置方式,可以带上javaType,jdbcType,mode,nullValue,numericScale,handler等参数(这个文档有详细描述)

jdbcType,javaType和TypeHandler

首先要说明一点的是,配置里边的jdbcType和javaType两个配置参数是为了生成TypeHandler(如果没有指定的话); 查找typeHandler的内部结构是Map<javaType, Map<jdbcType, typeHandler>>,其中javaType是一个类,jdbcType是一个字符串; 所以jdbcType其实和数据库的字段类型没什么关系,只要能找到相应的TypeHandler即可(当然通常都会对应上); typeHandler主要是做什么用的呢?无非就是使用jdbc api的时候选择setString/setInt还是getString/getObject之类

只指定resultClass,没有resultMap

如果没有指定resultMap,ibatis会根据parameterClass生成一个AutoResultMap对象; 对于AutoResultMap,里边的每个属性的映射对应的typeHandler是什么?

resultClassTypeHandler
MapObjectTypeHandler
原型类型相应类对应的typeHandler(javaType=?,jdbcType=null)
Bean会对实例变量名称进行大写并和ResultSetMetaData信息进行对比,最后生成typeHandler(javaType=?,jdbcType=null)

所以使用parameterClass是map的时候,某些字段的处理可能会有点问题,例如oracle的NUMBER类型会被转成BigDecimal类;

只指定parameterClass,没有parameterMap

如果没有指定parameterMap,就会根据配置的sql解析inlineParameterMap; 其中每个参数的TypeHandler如果没有指定,会根据参数的类型来寻找,例如#name,jdbcType=NUMBER# 会根据name计算后的类型来制定javaType 这个typeHandler的好处可以对jdbc api友好,例如对于int默认会采用IntegerTypeHandler,这样会调用PreparedStatement#setInt, 而不是统统setString或者setObject。 通常参数类型和jdbc类型不对应的时候,需要考虑设置typeHandler或者使用更强类型的Bean而不是统统使用map;

findbugs使用有感(旧)

最近抽出点时间来处理项目上庞大的findbugs问题,处理过程中总结了下面几类参见的毛病:

关于IO流

见过的有写response的outputStream,文件下载,数据导出。这些场景涉及流读取、写入和最后的关闭,是findbugs检查的重灾区. 解决办法是使用common-io库,常用的方法有IOUtils#copy,IOUtils#write,IOUtils#closeQuietly。

关于无效变量操作

包括变量使用后置null,没有被使用的变量,虽然这种findbugs问题很低级,不过在系统中仍然存在很多。 处理办法是

  1. 变量使用后置null是没什么用的,JVM的GC比想象中智能多了;
  2. 假如变量在稍后的处理过程一定会被赋值,则init是没有意义的。
    遵循的原则是相关的变量和操作集中在一起,缩短变量的作用域(同时,这样也对JVM比较友好)

关于对象的延时加载

系统中有些类实现了延时加载的singleton模式,还有的Action类中的实例变量使用了延时加载的方式。 很不幸的是,singleton的延时加载实现几乎都是错的, Action类的实例变量延时加载也没什么意义。 要实现singleton,最简单的方式是预加载方式,如果想做延时加载,可以使用内部静态类的实现方式。

关于执行效率

包括string的修改操作,map的遍历操作。如果有string的大量修改操作,应该使用StringBuffer或者StringBuilder (在jdk5以上,StringBuilder适用于大多数场合)。而对于map的遍历,应该使用entrySet而不是keySet,这些注意一下就ok了。 另外系统有很多DateFormater的东西,用了很多静态变量来提高效率,可惜jdk的日期库是非常烂的,DateFormater也不是线程安全的东西,应该避免使用静态变量。

关于异常处理

Exception is caught when Exception is not thrown 这个findbugs问题在系统出现非常多,也是最难处理的问题。从这个点 就可以看出系统实现非常混乱,本来属于不同抽象层次的代码都堆在一起了,没有层次之分。这个问题一般是需要修改到那段逻辑的时候才会去 解决,通过抽取方法/类的方式,把异常进行抽象化,最后借助框架层次上的异常统一处理逻辑就通常可以把Action上面的所有的try-catch干掉。

关于注释

这其实不是findbugs问题。可以发现java代码里边很多注释是没什么用的,包括一些显而易见的注释,方法注释(通常只有方法描述是有点意义,其他都是抄模板的) 注释写不好,还不如不写,正确的做法是尽量写出自文档化的代码。

关于分享

分享好处多多

通过做一些分享活动,一方面可以扩大自己的影响力,让更多的人知道你。 另外一方面,可以加深自己对知识的理解,在交流中纠正一些错误的理解。 还可以提高自己的表达能力。可以说,除了需要花时间准备之外,分享对自己来说,几乎是百利而无一害的。

参与度与氛围

自己做分享,总是希望有更多的人来参与,并从中营造一个愉悦的氛围, 互惠互利,从中可以学习到更多。但是,经常遇到的事实是,参与的童鞋常常不愿意发言。 一方面是可能是自己不擅长,更多看成是培训,而不是交流活动,害怕犯错。还有就是, 他们没有事先做一些准备,对内容理解不够快,所以也没什么特别的疑问。 我认为,提出一些低级问题,并不是什么丢人的事情,可能是分享人没有照顾到你(经常是一类人), 如果你不提出来的话,或许分享人会自认为大家都很明白。总之,参与一些分享活动,能全身心 投入,收获会更大。

敢于分享

很多童鞋都害怕分享,总是担心自己水平不够,准备不够充分,所以一拖再拖。 以我的经验,分享的内容你刚刚够得着就可以了,因为你准备的过程中,会对自己掌握的内容进行 筛选,收集更多的资料,然后进行浓缩总结,到最后你把它分享给其他人,整个过程下来,就是一次很好的 锻炼机会。不要太担心做不好,即使有些地方自己说不清,但或许经过众人的讨论,理解得就更好了。 还有就是,什么都要计划,给自己的分享计划加个合理的期限吧,少许的压力能让你做得更好。

节奏与时间

每次分享的时间不要过长,我就犯过这样的错误,到后面很多人都开始走神了。 还有就是,主题要比较单一,免得铺开太大,不好收缩,影响时间和效果。 时间比较长的话,中间可以适当休息一下。

想不到的收获

最近做了一次关于oracle执行计划的分享活动,说实话,我对这个主题不是很熟悉。 但是我觉得这东西很有用,也非常适合团队里的童鞋,所以花了几个星期的时间收集资料, 最后做了这么一个分享活动。经自己讲解了一遍,自己对这些知识加深了理解。更让我想不到的事情就是, 有其他项目组的童鞋遇到了相关的问题,跑来跟我讨论(哥俨然是专家啦)。正因为这样,我才有机会遇到更多现实的问题, 从而促进我对这些东西的理解,如果没有这次分享,有些东西可能就没法用在实践上了。 我想,很多高手也是这么炼成的。:)