如何将bean保存(持久化,序列化)到磁盘并在Spring下次运行时恢复(反序列化)它们?

时间:2015-03-04 17:14:31

标签: java spring persistence

我希望我的应用程序能够记住它在磁盘上的状态。所以,我的一些bean是有状态的,我希望保存并加载它们的状态。

假设我使用

之类的方法创建bean
@Bean
    MyBean myBean() {
        MyBean ans;
        if( /* bean is already written to disk */ ) {
            ans = readMyBean();
        }
        else {
            ans = new MyBean();
            ans.property1 = defaultValue1;
            // ...
        }
        return ans;

    }

这是正确的做法吗?或者我需要考虑一些现有的Spring API?

在哪里调用保存方法?

更新

目前我采取以下方式

我正在使用AnnotationConfigApplicationContext来创建我的应用程序。我用java配置类提供它,为bean提供合适的创建者。

在业务逻辑中调用序列化,而在创建代码中进行反序列化:

public class MyConfig {

    protected String getConfigName() {
        String name = getClass().getSimpleName();
        name = name.split("\\$\\$")[0];
        return name;
    }

    @Bean
    MyDirectoryBean myDirectoryBean() {
        MyDirectoryBean ans = new MyDirectoryBean (new File("data/" + getConfigName()));
        return ans;
    }

    @Bean(name="mybean")
    MyBean myBean() {

        MyBean ans = (MyBean) myDirectoryBean().deserialize("mybean");
        if( ans == null ) {
            ans =new MyBean();
            ans.setMyParameter1(100); // etc
        }
        return ans;

    }
}

我的班级MyDirectoryBean能够序列化和反序列化bean。

不幸的是,这种方式要求我两次编写bean名称:在bean创建者注释中以及在反序列化发生的行中。

我想象一些界面来拦截创建过程。可能是一些BeanPreProcessor课程?没有这样的课程。可能是我可以在自定义上下文中覆盖getBean()方法,以便它首先尝试反序列化bean?

3 个答案:

答案 0 :(得分:2)

简单的答案是序列化,您可以找到许多关于如何序列化对象并对其进行反序列化的示例,例如:

http://www.javapractices.com/topic/TopicAction.do?Id=45 http://www.tutorialspoint.com/java/java_serialization.htm

您可以在@Bean方法中创建反序列化代码。

但我有2个辩论,都是基于使用数据和getter / setter在java bean中提取数据,并根据这个java bean使你的bean可配置

  1. 使用jpa将此java bean保存在DB上,你可以使用类似H2数据库和jdbc:h2:〜/ test或jdbc:h2:file:/ data / sample
  2. 使用Spring资源并将数据放入属性文件中,然后使用@Value
  3. 进行读取

答案 1 :(得分:0)

我使用Spring Integration bean做了类似的事情 基本思想是有一个主弹簧上下文,有时可以加载子上下文。

子上下文示例;

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration" xmlns:int-file="http://www.springframework.org/schema/integration/file" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
   <context:property-placeholder/>
   <int:channel id="ch.file.in"/>
   <int-file:inbound-channel-adapter directory="${file.dir}" channel="ch.file.in">
      <int:poller fixed-rate="${file.pollInterval}"/>
   </int-file:inbound-channel-adapter>
   <bean id="fileBatchRunner" class="com.nevexis.dcard.integration.serviceactivator.FileBatchRunner">
      <property name="jobLauncher" ref="jobLauncher"/>
      <property name="job" ref="fileImportJob"/>
   </bean>
   <int:service-activator input-channel="ch.file.in" ref="fileBatchRunner" method="transform"/>
</beans>

然后您可以将其作为子项加载到根上下文:

public class ContextRegisterImpl implements ContextRegister, ApplicationContextAware{
    private ApplicationContext parentCtx;
    private Map<String, ConfigurableApplicationContext> contexts;
    public ContextRegisterImpl() {
        contexts = new HashMap<String, ConfigurableApplicationContext>();
    }
    public void loadContext(String name, String path, Properties props) throws ContextAlreadyLoadedException {
        ConfigurableApplicationContext ctx = 
                new ClassPathXmlApplicationContext(new String[] { path }, false, parentCtx);
        setEnvironment(ctx, props);
        ctx.refresh();
        contexts.put(name, ctx);
    }
    public void shutdownContext(String name) throws ContextNotOpenException {
        ConfigurableApplicationContext context = contexts.get(name);
        if(context != null){
            context.close();
            contexts.remove(name);
        }
    }
    private void setEnvironment(ConfigurableApplicationContext ctx, Properties props) {
        StandardEnvironment env = new StandardEnvironment();
        PropertiesPropertySource pps = new PropertiesPropertySource("childCtxProps", props);
        env.getPropertySources().addLast(pps);
        ctx.setEnvironment(env);
    }
    //getters and setters
}

从磁盘/数据库加载的东西是属性。您必须为每个占位符(${...})提供道具。然后setEnvironment会将它们置于上下文环境中,当您加载它时,property-placeholder会将占位符替换为实际值。

请注意,子上下文可以查看和使用父项中的bean,但是父项不能查看来自其父项的bean。 这个例子是Spring集成的,但与普通bean一样。

我从https://github.com/spring-projects/spring-integration-samples/tree/master/advanced/dynamic-ftp

得到了这个想法

另一种选择是直接在根上下文中加载bean定义:

if(beanExists(beanName, rootCtx)){ throw new BeanExitstException(beanName+" already exists!"); }
        AutowireCapableBeanFactory factory = rootCtx.getAutowireCapableBeanFactory();
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;
        registry.registerBeanDefinition(beanName, beanDef);

请参阅BeanDefinition界面:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanDefinition.html

答案 2 :(得分:0)

您可以通过实施Serializable来实现。这里是一个示例代码:第一次运行时,会保存对象。第二次,它被加载。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SerializeTest implements Serializable{
    private static SerializeTest p;
    private static String fileName;

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        fileName = "test.data";
        File f = new File(fileName);


        if(f.exists() && !f.isDirectory()) {
            FileInputStream fIn = new FileInputStream(fileName);
            ObjectInputStream oIn = new ObjectInputStream (fIn);
            Object object = oIn.readObject();

            if(object instanceof SerializeTest) {
                p = (SerializeTest) object;
                System.out.println("Successfully loaded!");
            }
            else
                System.err.println("Invalid object in " + fileName);

            fIn.close();
            oIn.close();
        }
        else {
            p = new SerializeTest();
            System.out.println("Successfully created!");
        }

        save(p);
    }

    public static void save(SerializeTest p) throws IOException {
        FileOutputStream fOut = new FileOutputStream(fileName);
        ObjectOutputStream oOut = new ObjectOutputStream(fOut);
        oOut.writeObject(p);

        fOut.close();
        oOut.close();
    }
}