记得刚做javaweb开发的时候被这个编码问题搞得晕头转向,经常稀里糊涂的编码正常了一会编码又乱了。那个时候迫于项目进度大多都是知其然不知其所以然。后来有时间就把整个体系搞了个遍,终于摸通了来龙去脉。

在C++的CGI开发时大家喜欢用latin,这个属于字节方式的编码格式,存储mysql节约空间,而C++也是比较容易控制到byte级别的语言。所以经过框架封装基本也问题不大。

在Java语言中,要涉及修改编码问题的地方还真多。一个地方没有设好就会乱码满天飞。大概总结包括以下这几部分:浏览器、服务器、数据库、操作系统。

浏览器:
如果使用模板语言,html需要设置显示的字符集。这个适用于浏览器判断什么编码显示。

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

扩展,浏览器识别编码的顺序:
1.如果HTTP头部申明了charset,则会使用HTTP头部的,
2.让HTTP头部没有设置,则会去解析meta标签的,
3.如果meta也没有的话,浏览器会根据是否设置了auto detect来进行编码识别,
4.否则会使用本地UI的字符编码。

服务器:
对于JSP等动态语言,需要在jsp头部设置编码格式,J2EE服务器解析这个JSP的时候才会把整个页面编码为UTF-8输出,不然就按照系统默认编码格式ISO-8859-1输出了。JSP设置格式如下:

<%@ page language= "java" contentType = "text/html; charset=UTF-8"
      pageEncoding ="UTF-8" %>

大家都知道,JSP对应的就是servlet。servlet的编码对应如下设置:

public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException{
response.setContentType("text/html;charset=utf-8");
}

还有不要漏掉大家常用的spring工具类,编码转换filter,很实用。在你用struts、spring mvc时这个过滤器帮你转换没有设置的编码过滤。如下设置:

       <filter>
             <filter-name> Set Character Encoding</filter-name>
             <filter-class>
                  org.springframework.web.filter.CharacterEncodingFilter
             </filter-class>
             <init-param>
                   <param-name> encoding</param-name>
                   <param-value> UTF-8</param-value>
             </init-param>
       </filter>

万一还有乱码怎么办呢?doGet方式的参数传递肯定会有乱码问题。只需要在tomcat的监听器里设置编码字符集如下(文件一般存储在 /tomcat安装目录/conf/server.xml ):

<Connector port="80" protocol="HTTP/1.1"
              connectionTimeout="20000"
              redirectPort="8443" URIEncoding="utf-8"   />

大家在开发的时候别忘了java文件本身也是有编码格式的。在类文件右键查看属性。
eclipse属性

 

 

如果开发时忘记更改文件的编码格式,windows默认是GBK的,后来又要一直到utf8编码的linux怎么办。文件巨多,总不能一个一个去更改吧。其实很简单,只需要在java命令的环境参数设置 -Dfile.encoding=GBK 解决。

编译java代码时,如果使用ant需要在javac里设置编译的字符集。这样打印的log输出到文件或者控制台上就不会乱码了。

<javac debuglevel= "source,lines" source= "1.6"   encoding= "utf-8">

maven编译时设置的字符集:

 < artifactId> maven-compiler-plugin </artifactId >
       < version> 2.5 </version >
                       
         < configuration>
           < optimize> true </optimize >
           < showDeprecation> false </showDeprecation >
           < debuglevel> lines,source </debuglevel >
            < source> 1.6 </source >
            < target> 1.6 </target >
            < encoding> UTF-8 </encoding >
            < meminitial> 128m </meminitial >  
             < maxmem> 768m </maxmem >
                       
         </ configuration>

sqlmap的sql xml,sping的xml 也是需要设置的,因为涉及到跨平台。 顶上添加:

<!--?xml version="1.0" encoding="UTF-8"?-->

数据库:
这里列出大家用的最多的Mysql字符集设置。打开mysql的配置文件( linux 一般在 /etc/my.cnf ,windows在mysql的安装目录 my.ini)。设置如下:

[mysqld]
default-character-set = utf8

[ mysql]
character_set_server = utf8

jdbc需要设置
jdbc : mysql://192.168.0.237:3306/dzh_db?useUnicode=true&characterEncoding=UTF-8

这些都设置了一般的中文是不会有问题的。

不过最近出现了一个问题很搞怪。以前以为所有的字符只要设置好了所有数据都可以录入数据库,结果有些字符就不行,比如●■★这类型的。后来把这些字符变成字节码,居然不是三位utf8的,我擦,大汗淋漓。后来查询可以通过过滤utf8 特殊字符的方式处理。

public static String Utf2String (byte buf[]) {
int len = buf.length ;
StringBuffer sb = new StringBuffer(len / 2);
for (int i = 0; i &lt; len; i++) {

if (by2int(buf[i]) &lt;= 0x7F)
sb.append(( char ) buf[i]);
else if (by2int(buf[i]) &lt;= 0xDF &amp;&amp; by2int(buf[i]) &gt;= 0xC0) {
int bh = by2int(buf[i] &amp; 0x1F);
int bl = by2int(buf[++i] &amp; 0x3F);

bl = by2int(bh &lt;&lt; 6 | bl); bh = by2int(bh &gt;&gt; 2);
int c = bh &lt;&lt; 8 | bl;
sb.append(( char ) c);
} else if (by2int(buf[i]) &lt;= 0xEF &amp;&amp; by2int(buf[i]) &gt;= 0xE0) {
int bh = by2int(buf[i] &amp; 0x0F);
int bl = by2int(buf[++i] &amp; 0x3F);
int bll = by2int(buf[++i] &amp; 0x3F);

bh = by2int(bh &lt;&lt; 4 | bl &gt;&gt; 2);
bl = by2int(bl &lt;&lt; 6 | bll);

int c = bh &lt;&lt; 8 | bl;
// 空格转换为半角
if (c == 58865) {
c = 32;
}
sb.append(( char ) c);

}
}
return sb.toString();
}

或者把mysql的字符集改为 utf8mb4 ,记得这个只有mysql55支持哦!

[mysqld]
default-character-set =utf8mb4

[ mysql]
character_set_server = utf8mb4

操作系统:
windows默认是gbk,一般不需要变动。不过大家又想每个文件都要建立为utf8格式怎么办,不可能我们每个文件建立后都去用属性改变一下?太麻烦!直接在eclipse设置后,同种类型的文件建立都会是utf8格式。

eclipse属性2

 

linux,可以有两个地方修改基本就足够了:
vi /etc/sysconfig/i18n
修改

LANG="zh_CN.GB3212"
LANGUAGE="zh_CN.GB18030:zh_CN.GB2312:zh_CN"
SUPPORTED="zh_CN.GB18030:zh_CN:zh:en_US.UTF-8:en_US:en"

vi /etc/profile

export LC_ALL="zh_CN.GB2312"
export LANG="zh_CN.GB2312"

原创文章,转载请注明: 转载自LANCEYAN.COM

本文链接地址: 谈谈WEB开发中的苦大难字符集问题

      在创业开始时,也许有一个点子,经过研究调查有市场可行性就开始召集人员开始创业。这个时候一般都是单打独斗,一两个人负责所有的事情,包括产品设计、交互原型设计、网页美工切割、程序开发、功能测试。这个时候基本不用合作,采用独立开发模式,自己搞定保存就可以了。不过也有风险就是硬盘坏了怎么办呢,要知道硬盘是最脆弱的东西了,我一般的话会定期备份硬盘的东西。

      如果发现创业模式可行,需要赶时间抢市场,一两个人完成不了很多任务,就需要更多专业的人加入才能做出一个交互很好、页面美观、功能好用的产品。这个时候一般的话应该有产品经理、美工设计、程序开发、测试、运维。但是这些角色不是说一定要安排这么多岗位的人,需要根据公司具体自身情况来设定。

    有了点市场机会,时间才是最重要的。如果按照传统的计划型开发模式就不太适合了,敏捷开发还算比较相像。不一定说非要采用什么开发模式,这个也要根据自己的公司来决定。我们的模式应该是像中国的社会主义社会一样是基于敏捷的XX公司的开发模式。

      好了,岗位都有了,咱们怎么分工合作呢?这时,因为是根据点子来演化产品,很多东西都是不确定的,不能定一个大的一成不变的长时间计划和需求。毕竟这个时候要求需求不变也是不可能的,我们本身自己就充当了产品经理的角色。不只要想市场和老板的需求,还要产品本身和功能实现的需求,逐步把一个模糊的东西变成可以实际操作的产品原型,再形成最终产品。结合敏捷开发的特点,以人为本,把一个大的需求分成小需求分给适合的人来完成。上面所说的那些角色不一定每个岗位都需要一个具体的人,也可以是一个人承担一个角色,或者多个人承担一个角色。比如我们公司暂时就没有产品经理、测试、运维,程序开发负责开发、测试、运维,产品设计所有人都要参与。总体产品设计由一个人来把关和跟踪,细分到每一个产品都要对自己的产品负责分析和跟进。相当于贯彻了一句话,好像来自于阿里巴巴的一个产品写的一本书《人人都是产品经理》。如果自己对自己的产品都不清楚、玩不转、操作不了,谈什么让用户喜欢呢?不管美工、研发、测试、运维,都需要对产品有很好的理解。如果可以的话,不只是要理解自己做的一小块需求的产品,还应该了解更多其他同事的产品。作为产品总体把关的人一般是技术总监需要对每一个产品都很熟悉,这样才能跟进和设计讨论。所以研发、美工、测试、运维不管做什么工作,都需要站在一定高度思考问题,既然创业就需要以创业的角度思考,这样对自己的职业发展有帮助,也可以做好产品。

      在根据一个大需求技术总监和大家讨论,把模糊的东西逐步清晰化得到一个比较明确的做法后分到合适的人上。这样大家各自领导自己的需求开始分析具体实现,得到一个大概的思路后再和技术总监讨论确定最终具体实现。关于美工总体需求分析可以参与,这样便于了解整个需求,还是那句话了解了才能设计出好看好用的产品界面。细节的需求和技术实现就让研发来想和确定。在研发确定好了实现方案后,和美工进行讨论界面的实现方案,这个时候美工就清楚了小需求的实现功能和界面方案。然后美工设计界面,有时间可以多弄几个方案,开始可以在草图用笔画,也要沟通、讨论、确定。最后选择一个用户界面,研发开始开发程序、美工切割页面。如果美工的模板和规范基本都有确定了,那工作可能就不是太紧张,所以可以应对几个开发的需求。这个根据具体的情况确定美工的人数。

      研发开始可以自己弄页面,先把动态的标签确定好了,后面根据美工切割的页面套入。然后研发后台逻辑,一般都是从前台到后台开发甚至db开发都需要懂点,这样才能保证高效开发。如果横向切分,一些人研发service、一些人研发dao,一些研发jsp会增加不必要的沟通浪费时间。如果后面项目庞大了,还是建议应该增加一个专业的前端js开发和后台DBA。研发开发好自己的业务代码对功能进行测试,测试ok技术总监确认就等集成连调。这个时候的开发都是在自己的本机完成,不在测试环境测试是为了提供研发速度和效率。大家都知道如果是java开发,部署war不断reload会影响他人的工作,如果工程比较大启动也比较慢。如果在本机,只是修改类里面的东西和jsp等直接就可以看到效果。修改类签名和增加类等需要重新部署。

      大家都开发好自己的模块后,集中在测试服务器集成测试。这里可以由指定人来打包部署,大家一起测试找问题,修改。关于前端一般是自己改好了原型界面,后面被嵌入了动态页面后就直接修改动态页面了。预览在集成服务器,这里可以把预览和svn服务器集成,前端美工提交jsp到svn后就生效,他也可以直接看到效果了。具体操作后面文章再介绍。最后测试没有问题,老板确认,发布外网服务器、部署db。这里也容易出错,如果发布者不是本人很容易漏掉东西,所以最好在发布时,如果改了除了程序以前的东西需要提供发布列表一并修改。

      再以后随着项目的庞大和人员的扩充就需要借助更多的模式和辅助工具来寻找更适合自己的方式了。比如页面发布工具(前端可以直接发布IDC)、持续集成(可以自动部署测试和IDC)等等。项目中最最重要的还是人,根据人来选择不同的工作模式,就像豆瓣CEO说的,什么语言工具技术都没关系,最重要的是人、团队。相信经过大家的磨合, 每一个都是超人(技能、沟通、合作)的团队就是这么诞生的。

原创文章,转载请注明: 转载自LANCEYAN.COM

本文链接地址: 谈谈创业公司技术的工作模式

不管是什么程序开发都可能会出现各种各样的异常。可能是程序错误,也可能是业务逻辑错误。针对这个各个开发人员都有自己的处理方式,不同的风格增加了业务系统的复杂度和维护难度。所以定义好一个统一的异常处理框架还是需要的。我们开发框架采用java实现,java中的异常一般分为两种,检查异常和运行时异常。检查异常(checked exception)有可能是程序的业务异常,这种异常一般都是开发人员自定义的、知道什么时候会抛出什么异常并进行捕捉处理。也可以是系统的异常,不捕捉编译不会通过,如  IOException、SQLException、ClassNotFoundException , 这种是必须要捕捉的并且大多都是继承Exception。 运行时异常一般都是系统抛出来的异常,这种异常不捕捉处理也不会报编译错误,如NullPointerException,ClassCastException。运行异常都是继承至RuntimeException。不管是检查异常还是运行时异常都是继承至Exception。另外还有一种异常是系统错误Error,这种异常是系统出现了故障抛出来的不能捕捉,如OutOfMemoryError。Exception和Error都是继承至Throwable。

了解了java的异常体系后,我们设计一下web框架的异常处理格式。在以往EJB时代的J2ee系统,一般是标准的三层架构:web层、业务逻辑层、数据访问层,并且每一层都分别部署在不同的机器集群中。这样我们的异常一般分为三个,WebException、BizException、DAOException分别映射到web层、业务逻辑层、数据访问层。并且这些异常都要设计的串行化可以跨机器传递生成异常链。这样的好处是看到异常链知道从哪儿抛出来的错误,比较清晰明了。

老异常链

随着后面spring的推出,java的开发越来越轻量级很多时候一台服务器可以同时部署三层并集群化,架构模式也慢慢由充血模式演变为贫血模式,再也没有了厚重的实体Bean和有状态会话Bean。针对现在轻量级的框架,异常结构如何设计呢?

先看看我们这个异常结构需要解决的问题是什么?

  1. 规范大家的异常处理方式。
  2. 简化异常处理。
  3. 区分业务异常和系统异常,业务异常需要业务逻辑支持,系统异常需要记录log。
  4. 友好的异常展示。
  5. 异常结构可扩展。

针对这些点,我的想法是开发可以使用三个类:SDKException、BizException、BizSystemException。SDKException是处理的基础类,可以在里面封装一些异常处理的基本函数。BizException是业务逻辑处理异常,一般这类异常是不需要记录log,只是展示给页面显示并提示给用户。如你的用户名、密码为空等错误。BizSystemException是业务系统异常,这类异常一般需要捕捉并记录log,比如数据库的主键冲突、sql语句错误等。按照三层架构的话,我们不可能对每一层都捕捉并且记录log,会造成重复log。可以从DAO层把捕捉到的数据库异常转换为BizSystemException抛出,如果有BizException也抛出。业务逻辑层对于BizSystemException、BizException不处理直接抛出。所有处理都在web层进行集中处理,如果是BizException,根据错误码和错误消息显示给用户对应的页面和错误消息。如果是BizSystemException告知用户系统错误,并把错误结果记入log。如果是其他异常和BizSystemException一致。这样就减少了异常处理的复杂度,开发也不用关心什么检查异常,运行时异常。

新错误结构

如果是ajax请求在做web层时,把返回的jsp变成json格式或者流格式输出即可,不影响异常框架。如采用struts2结构的代码:

public String testAjax()
{
    try
    {
         genAjaxDataStr(0, "{}");
    } catch (BizException e)
    {
         getRequest().setAttribute(this.ERRORMESSAGE, e.getErrorMessage());
         return this.ERRORJSON;
    } catch (BizSystemException e)
    {
         getRequest().setAttribute(this.ERRORMESSAGE, this.SYSTEMERROR);
         return this.ERRORJSON;
    } catch (Exception e)
    {
         this.errorTrace("test", e.getMessage(), e);
         getRequest().setAttribute(this.ERRORMESSAGE, this.SYSTEMERROR);
         return this.ERRORJSON;
    }
    return this.NONE;
}

这样前台就能根据我们的异常显示对应的错误页面了,并能把系统知道的和未知的异常记入log。

针对struts2还有个问题,在开发模式时,struts2和webwork的异常打印在页面,我们可以根据页面输出进行调试。一但部署在生产环境,需要将这个模式关闭,log就没有了。

<constant name="struts.devMode" value="false" />

为了记录一些未知的错误,需要做以下步骤:

  1. 将全局的异常映射页面从struts2的包定义里去掉。如果不去掉,在webwork不会抛出异常也就找不到出了什么问题。
  2. 扩展struts的DispatcherFilter捕捉未知的异常并记录入log。
<global-exception-mappings>
    <exception-mapping exception="com.linktong.sdk.biz.exception.BizException"
                result="checkedException" />
</global-exception-mappings>

这样,基本的异常框架就搭建完成。更进一步需要做的是:

  1. 分布式全局错误码体系,保证所有机器都共用一套错误码。
  2. 分布式部署,异常传递。可以采用hessian序列化错误码的机制,不用传输整个异常链节省带宽。
  3. 集中logger服务处理,所有机器的log统一发送到集中服务器处理。logger框架和log4j也有服务器的机制。

针对web框架、分布式部署、log服务器再讨论:)

原创文章,转载请注明: 转载自LANCEYAN.COM

本文链接地址: WEB框架的错误体系