Java 如何在反序列化时重新附加单例Springbean
我想在反序列化了单例范围的依赖项之后,将它们重新注入到原型Springbean中 假设我有一个进程bean,它依赖于存储库bean。存储库bean的作用域为单例,但流程bean的作用域为原型。我会定期序列化进程,然后再反序列化它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”放在成员变量上,该变量在过程中包含对它的引用
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实际上不在上下文中。我以为是经理