我希望我的应用程序记住它在磁盘上的状态。因此,我的一些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?
简单的答案是序列化,您可以找到许多关于如何序列化对象和反序列化对象的示例,例如:
http://www.javapractices.com/topic/TopicAction.do?Id=45http://www.tutorialspoint.com/java/java_serialization.htm
并且您可以在@Bean方法中生成反序列化代码。
但我有两个争论,都是基于用数据和getter/setter在javabean中提取数据,以及基于这个javabean 使您的bean可配置
- 使用jpa将这个javabean持久化到DB上,您可以使用类似H2数据库的jdbc:H2:~/test或jdbc:H2:file:/data/sample
- 使用Spring资源并将数据放入属性文件中以读取数据,然后使用@Value
我对Spring Integration bean做了类似的操作基本思想是有一个主要的spring上下文,有时您可以加载一个子上下文。
儿童上下文示例;
<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
您可以简单地通过实现Serializable来实现它。这里有一个示例代码:当第一次运行时,Object被保存。第二次,它被加载。
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();
}
}