JdonJive论坛系统完整分析(5)

779 篇文章 0 订阅
<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

  5 Jive的其他组件技术

  Jive是一个比较丰富的知识宝藏,从中可以学习到很多新的实战技巧和具体功能实现方式。前面基本介绍了Jive中的一些主要架构技术,通过这些技术可以基本上掌握Jive论坛系统。

  Jive中还有很多非常实用的组件技术和工具库,分析学习可重用技术,可以在自己具体的项目重复使用,大大提高了新系统的开发速度和效率。

  5.1 Jive的树形结构

  Jive的管理功能中提供了将Jive数据库数据导出到XML文件的管理工具,在这个工具功能实现中,使用了树形结构的遍历技术。

  Jive将ForumThread中的第一个ForumMessage作为root ForumMessage,以这个ForumMessage为根节点,每个ForumThread中包含了一套树形结构。

  TreeWalker是树形结构的一个抽象接口,代码如下:

  public interface TreeWalker {

  //根节点

  public ForumMessage getRoot();

  //获得父节点

  public ForumMessage getParent(ForumMessage child)

  throws ForumMessageNotFoundException;

  //获得子节点

  public ForumMessage getChild(ForumMessage parent, int index)

  throws ForumMessageNotFoundException;

  //获得所有子节点

  public Iterator children(ForumMessage parent);

  //获得所有的子节点,包括子节点的子节点…

  public Iterator recursiveChildren(ForumMessage parent);

  //获得一个节点的深度,相对根节点而言

  public int getMessageDepth(ForumMessage message);

  public int getChildCount(ForumMessage parent);

  public int getRecursiveChildCount(ForumMessage parent);

  /**

  * 返回相对父节点的子节点索引。例如

  *

  * 4

  * |-- 2

  * |-- |-- 1

  * |-- |-- 6

  * |-- |-- 8

  * |-- 5

  *

  * getIndexOfChild(4, 2) 将返回0

  * getIndexOfChild(4, 5) 将返回1

  * getIndexOfChild(2, 1) 将返回0

  * getIndexOfChild(2, 6) 将返回1

  * getIndexOfChild(2, 8) 将返回2

  */

  public int getIndexOfChild(ForumMessage parent, ForumMessage child);

  //一个节点是否是叶,叶相对枝来说,叶没有子节点了

  public boolean isLeaf(ForumMessage node);

  }

  DbTreeWalker 是TreeWalker的一个实现,它是将一个ForumThread下所有帖子的ID从数据库中装入LongTree中。一句LongTree的树形结构遍历核心技术实现ForumThread中的帖子结构的遍历。

  LongTree类似之前的Cache类,封装了树形结构遍历的核心算法,在LongTree中建立了3个数组long [] keys、char [] leftChildren和char [] rightSiblings。

  一个节点有两个特性:它有子节点;它有兄弟节点。leftChildren保存的是这个节点的子节点的索引;而rightSiblings保存的是这个节点兄弟节点的索引。例如:

  1000

  |-- 3000

  |-- |--4000

  |-- |--6000

  |-- |--7000

  |-- 5000

  1000是个根节点,1000下有两个子节点3000和5000,而3000则有3个子节点4000、6000和7000,3000还有一个兄弟节点5000,使用上述3个数组是这样保持信息的:

  keys[0] = 1000

  keys[1] = 3000

  keys[2] = 4000

  keys[3] = 5000

  keys[4] = 6000

  keys[5] = 7000

  keys数组中保存的是各个节点的数值,而leftChildren和rightSiblings数组保存的是keys数组的index,即0、1、2、3、4等数字。

  1000节点有两个子节点,那么其对应的leftChildren和rightSiblings分别是:

  leftChildren[0] = 1

  leftChildren[0]中的索引0表示当前索引,keus[0]是1000,说明现在节点是1000;1也表示keys数组的索引,keys[1]的值是3000,所以上一句表示1000的子节点是3000。

  1000节点没有兄弟节点:

  rightSiblings[0] = -1

  再看看3000节点,其keys的索引Index是1,其子节点是4000、6000和7000,取最近一个4000的索引index放入数组:

  leftChildren[1] = 2

  这表示1000节点的子节点是4000,那么另外一个6000节点如何表示?这是以4000节点的兄弟节点表现出来的。4000节点的keys的索引index是2,通过下列表示:

  rightSiblings[2] = 4

  其中,4表示6000在keys中的索引Index。同样,第3个子节点7000表示如下:

  rightSiblings[4] = 5

  这样,3000节点有3个子节点4000、6000和7000(4000、6000和7000是兄弟节点)通过上述简单两句就表现出来了。

  总结一个父子关系树的表示方法:在父节点中,使用leftChildren保存最靠近父节点的一个子节点(父节点的第一个儿子,叫长子)的索引,其他子节点则是通过rightSiblings表明与长子是兄弟关系。

  看看LongTress的初始化构造方法,keys数组的值保存的是ForumMessage的ID,如下:

  public LongTree(long rootKey, int size) {

  keys = new long[size+1]; //初始化

  leftChildren = new char[size+1]; //初始化

  rightSiblings = new char[size+1]; //初始化

  // 在keys[1]中保存的是rootMessage 的ID

  keys[1] = rootKey;

  leftChildren[1] = 0; //无子节点

  rightSiblings[1] = 0; //无兄弟姐妹

  }

  当加入一个节点时,其方法如下:

  public void addChild(long parentKey, long newKey) {

  // 根据parentKey找出其对应的keys索引index

  char parentIndex = findKey(parentKey, (char)1);

  if (parentIndex == 0) {

  throw new IllegalArgumentException("Parent key " + parentKey +

  " not found when adding child " + newKey + ".");

  }

  // 为newKey创建节点

  keys[nextIndex] = newKey;

  leftChildren[nextIndex] = 0;

  rightSiblings[nextIndex] = 0;

  //将新建节点标志为父节点的子节点

  if (leftChildren[parentIndex] == 0) {

  // 如果父节点原来没有子节点,那就将新建节点作为其子节点

  leftChildren[parentIndex] = nextIndex;

  }else {

  // 如果父节点有子节点,寻找最后一个子节点

  long siblingIndex = leftChildren[parentIndex];

  //在siblingIndex中循环查找,直至值为0

  while (rightSiblings[new Long(siblingIndex).intValue()] != 0) {

  siblingIndex = rightSiblings[new Long(siblingIndex).intValue()];

  }

  // 将新建节点作为最后一个字节点加入

  rightSiblings[new Long(siblingIndex).intValue()] = nextIndex;

  }

  // 最后,自动增加nextIndex 以便下一个等待插入

  nextIndex++;

  }

  Jive将数据导出到XML文件时,就是根据某个ForumMessage的ID,通过TreeWalker找出它的所有子节点ForumMessage的ID,然后将其内容导出。

  5.2 XML和JDOM

  XML 称为可扩充标记语言,是类似HTML定义文档标记语言的一个框架。XML以结构严谨著称,因此用来保存数据是非常适合的,这样在数据库之外,又多了一个持久化保存数据的方式。

  在Java中,XML更多时是作为配置文件数据存储体形式出现,在之前一般是使用properties来保存系统的配置文件,如下:

  cache.maxsize=1024

  cache.minsize=2

  这两句分别设置cache的最大值和最小值,那么在Java中通过下列语句读取:

  Properties p = new Properties();

  InputStream fin = new FileInputStream("Config.properties");

  p.load(fin);

  String maxSize = p.getProperty("cache.maxsize ");

  String minSize = p.getProperty("cache.minsize ");

  这样就可以获得配置文件中的两个值。

  这种配置文件使用方法简单直接,但是只适合配置文件不很复杂的情况。在复杂的配置情况下,properties就不是很合适,例如设置系统的可选属性,一个系统安装在不同应用场合,客户的要求总有些不一样,有些功能是可选的,那么需要在配置文件中配置一些可选的功能,以Tomcat的server.xml为例,如下:

 

  reloadable="true" crossContext="true">

  …

 

 

  reloadable="true" crossContext="true">

 

  prefix="localhost_examples_log." suffix=".txt"

  timestamp="true"/>

  …

 

  在一个配置中有很多Context,每个Contexr都包含Logger等具体配置,XML格式本身是一种树形结构的数据格式。在实际应用中,很多复杂的表示都可以使用树形结构来分解代表。因此,使用XML来表示这种树形结构的数据无疑是非常合适的。

  在Jive中,jive_config.xml是Jive系统的配置文件。这个配置文件是在Jive系统安装时,按照用户的选择动态生成的,其中包含数据库连接参数、界面显示颜色、电子邮件配置以及缓冲配置、搜索配置和文件或图片上传配置。

  分析读取XML数据有很多工具,如DOM(http://www.worg/DOM/)和SAX(http://www.saxproject.org/)。这两种是标准的XML分析器,可以使用任何语言来实现,DOM分析XML数据时,是将整个文档一下子读入内存,如果文档很大,性能就发生影响,而SAX则是动态地对每一行分析,无需全部读入,因此在分析大文档时速度比较快。

  但是这两种分析方法都是围绕XML树形结构展开的,在编制这两种分析器时,会涉及到大量XML概念的API,需要一定的XML基础和知识,使用起来有一定难度。

  JDOM(http://www.jdom.org)封装了DOM/SAX的具体使用技术,以非常符合Java编程方式的形式来分析XML,因此使用起来非常方便。

  在分析速度方面,JDOM比DOM要快,比SAX慢一点。但用在分析配置文件上,速度不是主要的,因为可以使用lazy initialization。这类似缓存机制,在第一次读取后就保存在内存中,以后每次直接从内存中获取。

  在Jive中,JDOM操作基本是由JiveGlobals完成的。

  public class JiveGlobals {

  private static final String JIVE_CONFIG_FILENAME = "jive_config.xml";

  private static XMLProperties properties = null;

  ...

  //从配置文件获取配置

  public static String getJiveProperty(String name) {

  loadProperties();

  return properties.getProperty(name);

  }

  //用JDOM载入配置文件

  private synchronized static void loadProperties() {

  if (properties == null) {

  properties = new XMLProperties(jiveHome + File.separator +

  JIVE_CONFIG_FILENAME);

  }

  }

  //将配置保存到配置文件中

  public static void setJiveProperty(String name, String value) {

  loadProperties();

  properties.setProperty(name, value);

  }

  }

  从上面代码看出,对XML文件读写非常方便,使用properties.getProperty(name)就可以获得name的配置值,而properties.setProperty(name, value)一句就可以将name和其值value保存到XML文件中,非常类似Hashtable的读取和存入。

  XMLProperties是JDOM的一个属性文件辅助包,它主要是对属性名进行分解和合成,例如XML如下:

 

 

  Jive_Administrator

  webmaster@example.com

  Your thread was updated!

  Hello {name}! The thread {threadName} was updated!

 

 

  jive/email/fromName的值是Jive_Administrator,那么如何读取Jive_Administrator?使用properties.getProperty("email.fromName")就可以。注意到,这里Key的名字组合是 email.fromName,这种特定的写法就是XMLProperties可以支持的,在对XML文件保存细节中,由XMLProperties将这种属性名称写法具体转换成XML文档操作。具体内部代码如下:

  public String getProperty(String name) {

  if (propertyCache.containsKey(name)) { //从缓存中获取

  return (String)propertyCache.get(name);

  }

  //将email.fromName转变为String数组

  //例如propName[0] = jive; propName[1] = email …

  String[] propName = parsePropertyName(name);

  // 通过propName数组循环,遍历XML的树形结构层次,寻找出对应的属性值

  Element element = doc.getRootElement();

  for (int i = 0; i < propName.length; i++) {

  element = element.getChild(propName[i]);

  if (element == null) {

  return null;

  }

  }

  // 寻找到element后,获得其内容

  String value = element.getText();

  if ("".equals(value)) { return null; }

  else {

  // 保存到缓存中

  value = value.trim();

  propertyCache.put(name, value);

  return value;

  }

  }

  以上只是分析了JDOM的XMLProperties包是如何做属性配置提取的,正是因为JDOM内部做了很多基础支持性的细节工作,才使得使用JDOM变得非常方便。

  总结使用JDOM对配置文件读写操作语法如下:

  · 获得配置(查询):getProperty(name)。

  · 新增和修改:properties.setProperty(name, value)。

  · 删除:properties.deleteProperty(name)。

  name的格式是xxx.xxx.xxx,例如:

 

  …

 

 

/home/jdon/jive/upload/

  upload/

 

  …

 

  要获得/home/jdon/jive/upload/,name的格式是upload.dir;要获得upload/,name的格式是upload.relurl。

  注意,如果要在系统中支持上述功能,必须下载JDOM包,还要有DataFormatFilter. java、DataUnformatFilter.java、XMLFilterBase.java和XMLProperties.java支持。这几个类不包含在JDOM标准包中,作为一个应用包含在其Sample中。当然也可以直接从Jive中复制出来使用。

  5.3 全文检索和Lucene

  Jive中支持全文检索,这个功能主要核心依赖另外一个开放源代码项目Lucene(http://jakarta.apache.org/lucene/docs/index.html)。Jakarta Lucene是一个高性能全文搜索引擎,可以跨平台应用于任何搜索应用。

  使用Lucene作为搜索引擎,应用系统需要做两件事情:

  (1)建立索引文件。将Jive数据库中的数据内容建立索引文件,这是通过SearchManager来完成。SearchManager代码如下:

  public interface SearchManager {

  public boolean isSearchEnabled();

  public void setSearchEnabled(boolean searchEnabled);

  /**

  //如果SearchManage正在工作,返回真

  public boolean isBusy();

  //返回索引完成率

  public int getPercentComplete();

  //是否自动建立索引

  //通过TaskEngine.scheduleTask方法实现定期自动索引

  public boolean isAutoIndexEnabled();

  public void setAutoIndexEnabled(boolean value);

  //自动索引间隔的分钟数

  public int getAutoIndexInterval();

  public void setAutoIndexInterval(int minutes);

  //获得上次建立索引的时间

  public Date getLastIndexedDate();

  //在实时建立索引时,将当前帖子加入索引

  public void addToIndex(ForumMessage message);

  public void removeFromIndex(ForumMessage message);

  //手动更新自上次建立索引后的新内容

  public void updateIndex();

  //手动重新建立全部的索引

  public void rebuildIndex();

  //优化

  public void optimize();

  }

  · SearchManager定义了建立索引的一些属性,建立索引有两种方式:当有新帖子加入时,通过调用indexMessage()方法实时索引;或者通过TaskEngine.scheduleTask方法每隔一定时间建立索引。

  · DbSearchManager作为SearchManager的子类实现,又是一个线程类,它是建立索引的主要功能类。在DbSearchManager中主要使用了Lucene的IndexWriter、 Analyzer、 Document和 Field等功能类来建立索引。

  · IndexWriter用户建立新的索引,当然也可以将文档加入已经存在的索引。

  在文本被索引之前,它必须通过一个分析器Analyzer。分析器Analyzer 负责从文本中分离出索引关键字。Lucene有几种不同类型的分析器:

  · SimpleAnalyzer是将英文转换为小写字母,按空格和标点符号切分出英文单词,

  如I am Java这一句,使用SimpleAnalyzer切词就会切分出下列词语:

  token1=I

  token2=am

  token3=Java

  · StandardAnalyzer是对英文进行了较为复杂的处理。除了按词语建立索引关键字(token)外,还能够为特殊名称、邮件地址、缩写格式等建立索引单元,而且对“and”、“ the”等词语做了过滤。

  · ChineseAnalyzer是专门用来分析中文的索引的。关于中文分析器,有很多尝试,如车东的http://sourceforge.net/projects/weblucene/;zhoujun的http://www. jdon.com/jive/thread.jsp? forum=61&thread=8400等,该问题将在后面章节继续讨论。

  一个索引是由一系列Document组成,每个Document是由一个或多个Field组成,每个Field都有一个名字和值,可以把Document作为关系数据库中一条记录,而Field则是记录中某列字段。一般建立索引如下:

  //指定将在哪个目录建立索引

  String indexDir = "/home/jdon/jive/WEB-INF/jiveHome";

  //指定将要建立索引的文本

  String text = "welcom here, I am Java,";

  Analyzer analyzer = new StandardAnalyzer(); //使用StandardAnalyzer

  //建立一个IndexWriter

  IndexWriter writer = new IndexWriter(indexDir, analyzer, true);

  //建立Document

  Document document = new Document();

  //进行切词、索引

  document.add(Field.Text("fieldname", text));

  //加入索引中

  writer.addDocument(document);

  writer.close();

  其中,Field根据具体要求有不同用法,Lucene提供4种类型的Field: Keyword、 UnIndexed、 UnStored和 Text。

  · Keyword 不实现切词,逐字地保存在索引中,这种类型适合一些如URL、日期、个人姓名、社会安全号码、电话号码等需要原封不动保留的词语。

  · UnIndexed既不实现切词也不索引,但是其值是一个词一个词地保存在索引中,这不适合很大很长的词语,适合于显示一些不经过直接搜索的结果值。

  · UnStored与UnIndexed正好相反,将被切词和索引,但是不保存在索引中,这适合巨大文本,如帖子内容、页面内容等。

  · Text是实现切词、索引,并且保存在索引中。

  在Jive中,索引的建立以DbSearchManager中加入帖子索引方法为例:

  protected final void addMessageToIndex(long messageID, long userID,

  long threadID, long forumID, String subject, String body,

  java.util.Date creationDate, IndexWriter writer) throws IOException

  {

  //建立一个 Document

  Document doc = new Document();

  doc.add(Field.Keyword("messageID",Long.toString(messageID)));

  doc.add(new Field("userID", Long.toString(userID), false, true, false));

  doc.add(new Field("threadID", Long.toString(threadID), false, true, false));

  doc.add(new Field("forumID", Long.toString(forumID), false, true, false));

  doc.add(Field.UnStored("subject", subject));

  doc.add(Field.UnStored("body", body));

  doc.add(new Field("creationDate", DateField.dateToString(creationDate),

  false, true, false));

  //将该Document加入当前索引中

  writer.addDocument(doc);

  }

  在DbSearchManager中同时也实现了自动建立索引的过程,通过在构造方法中生成TimeTask实例:

  timerTask = TaskEngine.scheduleTask(

  this,autoIndexInterval*JiveGlobals.MINUTE,

  autoIndexInterval*JiveGlobals.MINUTE);

  因为DbSearchManager是线程类,它在run方法中实现索引任务自动运行:

  TaskEngine.addTask(new IndexTask(false));

  (2)建立完成后,就可以直接搜索特定的词语了。搜索语句一般代码如下:

  Searcher searcher = new IndexSearcher((indexDir); //创建一个搜索器

  //使用和索引同样的语言分析器

  Query query = QueryParser.parse(queryString, "body", new StandardAnalyzer());

  //搜索结果使用Hits存储

  Hits hits = searcher.search(query);

  //通过hits得到相应字段的数据和查询的匹配度

  for (int i=0; i

  System.out.println(hits.doc(i).get("fieldname "));

  };

  Jive实现搜索就复杂得多,它为搜索专门建立了一个Query接口:

  public interface Query {

  //需要搜索的字符串

  public String getQueryString();

  public void setQueryString(String queryString);

  public Date getBeforeDate();

  public void setBeforeDate(Date beforeDate);

  public Date getAfterDate();

  public void setAfterDate(Date afterDate);

  public User getFilteredUser();

  public void filterOnUser(User user);

  public ForumThread getFilteredThread();

  public void filterOnThread(ForumThread thread);

  public int resultCount();

  public Iterator results();

  public Iterator results(int startIndex, int numResults);

  }

  Query接口中主要定义了和搜索相关的一些参数,可以根据具体要求定制,直接使用Query就可以达到搜索的目的,如需要搜索Java is cool,那么使用下列代码:

  ForumFactory forumFactory = ForumFactory.getInstance();

  Query query = forumFactory.createQuery(forums);

  query.setQueryString("Jive is cool");

  Iterator iter = query.results();

  while (iter.hasNext()) {

  ForumMessage message = (ForumMessage)iter.nextElement();

  //输出结果

  }

  追查代码会发现,上面forumFactory.createQuery(forums)方法实际内容是new DbQuery(forums, this)。DbQuery作为Query的一个子类,它的搜索语句通过executeQuery()方法中下列语句实现:

  private void executeQuery() {

  try {

  Searcher searcher = getSearcher(); //创建一个搜索器

  …

  //使用分析器获得Query对象

  org.apache.lucene.search.Query bodyQuery =

  QueryParser.parse(queryString, "body", DbSearchManager.analyzer);

  org.apache.lucene.search.Query subjectQuery =

  QueryParser.parse(queryString, "subject", DbSearchManager.analyzer);

  //将两个Query对象加入BooleanQuery

  BooleanQuery comboQuery = new BooleanQuery();

  comboQuery.add(subjectQuery,false,false);

  comboQuery.add(bodyQuery,false,false);

  //Jive自己的搜索结果过滤器

  MultiFilter multiFilter = new MultiFilter(3);

  int filterCount = 0;

  if (factory.getForumCount() != forums.length) {

  //将其他论坛内容搜索结果过滤掉

  String[] forumIDs = new String[forums.length];

  for (int i=0; i

  forumIDs[i] = Long.toString(forums[i].getID());

  }

  multiFilter.add(new FieldFilter("forumID", forumIDs));

  filterCount++;

  }

  //日期过滤器 如只查询某日期以后的内容

  if (beforeDate != null || afterDate != null) {

  if (beforeDate != null && afterDate != null) {

  multiFilter.add(new DateFilter("creationDate", beforeDate, afterDate));

  filterCount++;

  }else if (beforeDate == null) {

  multiFilter.add(DateFilter.After("creationDate", afterDate));

  filterCount++;

  }else {

  multiFilter.add(DateFilter.Before("creationDate", beforeDate));

  filterCount++;

  }

  }

  // 过滤用户

  if (user != null) {

  String userID = Long.toString(user.getID());

  multiFilter.add(new FieldFilter("userID", userID));

  filterCount++;

  }

  // 主题过滤

  if (thread != null) {

  String threadID = Long.toString(thread.getID());

  multiFilter.add(new FieldFilter("threadID", threadID));

  filterCount++;

  }

  if (filterCount > 0) {//实现搜索

  hits = searcher.search(comboQuery, multiFilter);

  } else {

  hits = searcher.search(comboQuery);

  }

  //搜索结果不要超过最大大小

  int numResults = hits.length() < MAX_RESULTS_SIZE ?

  hits.length() : MAX_RESULTS_SIZE;

  long [] messages = new long[numResults];

  for (int i=0; i

  messages[i]= Long.parseLong( ((Document)hits.doc(i)).get("messageID") );

  }

  results = messages;

  } catch (Exception e) {

  e.printStackTrace();

  results = new long[0];

  }

  }

  Jive的搜索使用了过滤器,以便过滤掉不想出现的结果,然后还对搜索结果进行了限制转换,这些在实际使用中都是必需的。

  5.4 Jive的中文问题

  Jive默认的字符集编码方式是ISO8859_1,即Latin-1字符集,这是国际标准化组织用来表示Latin等西方语言使用的字符集。

  ISO8859_1字符集非常类似常见的ASCII字符集。由于ISO8859_1是使用单字节来表示,而汉字是采取双字节来表示一个汉字,我国制定了一套专门用来表示汉字GB2312和GBK编码字符集。

  在Java内部运算中,涉及到的所有字符串都会被转化为UTF-8编码来进行运算。那么,在被Java转化之前,字符串是什么样的字符集? Java总是根据操作系统的默认编码字符集来决定字符串的初始编码,而且Java系统的输入和输出的都是采取操作系统的默认编码。

  因此,如果能统一Java系统的输入、输出和操作系统3者的编码字符集合,将能够使Java系统正确处理和显示汉字。这是处理Java系统汉字的一个原则,但是在实际项目中,能够正确抓住和控制住Java系统的输入和输出部分是比较难的。

  Jive是运行在Web容器中的一个Servlet/JSP系统。在这个系统中,输入途径有很多种:一种是通过页面表单打包成请求(request)发往服务器的;第二种是通过数据库读入;还有第3种输入比较复杂,JSP在第一次运行时总是被编译成Servlet,JSP中常常包含中文字符,那么编译使用javac时,Java将根据默认的操作系统编码作为初始编码。除非特别指定,如在Jbuilder中可以指定默认的字符集。

  输出途径也有几种:第一种是JSP页面的输出。由于JSP页面已经被编译成Servlet,那么在输出时,也将根据操作系统的默认编码来选择输出编码,除非指定输出编码方式;还有输出途径是数据库,将字符串输出到数据库。

  由此看来,一个J2EE系统的输入输出是非常复杂,而且是动态变化的,而Java是跨平台运行的,在实际编译和运行中,都可能涉及到不同的操作系统,如果任由Java自由根据操作系统来决定输入输出的编码字符集,这将不可控制地出现乱码。

  正是由于Java的跨平台特性,使得字符集问题必须由具体系统来统一解决,所以在一个Java应用系统中,解决中文乱码的根本办法是明确指定整个应用系统统一字符集。

  在Jive中如果指定默认字符集为某个字符集,那么就要在所有的输入输出环节都要标识为这个字符集。但是,前面已经提到,要完全在编码时做到还是有一定难度,必须对Web程序有相当地掌握和理解,而且步骤较繁琐。

  有一种相对省事的做法,例如统一指定为ISO8859_1,因为目前大多数软件都是西方人编制的,他们默认的字符集就是ISO8859_1,包括操作系统Linux和数据库MySQL等。这样,如果指定Jive统一编码为ISO8859_1,那么就有下面3个环节必须把握:

  · 开发和编译代码时指定字符集为ISO8859_1。

  · 运行操作系统的默认编码必须是ISO8859_1,如Linux。

  · 在JSP头部声明:<%@ page contentType="text/html;charset=ISO8859_1" %>。

  如果统一指定为GBK中文字符集,上述3个环节同样需要做到,不同的是只能运行在默认编码为GBK的操作系统,如中文Windows。

  所以统一编码为ISO8859_1和GBK虽然带来编制代码的方便,但是也破坏了Java跨平台运行的优越性,只在一定范围内行得通。

  很多情况下,程序员大都是在中文Windows下开发调试Java系统,然后直接部署到Linux等系统上真正运行。而且其中可能涉及到XML文件读写。XML是对编码方式要求严格的数据存储体,XML又可以随着代码移动。因此,在进行真正大规模Java系统开发运行时,上述临时简单的变通方式就没有效果了。

  要从根本上解决Java的中文问题,只要将Java系统的统一编码定义为UTF-8。UTF-8编码是一种兼容所有语言的编码方式,惟一比较麻烦的就是要找到应用系统的所有出入口,然后使用UTF-8去“结扎”它。

  Jive默认的字符集编码方式是ISO8859_1,如果都统一为UTF-8,那么也需要做下列几步工作:

  · 开发和编译代码时指定字符集为UTF-8。

  · 使用过滤器,将所有请求(request)转换为UTF-8;针对不同应用过滤器有两种。

  · 如果所有请求都经过一个Servlet控制分配器,那么使用Servlet的filter执行语句。

  · request.setCharacterEncoding("UTF-8")。

  · 如果不经过Servlet,而直接是JSP,那么每个JSP头部设置上述语句。

  · 在JSP头部声明:<%@ page contentType="text/html;charset= UTF-8" %>。

  · 设定数据库连接方式是UTF-8。

  以上讨论了Jive以及通用Java的中文问题。如果整个应用系统是从开始进行开发,那么统一指定编码为UTF-8就非常容易做到。如果是在英文源代码基础上二次开发,那么首先要将原来的源代码转换为统一编码UTF-8,那么这种转换工作会

<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值