团队,我们正在将spring webflow从1.x升级到2.4。
在SWF 1.x中,我们只对整个会话使用了一个flowExecutionKey,并且还禁用了后退导航。
在SWF 2.4中,我们想要有相同的行为,
我们可以通过将max-execution-snapshots配置为0来禁用快照,如下所示
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-repository max-executions="1" max-execution-snapshots="0"/>
</webflow:flow-executor>
但是在导航时,当我们调查此问题时,快照ID会不断增加(like e1s1, e1s2 ...)
,这会导致问题,
AbstractFlowExecutionRepository
具有以下字段,这有助于在true时为每个导航生成新的snapshotId,
alwaysGenerateNewNextKey
默认情况下为真。
在SWF 1.x中有一个选项可以通过RepositoryType.SINGLEKEY
设置为false,但我们找不到任何等效的方法在SWF 2.4中将此值设置为false。
如果有办法覆盖"alwaysGenerateNewNextKey"
答案 0 :(得分:0)
现在我知道这很难看,但是你可以做到这一点:
制作您自己的FlowExecutorFactoryBean
和FlowElementAttribute
副本,将其命名为MyFlowExecutorFactoryBean
和MyFlowElementAttribute
(将MyFlowExecutorFactoryBean
中的引用从FlowElementAttribute
更改为{ {1}})。
MyFlowElementAttribute
中的在MyFlowExecutorFactoryBean
上设置了您想要的属性,例如executionRepository
结果如下:
setAlwaysGenerateNewNextKey(true)
现在在您的网络流配置中使用它:
public class MyFlowExecutorFactoryBean implements FactoryBean<FlowExecutor>, ApplicationContextAware, BeanClassLoaderAware, InitializingBean {
private static final String ALWAYS_REDIRECT_ON_PAUSE = "alwaysRedirectOnPause";
private static final String REDIRECT_IN_SAME_STATE = "redirectInSameState";
private FlowDefinitionLocator flowDefinitionLocator;
private Integer maxFlowExecutions;
private Integer maxFlowExecutionSnapshots;
private Set<MyFlowElementAttribute> flowExecutionAttributes;
private FlowExecutionListenerLoader flowExecutionListenerLoader;
private ConversationManager conversationManager;
private ConversionService conversionService;
private FlowExecutor flowExecutor;
private MvcEnvironment environment;
private ClassLoader classLoader;
/**
* Sets the flow definition locator that will locate flow definitions needed for execution. Typically also a
* {@link FlowDefinitionRegistry}. Required.
* @param flowDefinitionLocator the flow definition locator (registry)
*/
public void setFlowDefinitionLocator(FlowDefinitionLocator flowDefinitionLocator) {
this.flowDefinitionLocator = flowDefinitionLocator;
}
/**
* Set the maximum number of allowed flow executions allowed per user.
*/
public void setMaxFlowExecutions(int maxFlowExecutions) {
this.maxFlowExecutions = maxFlowExecutions;
}
/**
* Set the maximum number of history snapshots allowed per flow execution.
*/
public void setMaxFlowExecutionSnapshots(int maxFlowExecutionSnapshots) {
this.maxFlowExecutionSnapshots = maxFlowExecutionSnapshots;
}
/**
* Sets the system attributes that apply to flow executions launched by the executor created by this factory.
* Execution attributes may affect flow execution behavior.
* @param flowExecutionAttributes the flow execution system attributes
*/
public void setFlowExecutionAttributes(Set<MyFlowElementAttribute> flowExecutionAttributes) {
this.flowExecutionAttributes = flowExecutionAttributes;
}
/**
* Sets the strategy for loading the listeners that will observe executions of a flow definition. Allows full
* control over what listeners should apply to executions of a flow definition launched by the executor created by
* this factory.
*/
public void setFlowExecutionListenerLoader(FlowExecutionListenerLoader flowExecutionListenerLoader) {
this.flowExecutionListenerLoader = flowExecutionListenerLoader;
}
/**
* Sets the service type that manages conversations and effectively controls how state is stored physically when a
* flow execution is paused.
*/
public void setConversationManager(ConversationManager conversationManager) {
this.conversationManager = conversationManager;
}
// implementing ApplicationContextAware
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
environment = MvcEnvironment.environmentFor(applicationContext);
}
// implement BeanClassLoaderAware
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
// implementing InitializingBean
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(flowDefinitionLocator, "The flow definition locator property is required");
if (conversionService == null) {
conversionService = new DefaultConversionService();
}
MutableAttributeMap<Object> executionAttributes = createFlowExecutionAttributes();
FlowExecutionImplFactory executionFactory = createFlowExecutionFactory(executionAttributes);
DefaultFlowExecutionRepository executionRepository = createFlowExecutionRepository(executionFactory);
executionRepository.setMaxSnapshots(0);
executionRepository.setAlwaysGenerateNewNextKey(false);
executionFactory.setExecutionKeyFactory(executionRepository);
flowExecutor = new FlowExecutorImpl(flowDefinitionLocator, executionFactory, executionRepository);
}
// implementing FactoryBean
@Override
public Class<?> getObjectType() {
return FlowExecutor.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public FlowExecutor getObject() throws Exception {
return flowExecutor;
}
private MutableAttributeMap<Object> createFlowExecutionAttributes() {
LocalAttributeMap<Object> executionAttributes = new LocalAttributeMap<Object>();
if (flowExecutionAttributes != null) {
for (MyFlowElementAttribute attribute : flowExecutionAttributes) {
executionAttributes.put(attribute.getName(), getConvertedValue(attribute));
}
}
putDefaultFlowExecutionAttributes(executionAttributes);
return executionAttributes;
}
private void putDefaultFlowExecutionAttributes(LocalAttributeMap<Object> executionAttributes) {
if (!executionAttributes.contains(ALWAYS_REDIRECT_ON_PAUSE)) {
Boolean redirect = environment != MvcEnvironment.PORTLET;
executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, redirect);
}
if (!executionAttributes.contains(REDIRECT_IN_SAME_STATE)) {
Boolean redirect = environment != MvcEnvironment.PORTLET;
executionAttributes.put(REDIRECT_IN_SAME_STATE, redirect);
}
}
private DefaultFlowExecutionRepository createFlowExecutionRepository(FlowExecutionFactory executionFactory) {
ConversationManager conversationManager = createConversationManager();
FlowExecutionSnapshotFactory snapshotFactory = createFlowExecutionSnapshotFactory(executionFactory);
DefaultFlowExecutionRepository rep = new DefaultFlowExecutionRepository(conversationManager, snapshotFactory);
if (maxFlowExecutionSnapshots != null) {
rep.setMaxSnapshots(maxFlowExecutionSnapshots);
}
return rep;
}
private ConversationManager createConversationManager() {
if (conversationManager == null) {
conversationManager = new SessionBindingConversationManager();
if (maxFlowExecutions != null) {
((SessionBindingConversationManager) conversationManager).setMaxConversations(maxFlowExecutions);
}
}
return this.conversationManager;
}
private FlowExecutionSnapshotFactory createFlowExecutionSnapshotFactory(FlowExecutionFactory executionFactory) {
if (maxFlowExecutionSnapshots != null && maxFlowExecutionSnapshots == 0) {
maxFlowExecutionSnapshots = 1;
return new SimpleFlowExecutionSnapshotFactory(executionFactory, flowDefinitionLocator);
} else {
return new SerializedFlowExecutionSnapshotFactory(executionFactory, flowDefinitionLocator);
}
}
private FlowExecutionImplFactory createFlowExecutionFactory(AttributeMap<Object> executionAttributes) {
FlowExecutionImplFactory executionFactory = new FlowExecutionImplFactory();
executionFactory.setExecutionAttributes(executionAttributes);
if (flowExecutionListenerLoader != null) {
executionFactory.setExecutionListenerLoader(flowExecutionListenerLoader);
}
return executionFactory;
}
// utility methods
private Object getConvertedValue(MyFlowElementAttribute attribute) {
if (attribute.needsTypeConversion()) {
Class<?> targetType = fromStringToClass(attribute.getType());
ConversionExecutor converter = conversionService.getConversionExecutor(String.class, targetType);
return converter.execute(attribute.getValue());
} else {
return attribute.getValue();
}
}
private Class<?> fromStringToClass(String name) {
Class<?> clazz = conversionService.getClassForAlias(name);
if (clazz != null) {
return clazz;
} else {
try {
return ClassUtils.forName(name, classLoader);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unable to load class '" + name + "'");
}
}
}
class MyFlowElementAttribute {
/**
* The name of the attribute.
*/
private String name;
/**
* The value of the attribute before type-conversion.
*/
private String value;
/**
* The attribute type, optional, but necessary for type conversion.
*/
private String type;
public MyFlowElementAttribute(String name, String value, String type) {
Assert.hasText(name, "The name is required");
Assert.hasText(value, "The value is required");
this.name = name;
this.value = value;
this.type = type;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public String getType() {
return type;
}
public boolean needsTypeConversion() {
return type != null && type.length() > 0;
}
}
}
我还没有完全测试它,但它应该可以工作
答案 1 :(得分:0)
谢谢,
我还修改了它,扩展了DefaultFlowExecutionRepository,如果可能,请查看是否可以,并希望它对某人有帮助,
<bean id="flowExecutionRepository" class="com.custom.module.SingleFlowExecutionRepository">
<constructor-arg index="0" ref="conversationManager"/>
<constructor-arg index="1" ref="snapshotFactory"/>
</bean>
<bean id="flowExecutor" class="org.springframework.webflow.executor.FlowExecutorImpl">
<constructor-arg index="0" ref="flowRegistry"/>
<constructor-arg index="1" ref="executionFactory" name="executionFactory"/>
<constructor-arg index="2" ref="flowExecutionRepository" name="executionRepository"/>
</bean>
并且
公共类SingleFlowExecutionRepository扩展了DefaultFlowExecutionRepository {
public SingleFlowExecutionRepository(ConversationManager conversationManager,
FlowExecutionSnapshotFactory snapshotFactory) {
super(conversationManager, snapshotFactory);
/**
* Set to maintain single snapshot for a session.
*/
super.setAlwaysGenerateNewNextKey(false);
}