如果您正在使用异步JavaScript和XML(Ajax)进行Java? Web开发,那么您最关心的问题可能就是把数据从服务器传递给客户机。在面向Java开发人员的Ajax系列的第二篇文章中,Philip McCarthy介绍了Java对象序列化的五种方式,并提供了选择最适合应用程序的数据格式和技术所需要的全部信息。
在这个系列的第一篇文章中,我介绍了Ajax的构造块:
·如何用JavaScript XMLHttpRequest对象从Web页面向服务器发送异步请求。
·如何用Java servlet处理和响应请求(向客户机返回XML文档)。
·如何在客户端用响应文档更新页面视图。
这一次,我将继续讨论Ajax开发的基础知识,但是将侧重于许多Java Web开发人员最关心的问题:为客户机生成数据。
多数Java开发人员已经把模型-视图-控制器(MVC)模式应用在他们的Web应用程序上。在传统的Web应用程序中,视图组件由JSP或者其他表示技术(例如Velocity模板)构成。这些表示组件动态地生成全新的HTML页面,替代用户以前正在查看的页面,从而更新用户界面。但是,在Java Web应用程序使用Ajax UI的情况下,基于从XMLHttpRequest的响应接收到的数据,JavaScript客户端代码对于更新用户看到的内容负有最终责任。从服务器的角度来看,视图成为它响应客户机请求而发送的数据表示。
这篇文章侧重于可以用来生成Java对象以数据为中心的视图的技术。我将演示可以把JavaBeans变成XML文档的各种方法,并且讨论每种方法的优劣。您将看到为什么XML并不总是最好的途径:对于简单的Ajax请求来说,传输纯文本更好。最后,我将介绍JavaScript对象标注(JSON)。JSON允许数据以序列化的JavaScript对象图的形式传输,在客户端代码中处理序列化的JavaScript对象图极为容易。
关于示例
我将使用一个示例应用程序和几个用例来演示这里讨论的技术特性和技术。图1显示的极为简单的数据模型可以表示示例用例。这个模型代表在线商店中的顾客帐户。顾客拥有以前订单的集合,每个订单包含几个商品。
图1. 简单的对象模型
虽然XMLHttpRequest对于发送数据使用的格式没有做任何限制,但是对于多数目的来说,只发送传统的表单数据是适合的,所以我的讨论集中在服务器的响应上。响应也可以有基于文本的格式,但是正如它的名字表示的,XMLHttpRequest具有内置的处理XML响应数据的能力。这使XML成为Ajax响应的默认选择,所以我们从XML格式开始讨论。
从Java类产生XML
把Ajax响应作为XML来传递有许多原因:每个支持Ajax的浏览器都有导航XML文档的方法,也有许多服务器端技术可以处理XML数据。通过制定一个方案,描述要交换的文档类型,在Ajax客户端和服务器端之间很容易定义合约,而且如果服务器端架构采用面向服务的方式,那么使用XML也可以允许非Ajax客户机使用您提供的数据。
我将考虑从Java对象产生XML数据的三种方法,并讨论每种方法的优劣。
自行进行序列化
首先,可以从对象图以编程的方式生成XML。这种方式可以简单到只是在每个JavaBean类中实现toXml()方法即可。然后就可以选择合适的XML API,让每个bean提供表示自己状态的元素,并递归地对自己的成员调用对象图。显然,这种方式无法扩展到大量的类,因为每个类都需要专门编写自己的XML生成代码。从好的方面来看,这是一个实现起来简单的方式,没有额外的配置支出或者更复杂的构建过程支出,任何JavaBean图都可以只用几个调用就变成XML文档。
在本系列 前一篇文章的示例代码中,我把XML标记字符串连接在一起,实现了toXml()方法。上次我就提到过,这是个糟糕的方法,因为它把确保标记配对、实体编码等工作的负担放在每个toXml()方法的代码中。在Java平台上有几个XML API可以替您做这些工作,这样您就可以把精力集中在XML的内容上。清单1用JDOM API实现了在线商店示例中表示订单的类中的toXml()(请参阅图1)。
清单1. Order类的toXml()的JDOM实现
public Element toXml() {
Element elOrder = new Element(“order”);
elOrder.setAttribute(“id”,id);
elOrder.setAttribute(“cost”,getFormattedCost());
Element elDate = new Element(“date”).addContent(date);
elOrder.addContent(elDate);
Element elItems = new Element(“items”);
for (Iterator<Item> iter =
items.iterator() ; iter.hasNext() ; ) {
elItems.addContent(iter.next().toXml());
}
elOrder.addContent(elItems);
return elOrder;
}
在这里可以看到用JDOM创建元素、使用属性和添加元素内容有多么简单。递归地调用复合JavaBean的toXml()方法是为了取得它们子图的Element表示。例如,items元素的内容是通过调用Order聚合的每个Item对象上的toXml()得到的。
一旦所有的JavaBean都实现了toXml()方法,那么把任意对象图序列化成XML文档并返回给Ajax客户机就简单了,如清单2所示。
清单2. 从JDOM元素生成XML响应
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws java.io.IOException, ServletException {
String custId = req.getParameter(“username”);
Customer customer = getCustomer(custId);
Element responseElem = customer.toXml();
Document responseDoc = new Document(responseElem);
res.setContentType(“application/xml”);
new XMLOutputter().output(responseDoc,res.getWriter());
}
JDOM再次把工作变得非常简单。只需要在对象图返回的XML元素外面包装一个Document,然后用XMLOutputter把文档写入servlet响应即可。清单3显示了用这种方式生成的XML示例,用JDOM Format.getPrettyFormat()对XMLOutputter进行初始化,格式化得非常好。在这个示例中,顾客只做了一个订单,包含两个商品。
清单3. 代表顾客的XML文档
<?xml version=”1.0″ encoding=”UTF-8″?>
<customer username=”jimmy66″>
<realname>James Hyrax</realname>
<orders>
<order id=”o-11123″ cost=”$349.98″>
<date>08-26-2005</date>
<items>
<item id=”i-55768″>
<name>Oolong 512MB CF Card</name>
<description>512 Megabyte Type 1 CompactFlash card.
Manufactured by Oolong Industries</description>
<price>$49.99</price>
</item>
<item id=”i-74491″>
<name>Fujak Superpix72 Camera</name>
<description>7.2 Megapixel digital camera featuring six
shooting modes and 3x optical zoom. Silver.</description>
<price>$299.99</price>
</item>
</items>
</order>
</orders>
</customer>
自行序列化的不足
有趣的是,清单3中的代码展示了让JavaBean把自己序列化为XM的一个主要不足。假设要用这个文档表示顾客的订单历史视图。在这种情况下,不太可能要显示每个历史订单中每个商品的完整说明,或者告诉顾客他或她自己的姓名。但是如果应用程序有一个ProductSearch类,它就是以Item bean列表的形式返回搜索结果,那么在Item的XML表示中包含说明可能会有帮助。而且,Item类上代表当前库存水平的额外字段,在产品搜索视图中可能就是需要显示的有用信息。但是,不管当前的库存水平是否与当前情况相关(比如对顾客的订单历史来说),这个字段都会从包含Item的任何对象图中序列化出来。
从设计的角度来看,这是数据模型与视图生成耦合的经典问题。每个bean只能用一种途径序列化自己,一成不变的方式意味着Ajax交互最终要交换它们不需要交换的数据,因此造成客户端代码要从文档中找到需要的信息更加困难,而且也会增加带宽消耗和客户端的XML解析时间。这种耦合的另一个后果就是XML的语法不能脱离Java类独立变化。例如,对顾客文档的方案做修改,可能会影响多个Java类,造成它们也不得不做修改和重新编译。
我稍后会解决这些问题,但是首先来看一个对自行序列化方式的可伸缩性问题的解决方案:XML绑定框架。
XML绑定框架
近些年来,已经开发了多个Java API来简化XML文档到Java对象图的绑定过程。多数都提供了XML编排和拆解;也就是说,它们可以在Java对象图和XML之间执行双向会话。这些框架封装了XML处理的全部工作,这意味着应用程序代码只需要处理普通的Java类。它们还希望提供有用的辅助功能,例如文档验证。笼统来说,这些框架采用了两种不同的方式:代码生成和对象到XML映射。我将分别解释这两种方式。
代码生成方式
使用代码生成的框架包括XMLBeans、JAXB、Zeus和JBind。castor也能使用这项技术。这类框架的起点是描述文档数据类型的 XML方案。使用框架提供的工具,就可以生成代表这些方案定义类型的Java类。最后,用这些生成的类编写应用程序,表示自己的模型数据,并通过框架提供的一些辅助机制把数据序列化成XML。
如果应用程序要使用大型XML语法,那么代码生成方式是个很好的方法。在数十个类上编写定制XML序列化代码的可伸缩性问题由此消除。另一方面,也不再需要定义自己的JavaBean。框架生成的Java类通常非常符合XML的结构,所以对它们进行编码很难。而且,生成的类变成哑数据容器,因为一般不能向它们添加行为。一般来说,在应用程序代码中要做些妥协,才能很好地处理方案生成的类型。另一个缺陷是如果修改方案,会造成生成的类也要修改,所以也就会对围绕它们编写的代码带来相应的影响。
这种类型的XML绑定框架在数据拆解时最有用(例如,使用XML文档并把它们转化成Java对象)。除非拥有大型数据模型而且有可能从生成的类中获益,否则基于代码生成的框架对于Ajax应用程序来说可能有很大的杀伤力。
映射方式
采用映射方式的框架包括castor和Apache Commons Betwixt。映射通常是比代码生成更灵活和更轻量的解决方案。首先,可以像通常一样编写JavaBean,包括任何行为以及任何自己喜欢的方便的方法。然后,在运行时,调用框架中基于内省的编排器,并根据对象成员的类型、名称和值生成XML文档。通过定义类的映射文件,可以覆盖默认的绑定策略,并就类在XML中的表示方式对编排器提出建议。
这种方法是在可伸缩性与灵活性之间的良好折中。可以按照自己喜欢的方式编写Java类,编排器负责处理XML。虽然映射定义文件编写起来简单,可伸缩性也足够好,但是映射规则最多只能改变标准的绑定行为,而且在对象结构和它们的XML表示之间总要残留一些耦合。最终,可能不得不在Java表示或XML格式之间任选一个做些折中,才能让映射方法起作用。
数据绑定总结
Dennis Sosnoski就XML数据绑定API的主题,在代码生成和代码映射两个方面写了深入的文章。如果想进一步研究这个领域,我推荐他在castor和代码生成框架方面的精彩文章(请参阅 参考资料中的链接)。
总之,代码生成方式损失了过多的灵活性和方便性,对于典型的Ajax应用程序用处不大。另一方面,基于映射的框架可能工作得很好,但是要恰到好处地调整它们的映射策略,以便从对象生成需要的XML。
所有的XML绑定API都具有手工序列化技术的一个主要不足:模型和视图的耦合。被限制为一个类型一个XML表示,就意味着在网络上总要有冗余数据传输。更严重的问题是,在情况要求客户端代码使用专门视图时,客户端代码却无法得到它,所以可能要费力地处理给定对象图的一成不变的视图。
在传统的Web应用程序开发中,采用页面模板系统把视图生成与控制器逻辑和模型数据干净地分离。这种方法在Ajax 场景中也会有帮助。
页面模板系统
任何通用目的的页面模板技术都可以用来生成XML,从而使Ajax应用程序根据自己的数据模型生成任何XML响应文档。额外收获是:模板可以用简单的、表现力强的标记语言编写,而不是用一行行的Java代码编写。清单5是一个JSP页面,采用了Customer bean并表示出定制的XML视图,适合客户端代码生成订单历史组件。
清单4. 生成订单历史文档的JSP
<?xml version=”1.0″?>
<%@ page contentType=”application/xml” %>
<%@ taglib uri=”http://java.sun.com/jsp/jstl/core” prefix=”c” %>
<c:set var=”cust” value=”${requestScope.customer}”/>
<orderhistory username=”${cust.username}”>
<c:forEach var=”order” items=”${cust.orders}”>
<order id=”${order.id}” cost=”${order.formattedCost}”>
<date>${order.date}</date>
<items>
<c:forEach var=”item” items=”${order.items}”>
<item id=”${item.id}”>
<name><c:out value=”${item.name}” escapeXml=”true”/></name>
<price>${item.formattedPrice}</price>
</item>
</c:forEach>
</items>
</order>
</c:forEach>
</orderhistory>
这个简洁的模板只输出订单历史视图需要的数据,不输出不相关的资料(例如商品说明)。创建产品搜索视图的定制XML应当同样简单,这个视图包含每个商品的完整说明和库存水平。
模板的问题
另一方面,现在我需要为每个不同视图创建一个新JSP,而不能仅仅把需要的对象图组织起来并序列化它。从设计的角度来说,许多人可能会有争议,认为这无论如何是件好事,因为这意味着正式地考虑服务器要生成的文档类型。而且,因为我现在要处理通用的模板环境,而不是特定于XML的API,所以确保标记匹配、元素和属性的顺序正确以及XML实体(例如<或&)正确转义就成了我的责任。JSP的核心out标记使后面这项工作变得很容易,但是不是所有的模板技术都提供了这样的机制。最后,没有方便的途径可以在服务器端根据方案检验生成的XML文档的正确性,但这毕竟不是要在生产环境中做的事,可以方便地在开发期间处理它。
不用XML的响应数据
迄今为止,我介绍的所有技术都用XML文档的形式生成服务器响应。但是,XML有一些问题。其中一个就是延迟。浏览器不能立即解析XML文档并生成DOM模型,所以这会降低某些Ajax组件需要的“迅捷”感,特别是在较慢的机器上解析大型文档的时候更是如此。“现场搜索”就是一个示例,在这种搜索中,当用户输入搜索术语时,就会从服务器提取搜索结果并显示给用户。对于现场搜索组件来说,迅速地响应输入是非常重要的,但是同时它还需要迅速而持续地解析服务器的响应。
延迟是一个重要的考虑因素,但是避免使用XML的最大原因是差劲的客户端DOM API。清单5显示了使用跨浏览器兼容的方式通过DOM得到某个值的时候,通常不得不面对的困难。
清单5. 在JavaScript中导航XML响应文档
// Find name of first item in customer’s last order
var orderHistoryDoc = req.responseXML;
var orders = orderHistoryDoc.getElementsByTagName(“order”);
var lastOrder = orders[orders.length – 1];
var firstItem = lastOrder.getElementsByTagName(“item”)[0];
var itemNameElement = firstItem.firstChild;
var itemNameText = itemNameElement.firstChild.data;
当元素中间存在空白时,情况就变得更加复杂,因为每个元素的firstChild经常是个空白文本节点。现在有JavaScript库可以缓解处理XML文档的麻烦。这些库包括Sarissa(请参阅参考资料)和Google-ajaXSLT,这两个库都把XPath功能添加到了大多数浏览器中。
但是,想想替代方案还是值得的。除了responseXML之外,XMLHttpRequest对象还提供了名为responseText的属性,这个属性只是以字符串的方式提供服务器的响应体。
responseText属性
当服务器需要向客户机发送非常简单的值时,responseText特别方便,它可以避免XML导致的带宽支出和处理支出。例如,简单的true/false响应可以由服务器以纯文本方式返回,可以是逗号分隔的简单的名称或数字列表。但是,一般来说,最好不要在同一个应用程序中把XML响应和纯文本响应混合使用;保持单一数据格式可以让代码抽象和重用更加简单。
responseText与XML响应数据结合时也会有用。在只需要从响应文档中提取单一值的场景中,“欺骗性”地把XML当作文本字符串,而不把它当作结构化的文档对待,会更方便。例如,清单6显示了如何用正则表达式从顾客的订单历史中提取第一笔订单的日期。不过,这实际是种花招,一般不应当依赖XML文档的词汇表达。
清单6. 用正则表达式处理XMLHttpRequest的responseText对象
var orderHistoryText = req.responseText;
var matches = orderHistoryText.match(/<date>(.*?)</date>/);
var date = matches[1];
在某些情况下,采用即时方式使用responseText会比较方便。但是,理想情况下,应当有种途径,可以用一种能够让JavaScript轻松导航、却没有XML处理支出的格式表示复杂的结构化数据。幸运的是,确实存在这样一种格式。
JavaScript对象标注
实际上,JavaScript对象的大部分都由联合数组、数字索引数组、字符串、数字或者这些类型的嵌套组合而成。因为所有类型都可以用JavaScript直接声明,所以可以在一条语句中静态地定义对象图。清单7使用JSON语法声明了一个对象,并演示了如何访问这个对象。大括号表示联合数组(即对象),它的键 -值组合由逗号分隔。方括号表示数字索引数组。
清单7. 用JSON在JavaScript中直接声明一个简单对象
var band = {
name: “The Beatles”,
members: [
{
name: “John”,
instruments: [“Vocals”,”Guitar”,”Piano”]
},
{
name: “Paul”,
instruments: [“Vocals”,”Bass”,”Piano”,”Guitar”]
},
{
name: “George”,
instruments: [“Guitar”,”Vocals”]
},
{
name: “Ringo”,
instruments: [“Drums”,”Vocals”]
}
]
};
// Interrogate the band object
var musician = band.members[3];
alert( musician.name
+ ” played ” + musician.instruments[0]
+ ” with ” + band.name );
既然JSON是一个有趣的语言特性,那么它对Ajax有什么意义呢?妙处在于可以用JSON在Ajax服务器响应中通过网络发送JavaScript对象图。这意味着在客户端可以避免使用笨拙的DOM API对XML进行导航——只需要分析JSON响应,就会立即得到可以访问的JavaScript对象图。但是,首先需要把JavaBean变成JSON。
从Java类产生JSON
不同XML生成技术所具有的优缺点也适用于JSON的产生。而且可以证明,存在需要再次使用表示模板技术的情况。但是,使用JSON在理念上更接近于在应用层之间传递序列化的对象,而不是创建应用程序状态的视图。我将介绍如何用org.json这个JavaAPI在Java类上创建toJSONObject()方法。然后,就可以把JSONObject简单地序列化成JSON。清单8反映了清单1讨论的XML,显示了Order类的toJSONObject()实现。
清单8. Order类的toJSONObject()方法实现
public JSONObject toJSONObject() {
JSONObject json = new JSONObject();
json.put(“id”,id);
json.put(“cost”,getFormattedCost());
json.put(“date”,date);
JSONArray jsonItems = new JSONArray();
for (Iterator<Item> iter =
items.iterator() ; iter.hasNext() ; ) {
jsonItems.put(iter.next().toJSONObject());
}
json.put(“items”,jsonItems);
return json;
}
可以看到,org.json API非常简单。 JSONObject代表JavaScript对象(即联合数组),有不同的put()方法,方法接受的String键和值是原生类型、String类型或其他JSON类型。JSONArray代表索引数组,所以它的put()方法只接受一个值。请注意在清单8中,创建jsonItems数组,然后再用put()把它附加到json对象上;可以用另外一种方法做这项工作,就是对每个项目调用 json.accumulate(“items”,iter.next().toJSONObject());。accumulate()方法与put()类似,区别在于它把值添加到按照键进行识别的索引数组。
清单9显示了如何序列化JSONObject并把它写入servlet响应。
清单9. 从JSONObject生成序列化的JSON响应
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws java.io.IOException, ServletException {
String custId = req.getParameter(“username”);
Customer customer = getCustomer(custId);
res.setContentType(“application/x-json”);
res.getWriter().print(customer.toJSONObject());
}
可以看到,它实际上什么也没有做。在这里隐式调用的JSONObject的toString()方法做了所有工作。请注意,application/x-json内容类型还有一点不确定 —— 在编写这篇文章的时候,关于JSON应当属于什么MIME类型还没有定论。但是,目前application/x-json是合理的选择。清单10显示了这个servlet代码的示例响应。
清单10. Customer bean的JSON表示
{
“orders”: [
{
“items”: [
{
“price”: “$49.99”,
“description”: “512 Megabyte Type 1 CompactFlash card.
Manufactured by Oolong Industries”,
“name”: “Oolong 512MB CF Card”,
“id”: “i-55768”
},
{
“price”: “$299.99”,
“description”: “7.2 Megapixel digital camera featuring six
shooting modes and 3x optical zoom. Silver.”,
“name”: “Fujak Superpix72 Camera”,
“id”: “i-74491”
}
],
“date”: “08-26-2005”,
“cost”: “$349.98”,
“id”: “o-11123”
}
],
“realname”: “James Hyrax”,
“username”: “jimmy66”
}
在客户端使用JSON
处理的最后一步是把在客户端把JSON数据变成JavaScript对象。这可以通过对eval()的简单调用实现,这个函数可以即时地解释包含JavaScript表达式的字符串。清单11把JSON响应转变成JavaScript对象图,然后执行清单5的任务,从顾客的最后一次订单中得到第一个商品的名称。
清单11. 评估JSON响应
var jsonExpression = “(” + req.responseText + “)”;
var customer = eval(jsonExpression);
// Find name of first item in customer’s last order
var lastOrder = customer.orders[customer.orders.length-1];
var name = lastOrder.items[0].name;
比较清单11和清单5可以发现使用JSON的客户端的优势。如果在Ajax项目中要在客户端对许多复杂的服务器响应进行导航,那么JSON可能适合您的需要。JSON和XMLHttpRequest结合还会让Ajax交互看起来更像RPC调用而不是SOA请求,这对应用程序的设计可能会有意义。在下一篇文章中,我要研究的框架,就是明确地为了让JavaScript代码对服务器端对象进行远程方法调用而设计的。
JSON的不足
JSON也有它的不足。使用这里介绍的JSON方式,就没有办法针对每个请求对对象的序列化进行裁剪,所以不需要的字段可能经常会在网络上发送。另外,添加toJSONObject()方法到每个JavaBean,可伸缩性不太好,虽然用内省和标注编写一个通用的JavaBean到JSON的序列化器可能很简单。最后,如果服务器端代码是面向服务的,没有单独针对处理Ajax客户请求调整过,那么由于对XML一致的支持,XML会是更好的选择。
比较序列化技术
现在已经看到了把Java状态传输到Ajax客户端的五种不同技术。我讨论了自行手工编码XML序列化、通过代码生成的XML绑定、通过映射机制的XML绑定、基于模板的XML生成以及手工编码到JSON的序列化。每种技术都有自己的优势和不足,分别适用于不同的应用程序架构。
为了总结每种方式的优势与不足,表1从六个方面进行了粗略的评分:
可伸缩性
描述技术适应大量数据类型的容易程度。对于每个附加类型,编码和配置工作量是否会增长?
易于集成
评估把技术集成到项目的简单程度。是否需要更加复杂的构建过程?是否增加了部署的复杂性?
Java类API
描述以指定方式处理服务器端Java对象的容易程度。是可以编写普通的bean,还是不得不处理笨拙的文档表示?
对输出的控制
描述对类的序列化表示控制的精确程度。
视图灵活性
评估从同一组对象是否可以创建不同的、定制的数据序列化。
客户端数据访问
描述JavaScript代码处理服务器响应数据的难易程度。
表1. 数据生成技术的相对价值自行编写XML通过代码生成的XML绑定通过映射的 XML绑定页面模板XML手工编码的JSON序列化
结束语
表1中的数据并不表明某项序列化技术比其他的技术好。毕竟,六种标准的相对重要性取决于项目的具体情况。例如,如果要处理数百种数据类型,这时想要的是可伸缩性,那么代码生成可能就是最好的选择。如果需要为同一数据模型生成多个不同视图,那么就应当使用页面模板。如果处理的是小规模项目,想降低需要编写的JavaScript代码数量,那么请考虑JSON。
希望这篇文章为您提供了选择适合自己应用程序的序列化技术所需要的信息。请参阅参考资料 一节,学习关于这里讨论的技术的更多内容。您还应当继续关注这个系列的下一篇文章,在下一篇文章中,我将介绍如何用直接Web远程(DWR)编写Java Ajax应用程序。DWR框架支持从JavaScript代码中直接调用Java类上的方法。换句话说,它替您负责数据序列化的工作,所以您可以在更高的抽象层次上使用Ajax。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
内存数据网格提供商一头扎进Java
10年的时间里,应用性能解决方案提供商Alachisoft一直在用NCache(针对N-Tier和网格计算.NET应用的内存计算和数据网格产品)为.NET社区服务。
-
遇到这样一个问题:通过java service wrapper部署应用,wrapper进程占用的内存会一直升高, 直到把内存吃完应用崩溃,但是这个wrapper
遇到这样一个问题:通过java service wrapper部署应用,wrapper进程占用的内存会一直升高 […]
-
Google App Engine for Java 对于目前中国需要学习吗?
-
前无古人后无来者的Java平台
开发人员一直在致力于保持Java的活力,经过20年后,我们感觉从来没有更好的、更令人激动的时刻如同Java社区一样。