8

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

假设我有一个 Process bean,它依赖于一个 Repository bean。Repository bean 的作用域是单例,但 Process bean 是原型作用域的。我定期序列化进程,然后反序列化它。

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

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

我想我想要的是让 Process 的依赖项填充一个可序列化的代理,该代理指向(具有临时引用)存储库,并且在反序列化后,可以再次找到存储库。我如何自定义 Spring 来做到这一点?

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

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

4

6 回答 6

3

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

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。

于 2011-09-14T01:37:30.953 回答
3

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: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 会自动注入依赖。如果 Process 对象被反序列化,这也有效。

于 2012-02-07T20:44:07.117 回答
1

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

使用某种 ProcessWrapper bean 代替它可能是一个单例怎么样。它将与 Repository 一起注入并管理 Process 的反序列化或为其设置一个 setter。当在包装器中设置一个新进程时,它会调用setRepository()该进程。使用 Process 的 bean 可以由包装器设置为新的,也可以调用 ProcessWrapper 委托给 Process。

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);
   }
}
于 2010-08-12T21:03:24.593 回答
1

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

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

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

这篇文章也可能有帮助:http: //java.sun.com/developer/technicalArticles/Programming/serialization/

于 2010-08-12T21:48:11.647 回答
1

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

于 2010-09-29T18:39:31.447 回答
0

该方法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);
    }

}
于 2018-06-07T15:05:55.933 回答