微信公众平台开发教程第23篇-SAE不支持XStream框架的解决方案

本文主要是介绍微信公众平台开发教程第23篇-SAE不支持XStream框架的解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题描述

最近几天(2014年8月20日之后),突然有不少网友反应,柳峰博客中的微信公众平台开发代码在SAE上运行会报错,或者是能正常部署,但向公众号发消息没反应。以前也有一些初学者质疑过我博客中的代码是否能正常运行,最后都被我一一证明是由于他们的不理解和粗心导致,但这一次短短几天就有很多人反应同样的问题,这就引起了我的足够重视。对于这种“同样的代码以前可以正常运行,现在却不能运行”的问题,我猜测可能是程序运行环境发生了某种变化,应该是SAE近期做了什么更新导致的。


问题分析

如果Java Web项目中使用了日志工具log4j或者slf4j,并且设置了将日志输出到控制台(console),那么在项目部署到SAE之后,可以在SAE网站的“日志中心”看到应用的相关日志。查看HTTP服务error级别的日志,能够看到如下图所示的错误日志:


为了方便查看和讲解,我对上述错误日志进行了格式化处理,结果如下:

[java]  view plain copy
  1. 101.226.62.83 [27/Aug/2014:17:23:10 +0800] JAVA_SAE_Fatal_error:   
  2. Error for /coreServletjava.lang.NoClassDefFoundError: Could not initialize class org.liufeng.weixin.util.MessageUtil      
  3. at org.liufeng.gywodejia.service.CoreService.processRequest(CoreService.java:40)      
  4. at org.liufeng.gywodejia.servlet.CoreServlet.doPost(CoreServlet.java:54)      
  5. at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)   
  6. at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)   
  7. at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:538)     
  8. at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:478)     
  9. at com.sina.sae.servlet.SaeServletHandler.doHandle(SaeServletHandler.java:49)     
  10. at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)      
  11. at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:517)    
  12. at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:225)      
  13. at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:937)      
  14. at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:406)      
  15. at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)   
  16. at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:871)   
  17. at com.sina.sae.webapp.SaeWebAppContext.doScope(SaeWebAppContext.java:166)    
  18. at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)      
  19. at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:259)    
  20. at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)      
  21. at com.sina.sae.handler.SaeUserInfoHandler.handle(SaeUserInfoHandler.java:105)    
  22. at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)    
  23. at org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:305)   
  24. at org.eclipse.jetty.server.handler.HandlerW yq36.javaruntime   
从日志中的第二行可以看出,在访问/coreServlet时报了一个错误NoClassDefFoundError(类找不到),并且提示org.liufeng.weixin.util.MessageUtil类不能被实例化。在部署的WAR中,MessageUtil.class明明存在,为什么会找不到类呢?我们来看看,MessageUtil.java中到底都写了些什么,源代码如下:

[java]  view plain copy
  1. package org.liufeng.course.util;  
  2.   
  3. import java.io.InputStream;  
  4. import java.io.Writer;  
  5. import java.util.HashMap;  
  6. import java.util.List;  
  7. import java.util.Map;  
  8.   
  9. import javax.servlet.http.HttpServletRequest;  
  10.   
  11. import org.dom4j.Document;  
  12. import org.dom4j.Element;  
  13. import org.dom4j.io.SAXReader;  
  14. import org.liufeng.course.message.resp.Article;  
  15. import org.liufeng.course.message.resp.MusicMessage;  
  16. import org.liufeng.course.message.resp.NewsMessage;  
  17. import org.liufeng.course.message.resp.TextMessage;  
  18.   
  19. import com.thoughtworks.xstream.XStream;  
  20. import com.thoughtworks.xstream.core.util.QuickWriter;  
  21. import com.thoughtworks.xstream.io.HierarchicalStreamWriter;  
  22. import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;  
  23. import com.thoughtworks.xstream.io.xml.XppDriver;  
  24.   
  25. /** 
  26.  * 消息工具类 
  27.  *  
  28.  * @author liufeng 
  29.  * @date 2013-05-19 
  30.  */  
  31. public class MessageUtil {  
  32.   
  33.     /** 
  34.      * 返回消息类型:文本 
  35.      */  
  36.     public static final String RESP_MESSAGE_TYPE_TEXT = "text";  
  37.   
  38.     /** 
  39.      * 返回消息类型:音乐 
  40.      */  
  41.     public static final String RESP_MESSAGE_TYPE_MUSIC = "music";  
  42.   
  43.     /** 
  44.      * 返回消息类型:图文 
  45.      */  
  46.     public static final String RESP_MESSAGE_TYPE_NEWS = "news";  
  47.   
  48.     /** 
  49.      * 请求消息类型:文本 
  50.      */  
  51.     public static final String REQ_MESSAGE_TYPE_TEXT = "text";  
  52.   
  53.     /** 
  54.      * 请求消息类型:图片 
  55.      */  
  56.     public static final String REQ_MESSAGE_TYPE_IMAGE = "image";  
  57.   
  58.     /** 
  59.      * 请求消息类型:链接 
  60.      */  
  61.     public static final String REQ_MESSAGE_TYPE_LINK = "link";  
  62.   
  63.     /** 
  64.      * 请求消息类型:地理位置 
  65.      */  
  66.     public static final String REQ_MESSAGE_TYPE_LOCATION = "location";  
  67.   
  68.     /** 
  69.      * 请求消息类型:音频 
  70.      */  
  71.     public static final String REQ_MESSAGE_TYPE_VOICE = "voice";  
  72.   
  73.     /** 
  74.      * 请求消息类型:推送 
  75.      */  
  76.     public static final String REQ_MESSAGE_TYPE_EVENT = "event";  
  77.   
  78.     /** 
  79.      * 事件类型:subscribe(订阅) 
  80.      */  
  81.     public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";  
  82.   
  83.     /** 
  84.      * 事件类型:unsubscribe(取消订阅) 
  85.      */  
  86.     public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";  
  87.   
  88.     /** 
  89.      * 事件类型:CLICK(自定义菜单点击事件) 
  90.      */  
  91.     public static final String EVENT_TYPE_CLICK = "CLICK";  
  92.   
  93.     /** 
  94.      * 解析微信发来的请求(XML) 
  95.      *  
  96.      * @param request 
  97.      * @return 
  98.      * @throws Exception 
  99.      */  
  100.     @SuppressWarnings("unchecked")  
  101.     public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {  
  102.         // 将解析结果存储在HashMap中  
  103.         Map<String, String> map = new HashMap<String, String>();  
  104.   
  105.         // 从request中取得输入流  
  106.         InputStream inputStream = request.getInputStream();  
  107.         // 读取输入流  
  108.         SAXReader reader = new SAXReader();  
  109.         Document document = reader.read(inputStream);  
  110.         // 得到xml根元素  
  111.         Element root = document.getRootElement();  
  112.         // 得到根元素的所有子节点  
  113.         List<Element> elementList = root.elements();  
  114.   
  115.         // 遍历所有子节点  
  116.         for (Element e : elementList)  
  117.             map.put(e.getName(), e.getText());  
  118.   
  119.         // 释放资源  
  120.         inputStream.close();  
  121.         inputStream = null;  
  122.   
  123.         return map;  
  124.     }  
  125.   
  126.     /** 
  127.      * 文本消息对象转换成xml 
  128.      *  
  129.      * @param textMessage 文本消息对象 
  130.      * @return xml 
  131.      */  
  132.     public static String textMessageToXml(TextMessage textMessage) {  
  133.         xstream.alias("xml", textMessage.getClass());  
  134.         return xstream.toXML(textMessage);  
  135.     }  
  136.   
  137.     /** 
  138.      * 音乐消息对象转换成xml 
  139.      *  
  140.      * @param musicMessage 音乐消息对象 
  141.      * @return xml 
  142.      */  
  143.     public static String musicMessageToXml(MusicMessage musicMessage) {  
  144.         xstream.alias("xml", musicMessage.getClass());  
  145.         return xstream.toXML(musicMessage);  
  146.     }  
  147.   
  148.     /** 
  149.      * 图文消息对象转换成xml 
  150.      *  
  151.      * @param newsMessage 图文消息对象 
  152.      * @return xml 
  153.      */  
  154.     public static String newsMessageToXml(NewsMessage newsMessage) {  
  155.         xstream.alias("xml", newsMessage.getClass());  
  156.         xstream.alias("item"new Article().getClass());  
  157.         return xstream.toXML(newsMessage);  
  158.     }  
  159.   
  160.     /** 
  161.      * 扩展xstream,使其支持CDATA块 
  162.      *  
  163.      * @date 2013-05-19 
  164.      */  
  165.     private static XStream xstream = new XStream(new XppDriver() {  
  166.         public HierarchicalStreamWriter createWriter(Writer out) {  
  167.             return new PrettyPrintWriter(out) {  
  168.                 // 对所有xml节点的转换都增加CDATA标记  
  169.                 boolean cdata = true;  
  170.   
  171.                 @SuppressWarnings("unchecked")  
  172.                 public void startNode(String name, Class clazz) {  
  173.                     super.startNode(name, clazz);  
  174.                 }  
  175.   
  176.                 protected void writeText(QuickWriter writer, String text) {  
  177.                     if (cdata) {  
  178.                         writer.write("<![CDATA[");  
  179.                         writer.write(text);  
  180.                         writer.write("]]>");  
  181.                     } else {  
  182.                         writer.write(text);  
  183.                     }  
  184.                 }  
  185.             };  
  186.         }  
  187.     });  
  188. }  
MessageUtil是消息处理工具类,该类的代码大致可以分为以下3部分:

1)第33~91行:定义了若干常量,用于表示请求消息类型、事件类型和响应消息类型。

2)第93-124行:定义了一个parseXml()方法,通过dom4j工具解析微信服务器发来的xml格式的消息。

3)第126~187行:通过XStream工具将Java消息对象转换成xml。

很明显,问题应该不会出现在第1部分代码中,因为这段代码太平常不过了。我猜想,问题可能与第2、3部分代码中引用的第三方工具dom4j或XStream有关,会不会是SAE做了什么更新不支持dom4j或XStream了呢?要想证明也不难,写一个最简单的Java web工程,其中只用到dom4j或者只用到XStream工具,就能知道是哪里出了问题。好在我认识一个SAE官方的运营人员,就偷了个懒,直接咨询他,他帮忙问过SAE研发人员之后给出的答复是:XStream源码中通过反射机制使用到了sun.misc.Unsafe类,而该类因为安全原因被SAE禁用掉了,这就是为什么用到XStream的项目部署到SAE会报NoClassDefFoundError的原因。噢,原来是这么回事,知道原因了就总能找到解决方案。


问题解决

XStream框架的作用是实现Java对象与XML的互相转换,SAE研发人员建议用其他有类似功能的框架替代,如Xerces、jdom或者dom4j,当然,这是一个很不错的建议,如果是在新的项目中,我肯定会这样做。但现在的问题是,如果真的用其他框架来替换XStream,可能要修改的不仅仅是MessageUtil一个类,这样的改动太大了,我也很难向这么多读者交待。正是出于这种考虑,让我想到了有没有可能通过修改XStream框架的源码来解决问题。

我在XStream官方网站http://xstream.codehaus.org/上找到了xstream-1.3.1.jar对应的源码,导入到Eclipse,然后借助Eclipse强大的搜索功能,很快找到了使用sun.misc.Unsafe的类,我尝试将这些类删除或者修改它们的实现,避免使用sun.misc.Unsafe类,最终得到了一个新的jar包,我将其命名为xstream-1.3.1-sae-liufeng.jar,用它替换以前项目中使用的xstream-1.3.1.jar,最终项目再次顺利地运行在SAE上。

可能很多看到标题进来的读者,就是想知道这个问题是如何解决的,并不想听我哆嗦半天。授人鱼不如授人以渔,我之所以将问题的发现、分析和解决整个过程写出来,也是希望能够帮助更多初学者逐渐掌握自行解决问题的方法。

xstream-1.3.1-sae-liufeng.jar的下载地址:http://download.csdn.net/download/lyq8479/7830911。


声明

我提供的解决方案有些暴力,旨在帮助大家能够在SAE上继续测试学习微信公众平台开发,可能会影响到XStream的性能。如果是作为正式项目使用,在非SAE平台上运行公众平台程序,还是建议用XStream官方原本的jar。


如果觉得博客的文章对你有所帮助,请通过留言或关注下方的微信公众账号来支持柳峰!

 

转帖请注明本文出自柳峰的博客(http://blog.csdn.net/lyq8479),请尊重他人的辛勤劳动成果,谢谢!

这篇关于微信公众平台开发教程第23篇-SAE不支持XStream框架的解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/993505

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

W外链微信推广短连接怎么做?

制作微信推广链接的难点分析 一、内容创作难度 制作微信推广链接时,首先需要创作有吸引力的内容。这不仅要求内容本身有趣、有价值,还要能够激起人们的分享欲望。对于许多企业和个人来说,尤其是那些缺乏创意和写作能力的人来说,这是制作微信推广链接的一大难点。 二、精准定位难度 微信用户群体庞大,不同用户的需求和兴趣各异。因此,制作推广链接时需要精准定位目标受众,以便更有效地吸引他们点击并分享链接

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听