如何在反序列化时重新附加单例Spring bean

我想在反序列化之后将单例范围的依赖项重新注入原型Spring bean。

假设我有一个Process bean,它取决于Repository bean。 Repository bean的作用域是单例,但Process bean是原型作用域。 我会定期对序列进行序列化,然后对其进行反序列化。

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

我不想序列化和反序列化存储库。 我也不想将“瞬态”放在成员变量上,该变量在Process中保存对它的引用,也不想对某种代理的引用,或者除了声明为Repository的普通旧成员变量之外的任何东西。

我想我想要的是让Process的依赖关系填充一个可序列化的代理,该代理指向Repository,并且在反序列化时,可以再次找到Repository。 我怎么能自定义Spring呢?

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

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

在反序列化对象时添加使用方面添加注入步骤怎么样?

你需要AspectJ或类似的东西。 它与Spring中的@Configurable函数非常相似。

例如,在“private void readObject(ObjectInputStream in)throws IOException,ClassNotFoundException”方法周围添加一些建议

本文也可能有所帮助: http : //java.sun.com/developer/technicalArticles/Programming/serialization/

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

 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文档http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable 。

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

该支持旨在用于在任何容器控制之外创建的对象。 域对象通常属于此类别,因为它们通常使用new运算符以编程方式创建,或者由于数据库查询而由ORM工具创建。

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

只需将Process类更改为

 @Configurable class Process { @Autowired private transient Repository repository; // getters, setters, etc. } 

每当您创建新实例时

 Process process = new Process(); 

spring会自动注入依赖项。 如果反序列化Process对象,这也适用。

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

如何使用某种ProcessWrapper bean代替可能是单例。 它将注册到Repository,并管理Process的反序列化或为其设置setter。 当在包装器中设置新进程时,它将在进程上调用setRepository() 。 使用Process的bean可以通过包装器使用新的bean设置,也可以调用委托给Process的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); } } 

回答我自己的问题:到目前为止我是如何解决这个问题的,就是创建一个使用便宜的小代理序列化和反序列化的基类。 代理只包含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个字节(如果我没记错的话)。

方法applicationContext.getAutowireCapableBeanFactory().autowireBean(detachedBean); 可用于重新配置已序列化然后反序列化的Spring管理的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); } }