Java序列化对象的一个使用案例-使用Http发送对象

来源:互联网 发布:商城模板html源码下载 编辑:程序博客网 时间:2024/06/07 02:34

《 Effective Java 》中序列化一节关于java的序列化存在如下说法:对象序列化(object serialization)API,它提供了一个框架,用来将对象编码成字节流(serializing),并从字节流编码中重新构建对象(deserializing)。一旦对象被序列化后,它的编码就可以从一台正在运行的jvm传到另一台jvm上,或者被存储在磁盘上,供以后反序列化时用(如tomcat的session的可持久化)。序列化技术为 远程通信 提供了标准的线路级对象表示法,也为JavaBeans组件结构提供了标准的 持久化 数据格式。

       关于java序列化,看过不少资料,正好最近在重读Effective Java,然后加上这两天碰到一个需求,能很好的诠释序列化,所以在这边小结下。

        需求:系统中自己写了个内存缓存cache,然后集群内每个节点都单独维护自己各自的内存缓存(ps:有人肯定该说艾斯比,怎么不用Memcached或者Redis,么法,leader的要求…… ),这个内存缓存其实就是基于Map的,key=String,value=Object对象。cache需要提供这样一个方法,要能够刷新集群内所有节点的map中单个KV。

        分析如上需求,map中value保存的是Object,这个Object并不是一般的String之类的,而恰恰就是一个真正名符其实的JavaBean对象,刷新时,需要将新的bean发送各节点,由各节点再各自操作内存中的map。这样,就出现了雏形,各节点需要提供一个http接口,刷新时,将bean对象发到到http上,这样就形成了上述Effective Java说到的场景,为远程通信 提供标准的线路级对象表示法,也即 将Object bean以stream的形式post到http接口,http接口接到stream后,将stream反序列化成bean对象。

       代码实现如下:

       javabean就不列了,是一个很纯粹的bean类,只不过该类一定是序列化后的(关于序列化的高级进阶请参考Effective Java),笔者这里只 implements Serializable 即可满足需求。

       java:HttpPost工具类

[java] view plain copy
  1. import java.io.BufferedReader;  
  2. import java.io.InputStream;  
  3. import java.io.InputStreamReader;  
  4. import java.io.ObjectOutputStream;  
  5. import java.io.OutputStream;  
  6. import java.net.HttpURLConnection;  
  7. import java.net.URL;  
  8.   
  9. import org.apache.commons.io.IOUtils;  
  10. import org.apache.commons.lang.StringUtils;  
  11. import org.apache.log4j.Logger;  
  12.   
  13.   
  14. /** 
  15.  * Http 请求访问工具类 
  16.  *  
  17.  * @author will_awoke 
  18.  * @version 2014-6-26 
  19.  * @see HttpAccessUtil 
  20.  * @since 
  21.  */  
  22. public class HttpAccessUtil  
  23. {  
  24.   
  25.   /** 
  26.    * 日志  
  27.    */  
  28.   private static Logger log = Logger.getLogger(HttpAccessUtil.class);  
  29.   
  30.      
  31.   /** 
  32.    * 采取post方式提交序列化后的object对象 </br> 
  33.    * 另请参考:java.io.ObjectInputStream/ObjectOutputStream 
  34.    * @param requestUrl 请求地址 
  35.    * @param connTimeoutMills 设置连接主机超时,单位:毫秒 
  36.    * @param readTimeoutMills 设置从主机读取数据超时,单位:毫秒 
  37.    * @param serializedObject 序列化后的object对象 
  38.    *  
  39.    * @return remoteHttp返回的结果 
  40.    */  
  41.   public static String httpPostSerialObject(String requestUrl, int connTimeoutMills,  
  42.                         int readTimeoutMills, Object serializedObject) throws Exception    
  43.   {  
  44.     HttpURLConnection httpUrlConn = null;  
  45.     InputStream inputStream = null;  
  46.     InputStreamReader inputStreamReader = null;  
  47.     BufferedReader bufferedReader = null;  
  48.     ObjectOutputStream oos = null;  
  49.     StringBuffer buffer = new StringBuffer();  
  50.     try  
  51.     {  
  52.       URL url = new URL(requestUrl);  
  53.       httpUrlConn = (HttpURLConnection)url.openConnection();  
  54.       // 设置content_type=SERIALIZED_OBJECT  
  55.       // 如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException  
  56.       httpUrlConn.setRequestProperty("Content-Type","application/x-java-serialized-object");  
  57.       httpUrlConn.setConnectTimeout(connTimeoutMills);  
  58.       httpUrlConn.setReadTimeout(readTimeoutMills);  
  59.       // 设置是否向httpUrlConn输出,因为是post请求,参数要放在http正文内,因此需要设为true, 默认情况下是false  
  60.       httpUrlConn.setDoOutput(true);  
  61.       // 设置是否从httpUrlConn读入,默认情况下是true  
  62.       httpUrlConn.setDoInput(true);  
  63.       // 不使用缓存     
  64.       httpUrlConn.setUseCaches(false);  
  65.   
  66.       // 设置请求方式,默认是GET  
  67.       httpUrlConn.setRequestMethod("POST");  
  68.       httpUrlConn.connect();  
  69.   
  70.       if (serializedObject != null)  
  71.       {  
  72.         // 此处getOutputStream会隐含的进行connect,即:如同调用上面的connect()方法,     
  73.         // 所以在开发中不调用上述的connect()也可以,不过建议最好显式调用  
  74.         // write object(impl Serializable) using ObjectOutputStream  
  75.         oos = new ObjectOutputStream(httpUrlConn.getOutputStream());  
  76.         oos.writeObject(serializedObject);  
  77.         oos.flush();  
  78.         // outputStream不是一个网络流,充其量是个字符串流,往里面写入的东西不会立即发送到网络,   
  79.         // 而是存在于内存缓冲区中,待outputStream流关闭时,根据输入的内容生成http正文。所以这里的close是必须的  
  80.         oos.close();  
  81.       }  
  82.       // 将返回的输入流转换成字符串  
  83.       // 无论是post还是get,http请求实际上直到HttpURLConnection的getInputStream()这个函数里面才正式发送出去  
  84.       inputStream = httpUrlConn.getInputStream();//注意,实际发送请求的代码段就在这里   
  85.       inputStreamReader = new InputStreamReader(inputStream, "UTF-8");  
  86.       bufferedReader = new BufferedReader(inputStreamReader);  
  87.   
  88.       String str = null;  
  89.       while ((str = bufferedReader.readLine()) != null)  
  90.       {  
  91.         buffer.append(str);  
  92.       }  
  93.     }  
  94.     catch (Exception e)  
  95.     {  
  96.       log.error(requestUrl + " error ", e);  
  97.       throw e;  
  98.     }  
  99.     finally  
  100.     {  
  101.       try  
  102.       {  
  103.         IOUtils.closeQuietly(bufferedReader);  
  104.         IOUtils.closeQuietly(inputStreamReader);  
  105.         IOUtils.closeQuietly(inputStream);  
  106.         IOUtils.closeQuietly(oos);  
  107.         if (httpUrlConn != null)  
  108.         {  
  109.           httpUrlConn.disconnect();  
  110.         }  
  111.       }  
  112.       catch (Exception e)  
  113.       {  
  114.         log.error(e);  
  115.       }  
  116.     }  
  117.     return buffer.toString();  
  118.   }  
  119.   
  120.   
  121. }  



         这里有点要着重说明下,httpConnect的Content-Type的一定要设置为 application/x-java-serialized-object ,否则http接口处在接收stream的时候会出现java.io.EOFException 。另外:输出对象使用的是ObjectOutputStream的writeObject方法。 
         http接口接收stream,并反序列化为Object bean即可:

[html] view plain copy
  1. //read object from stream 反序列化将stream生成Object对象  
  2. ObjectInputStream ois = new ObjectInputStream(req.getInputStream());//HttpServletRequest req对象  
  3. Object value = ois.readObject();