我希望我的应用程序能够记住它在磁盘上的状态。所以,我的一些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?
答案 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 :(得分: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();
}
}