使用序列化将对象传递给另一个JVM - 相同的Java版本和jar(都运行我们的应用程序)

时间:2013-07-22 18:58:00

标签: java http java-ee serialization

更新: 现在使用地图。想要向其他实例发送内容的类发送对象即路由字符串。

使用对象流,使用Java serializable将对象写入servlet。

首先写入字符串,然后写入对象。

接收servlet将输入流包装在ObjectInputStream周围。首先读取字符串,然后读取Object。路由字符串决定它去了。

更通用的方法可能是发送类名及其声明的方法或Spring bean名称,但这对我们来说已经足够了。


原始问题

了解基本方法,但需要步骤的详细信息。还知道我可以使用Jaxb或RMI或EJB ...但是想使用纯序列化到bytearray然后进行编码,将它从jvm 1中的servlet 1发送到jvm 2中的servlet 2(同一个中的两个app server实例)局域网,在两个J2EE应用程序中设置相同的Java版本和jar)

基本步骤是(Approcah 1): -

  1. 将任何Serializable对象序列化为字节数组并生成一个字符串。确切的代码见下文

  2. Base64输出1.是否需要基于64或可以跳过第2步?

  3. 使用java.util.URLEncode.encode对字符串进行编码

  4. 在命名参数后使用apache http组件或URL类从servlet 1发送到2

  5. 关于Servlet 2的
  6. J2EE框架已经有URLDecoced它,现在只需执行反向步骤并根据param名称转换为对象。 既然两者都是我们的应用程序,我们就会知道类型/类映射的param名称。基本上寻找最快的&在JVM之间发送对象的最便捷方式。

  7. 示例: POJO类发送

    package tst.ser;
    
    import java.io.Serializable;
    
    public class Bean1 implements Serializable {
        /**
         * make it 2 if add something without default handling
         */
        private static final long serialVersionUID = 1L;
        private String s;
    
        public String getS() {
            return s;
        }
    
        public void setS(String s) {
            this.s = s;
        }   
    
    }
    

    *实用程序*

    package tst.ser;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.net.URLEncoder;
    
    public class SerUtl {
    
        public static String serialize(Object o) {
            String s = null;
            ObjectOutputStream os = null;
            try {
                os = new ObjectOutputStream(new ByteArrayOutputStream());
                os.writeObject(o);
                s = BAse64.encode(os.toByeArray());
    
    
                //s = URLEncoder.encode(s, "UTF-8");//keep this for sending part
    
            } catch (Exception e) {
                // TODO: logger
                e.printStackTrace();
                return null;
            } finally {
                // close OS but is in RAM
                try {
                    os.close();// not required in RAM
                } catch (Exception e2) {// TODO: handle exception logger
                }
                os = null;
            }
            return s;
        }
    
        public static Object deserialize(String s) {
            Object o = null;
            ObjectInputStream is = null;
    
            try {
                // do base 64 decode if done in serialize
                is = new ObjectInputStream(new ByteArrayInputStream(
                        Base64.decode(s)));
                o = is.readObject();
            } catch (Exception e) {
                // TODO: logger
                e.printStackTrace();
                return null;
            } finally {
                // close OS but is in RAM
                try {
                    is.close();// not required in RAM
                } catch (Exception e2) {// TODO: handle exception logger
                }
                is = null;
            }
            return o;
        }
    
    }
    

    ****示例发送servlet ***

        Bean1 b = new Bean1(); b.setS("asdd");
        String s = SerUtl.serialize(b);
                //do UrlEncode.encode here if sending lib does not.
        HttpParam p = new HttpParam ("bean1", s);
        //http components send obj
    

    ****样本接收servlet ***

        String s = request.getParameter("bean1");
        Bean1 b1 = (Beean1)SerUtl.deserialize(s);
    

4 个答案:

答案 0 :(得分:7)

  

将任何Serializable对象序列化为字节数组

  

并制作一个字符串。

没有

  

确切陈述见下文

os = new ObjectOutputStream(new ByteArrayOutputStream());
os.writeObject(o);
s = os.toString();

// s = Base64.encode(s);//Need this some base 64 impl like Apache ?
s = URLEncoder.encode(s, "UTF-8");

这些陈述甚至没有做你所描述的,在任何情况下都是不正确的。 OutputStream.toString()不会将任何字节转换为字符串,它只返回唯一的对象标识符。

  

Base64输出1。

base64输出应该使用字节数组作为输入,而不是String。 String不是二进制数据的容器。请参阅下面的更正代码。

ByteArrayOutputStream baos = new ByteArrayOutputStream();
os = new ObjectOutputStream(baos);
os.writeObject(o);
os.close();
s = Base64.encode(baos.toByeArray()); // adjust to suit your API
s = URLEncoder.encode(s, "UTF-8");

这至少可以实现你的目标。

  

是否需要基于64或可以跳过第2步?

如果你想要一个字符串,你必须以某种方式对其进行编码。

  

使用java.util.URLEncode.encode对字符串

进行编码

仅当您将其作为GET或POST参数发送时,才需要这样做。

  

在命名params后,使用apache http组件或URL类从servlet 1发送到2

  

在Servlet 2上,J2EE框架已经对其进行了URL解码,现在只需执行反向步骤并根据参数名称转换为对象。

是的,但请记住直接从base64编码的字符串转到字节数组,没有中间字符串。

  

基本上寻找最快的&最方便的在JVM之间发送对象的方法。

这些目标不一定是可以协调的。这些天最方便的可能是XML或JSON,但我怀疑它们比序列化更快。

  

os = null;

将即将超出范围的引用设置为null毫无意义。

  

HttpParam p = new HttpParam(“bean1”,s);

HttpParam可能会为你做URLEncoding。检查一下。

答案 1 :(得分:3)

您无需转换为字符串。您可以将二进制数据直接发布到servlet,例如通过在HttpUrlConnection的输出流之上创建ObjectOutputStream。将请求方法设置为POST。

处理post的servlet可以从HttpServletRequest的ServletInputStream创建的ObjectStream中反序列化。

不过,我建议JAXB随时使用二进制序列化。这些框架不仅非常适合互操作性,还可加快开发速度并创建更强大的解决方案。

我看到的优点是更好的工具,类型安全和代码生成,保持您的选项打开,以便您可以从其他版本或其他语言调用您的代码,并更容易调试。不要低估难以解决因意外地将错误类型或双重转义数据发送到servlet而导致的错误的成本。我希望性能优势太小,无法弥补这一点。

答案 2 :(得分:2)

发现这个Base64 impl对我来说很重要:http://iharder.net/base64

有实用方法:

 String encodeObject(java.io.Serializable serializableObject, int options )
Object decodeToObject(String encodedObject, int options, final ClassLoader loader )

使用:

try {
            String dat = Base64.encodeObject(srlzblObj, options);
            StringBuilder data = new StringBuilder().append("type=");
            data.append(appObjTyp).append("&obj=").append(java.net.URLEncoder.encode(dat, "UTF-8"));

使用类型参数告诉接收JVM我正在发送什么类型的对象。每个servlet / jsps最多接收4种类型,通常是1.再次,因为它我们自己的应用程序和类,我们发送它很快(如通过网络发送)和简单。

另一端通过以下方式解压缩:

        String objData = request.getParameter("obj");   
        Object obj = Base64.decodeToObject(objData, options, null);

处理它,编码结果,发回结果:

        reply = Base64.encodeObject(result, options);
        out.print("rsp=" + reply);

调用servlet / jsp得到结果:

            if (reply != null && reply.length() > 4) {
                String objDataFromServletParam = reply.substring(4);
                Object obj = Base64.decodeToObject(objDataFromServletParam, options, null);

选项可以是0或Base64.GZIP

答案 3 :(得分:1)

您也可以使用JMS。 Apache Active-MQ是一个很好的解决方案。您不必为所有这些转换而烦恼。

  /**
 * @param objectToQueue
 * @throws JMSException
 */
public void sendMessage(Serializable objectToQueue) throws JMSException 
{
    ObjectMessage message = session.createObjectMessage();
    message.setObject(objectToQueue);
    producerForQueue.send(message);
}

/**
 * @param objectToQueue
 * @throws JMSException
 */
public Serializable receiveMessage() throws JMSException 
{
    Message message = consumerForQueue.receive(timeout);
    if (message instanceof ObjectMessage) 
          { 
              ObjectMessage objMsg = (ObjectMessage) message;
              Serializable sobject = objMsg.getObject();
              return sobject;
          }
    return null;
}

我的观点是do not write custom code for Serialization, iff it can be avoided

当您使用AMQ时,您需要做的就是使您的POJO可序列化。 Active-MQ功能负责序列化。

如果您想从AMQ快速响应,请使用vm-transport。它将最小化n / w开销。 您将自动获得AMQ功能的好处。

我建议这是因为

  • 您在网络上运行自己的应用程序。
  • 您需要一种传输对象的机制。
  • 您还需要一种方法来监控它。

如果你选择自定义解决方案,你可能需要自己解决上述问题。