Java 如何在反序列化时重新附加单例Springbean

Java 如何在反序列化时重新附加单例Springbean,java,spring,serialization,dynamic-proxy,Java,Spring,Serialization,Dynamic Proxy,我想在反序列化了单例范围的依赖项之后,将它们重新注入到原型Springbean中 假设我有一个进程bean,它依赖于存储库bean。存储库bean的作用域为单例,但流程bean的作用域为原型。我会定期序列化进程,然后再反序列化它 class Process { private Repository repository; // getters, setters, etc. } 我不想序列化和反序列化存储库。我也不想将“transient”放在成员变量上,该变量在过程中包含对它的引用

我想在反序列化了单例范围的依赖项之后,将它们重新注入到原型Springbean中

假设我有一个进程bean,它依赖于存储库bean。存储库bean的作用域为单例,但流程bean的作用域为原型。我会定期序列化进程,然后再反序列化它

class Process {
   private Repository repository;
   // getters, setters, etc.
}
我不想序列化和反序列化存储库。我也不想将“transient”放在成员变量上,该变量在过程中包含对它的引用,也不想引用某种代理,或者除了声明为存储库的普通旧成员变量之外的任何东西

我想我想要的是让流程用一个可序列化的代理来填充其依赖关系,该代理指向存储库(带有一个临时引用),并且在反序列化后,可以再次找到存储库。我如何定制Spring来做到这一点

我想我可以使用代理来保存依赖项引用,就像。我希望我能使用那种精确的技术。但是我看到Spring生成的代理是不可序列化的,文档说如果我将它与单例bean一起使用,我会得到一个异常


我可以在单例bean上使用一个自定义作用域,当请求自定义作用域bean时,它总是提供一个代理。这是个好主意吗?其他想法?

我认为序列化bean然后强制重新注入依赖项的想法不是最好的架构

不如使用某种ProcessWrapper bean,它可以是一个单例。它将与存储库一起注入,或者管理流程的反序列化,或者为其设置一个setter。当在包装器中设置新进程时,它将对该进程调用
setRepository()
。使用流程的bean可以由包装器设置为新的bean,也可以调用将委托给流程的ProcessWrapper

class ProcessWrapper {
   private Repository repository;
   private Process process;
   // getters, setters, etc.

   public void do() {
      process.do();
   }

   public void setProcess(Process process) {
      this.process = process;
      this.process.setRepository(repository);
   }
}

在反序列化对象时,如何使用方面添加注入步骤

为此,您需要AspectJ或类似的工具。它的工作原理与Spring中的@Configurable函数非常相似

e、 g.围绕“私有void readObject(ObjectInputStream in)抛出IOException,ClassNotFoundException”方法添加一些建议


这篇文章也可能有帮助:

回答我自己的问题:到目前为止,我是如何解决这个问题的,就是创建一个基类,它使用一个便宜的小代理进行序列化和反序列化。代理仅包含bean的名称

您会注意到,它使用一个全局变量来访问Spring上下文;一个更优雅的解决方案可能会将上下文存储在线程局部变量中,类似于这样

public abstract class CheaplySerializableBase 
   implements Serializable, BeanNameAware {

    private String name;

    private static class SerializationProxy implements Serializable {

        private final String name;

        public SerializationProxy(CheaplySerializableBase target) {
            this.name = target.name;
        }

        Object readResolve() throws ObjectStreamException {
            return ContextLoader.globalEvilSpringContext.getBean(name);
        }

    }

    @Override
    public void setBeanName(String name) {
        this.name = name;
    }

    protected Object writeReplace() throws ObjectStreamException {
        if (name != null) {
            return new SerializationProxy(this);
        }
        return this;
    }
}

产生的序列化对象大约为150字节(如果我记得正确的话)。

我使用了这个,没有任何代理:

public class Process implements HttpSessionActivationListener {
    ...
    @Override
    public void sessionDidActivate(HttpSessionEvent e) {
        ServletContext sc = e.getSession().getServletContext();
        WebApplicationContext newContext = WebApplicationContextUtils
            .getRequiredWebApplicationContext(sc);
        newContext.getAutowireCapableBeanFactory().configureBean(this, beanName);
    }
}

该示例适用于web环境,当应用程序服务器序列化会话时,它应该适用于任何ApplicationContext。

Spring提供了此问题的解决方案

看看spring文档

7.8.1使用AspectJ向Spring注入依赖域对象

该支持旨在用于在外部创建的对象 对任何容器的控制。域对象通常属于 这是因为它们通常是通过编程方式创建的 使用新运算符,或通过ORM工具作为数据库查询的结果

诀窍是使用加载时编织。只需使用-javaagent:path/to/org.springframework.instrument-{version}.jar启动jvm。此代理将识别实例化的每个对象,如果用@Configurable注释,则将配置(注入@Autowired或@Resource依赖项)该对象

只需将Process类更改为

@Configurable
class Process {

   @Autowired
   private transient Repository repository;
   // getters, setters, etc.
}
无论何时创建新实例

Process process = new Process();
spring将自动注入依赖项。
如果进程对象被反序列化,这也可以工作。

方法
applicationContext.getAutowireCapableBeanFactory().autowireBean(detachedBean)
可用于重新配置Spring管理的bean,该bean先被序列化,然后被反序列化(其
@Autowired
字段变为
null
)。见下面的例子。为了简单起见,省略了序列化细节

public class DefaultFooService implements FooService {

    @Autowired
    private ApplicationContext ctx;

    @Override
    public SerializableBean bar() {
        SerializableBean detachedBean = performAction();
        ctx.getAutowireCapableBeanFactory().autowireBean(detachedBean);
        return detachedBean;
    }

    private SerializableBean performAction() {
        SerializableBean outcome = ... // Obtains a deserialized instance, whose @Autowired fields are detached.
        return outcome;
    }

}


public class SerializableBean {

    @Autowired
    private transient BarService barService;

    private int value;

    public void doSomething() {
        barService.doBar(value);
    }

}

这些bean位于什么样的应用程序上下文中?webapp上下文?现在,不是webapp上下文。稍后,可能是一个webapp上下文。在这种情况下,上下文是如何引导的?它是一个桌面应用程序吗?它是一个网络应用程序。上下文通过ServletContextListener引导。我们有一点春天;我正在尝试添加更多。我们还没有spring MVC JAR,所以我还没有使用web应用程序上下文。我已经发布了我自己问题的答案,说明了到目前为止我是如何解决这个问题的。但是
RepositoryFactory
如何工作呢?它必须是可序列化的,因此不能引用应用程序上下文。流程和存储库只是一个例子(流程是一个糟糕的例子,对此我很抱歉)。通常,被反序列化的bean是管理用户和我们的webapp之间特定交互的控制器。它们有很多,而且总是有更多的东西要写。它们使用许多不同的服务。它们是否都可以实现相同的接口?然后,您可以编写一个代理包装器来实现该接口并依次委托给每个接口吗。如果它们都不同,那么我同意这个解决方案很麻烦。为什么在反序列化时间自动重新注入丢失的依赖项不是一个好主意/体系结构?我的意思是,您必须将spring已经完成的许多功能复制到手上的代码中。如何找到合适的bean来自动注入。。。。另外,Spring中有许多特性是不会被复制的,因为这个bean实际上不在上下文中。我以为是经理