Spring webflow SpringWebFlow2.x-如何将AlwaysGenerateNewExtKey设置为false以使用单个快照
团队,我们正在将SpringWebFlow从1.x升级到2.4 在SWF1.x中,我们在整个会话中只使用了一个flowExecutionKey,还禁用了反向导航 在SWF2.4中,我们希望有相同的行为 我们可以通过将最大执行快照配置为0来禁用快照,如下所示Spring webflow SpringWebFlow2.x-如何将AlwaysGenerateNewExtKey设置为false以使用单个快照,spring-webflow,spring-webflow-2,Spring Webflow,Spring Webflow 2,团队,我们正在将SpringWebFlow从1.x升级到2.4 在SWF1.x中,我们在整个会话中只使用了一个flowExecutionKey,还禁用了反向导航 在SWF2.4中,我们希望有相同的行为 我们可以通过将最大执行快照配置为0来禁用快照,如下所示 <webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry"> <webflow:flow-execution-repository ma
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-repository max-executions="1" max-execution-snapshots="0"/>
</webflow:flow-executor>
但在导航上,快照id不断增加(如e1s1、e1s2…
,这是导致问题的原因,当我们对此进行调查时
AbstractFlowExecutionRepository
具有以下字段,该字段有助于在为true时为每个导航生成新的快照ID
AlwaysGenerateNewExtKey
默认为true
在SWF 1.x中,有一个选项可以通过RepositoryType.SINGLEKEY
设置为false,但在SWF 2.4中,我们找不到任何等效的方法将该值设置为false
如果有办法覆盖“AlwaysGenerateNewExtKey”请提供帮助现在我知道这很难看,但以下是您可以做的: 制作自己的
FlowExecutoryBean
和FlowElementAttribute
副本,将它们命名为MyFlowExecutoryBean
和MyFlowElementAttribute
(将MyFlowExecutoryBean
中的引用从FlowElementAttribute
更改为MyFlowElementAttribute
)
在myflowExecutoryFactoryBean
中,在executionRepository
上设置所需的属性,如setAlwaysGenerateNewExtKey(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;
}
}
}
公共类MyFlowExecutorFactoryBean实现FactoryBean、ApplicationContextAware、BeanClassLoaderware、InitializingBean{
私有静态最终字符串始终\u重定向\u ON\u PAUSE=“AlwaysDirectionPause”;
私有静态最终字符串REDIRECT_IN_SAME_STATE=“redirectInSameState”;
专用FlowDefinitionLocator FlowDefinitionLocator;
私有整数maxFlowExecutions;
私有整数maxFlowExecutionSnapshots;
私有设置FlowExecutionAttribute;
私有FlowExecutionListenerLoader FlowExecutionListenerLoader;
私人会话管理器会话管理器;
私有转换服务转换服务;
私有流执行器流执行器;
私人环境;
私有类加载器;
/**
*设置流定义定位器,该定位器将定位执行所需的流定义。通常也是
*{@link FlowDefinitionRegistry}。必需。
*@param flowDefinitionLocator流定义定位器(注册表)
*/
public void setFlowDefinitionLocator(FlowDefinitionLocator FlowDefinitionLocator){
this.flowDefinitionLocator=flowDefinitionLocator;
}
/**
*设置每个用户允许的最大流执行数。
*/
公共void setMaxFlowExecutions(int maxFlowExecutions){
this.maxFlowExecutions=maxFlowExecutions;
}
/**
*设置每次流执行允许的最大历史快照数。
*/
public void setMaxFlowExecutionSnapshots(int-maxFlowExecutionSnapshots){
this.maxFlowExecutionSnapshots=maxFlowExecutionSnapshots;
}
/**
*设置应用于此工厂创建的执行器启动的流执行的系统属性。
*执行属性可能会影响流执行行为。
*@param flowExecutionAttributes流执行系统属性
*/
public void setFlowExecutionAttributes(设置flowExecutionAttributes){
this.flowExecutionAttributes=flowExecutionAttributes;
}
/**
*设置加载将观察流定义执行情况的侦听器的策略。允许完全
*控制哪些侦听器应应用于由创建的执行器启动的流定义的执行
*这家工厂。
*/
public void setFlowExecutionListenerLoader(FlowExecutionListenerLoader FlowExecutionListenerLoader){
this.flowExecutionListenerLoader=flowExecutionListenerLoader;
}
/**
*设置服务类型,该服务类型用于管理会话,并有效地控制状态在会话发生时的物理存储方式
*流执行已暂停。
*/
公共无效设置ConversationManager(ConversationManager ConversationManager){
this.conversationManager=conversationManager;
}
//实现ApplicationContextAware
@凌驾
public void setApplicationContext(ApplicationContext ApplicationContext)抛出BeansException{
environment=MVCEnEnvironment.environmentFor(applicationContext);
}
//实现BeanClassLoaderware
@凌驾
公共类加载器(类加载器类加载器){
this.classLoader=classLoader;
}
//实现初始化bean
@凌驾
public void afterPropertieSet()引发异常{
notNull(flowDefinitionLocator,“需要流定义定位器属性”);
if(conversionService==null){
conversionService=新的DefaultConversionService();
}
MutableAttributeMap executionAttributes=createFlowExecutionAttributes();
FlowExecutionImpactFactory executionFactory=createFlowExecutionFactory(executionAttributes);
DefaultFlowExecutionRepository executionRepository=createFlowExecutionRepository(executionFactory);
executionRepository.setMaxSnapshots(0);
executionRepository.setAlwaysGenerateNewExtKey(false);
setExecutionKeyFactory(executionRepository);
flowExecutor=新的FlowExecuteImpl(flowDefinitionLocator、executionFactory、executionRepository);
}
//实现FactoryBean
@凌驾
公共类getObjectType(){
返回FlowExecutor.class;
}
@凌驾
公共布尔isSingleton(){
返回true;
}
@凌驾
公共FlowExecut
<bean id="flowExecutor" class="com.example.MyFlowExecutorFactoryBean ">
<property name="flowDefinitionLocator" ref="flowRegistry" />
<property name="flowExecutionListenerLoader">
<bean class="org.springframework.webflow.config.FlowExecutionListenerLoaderFactoryBean">
<property name="listeners">
<map>
<entry key-ref="yourFirstListener" value="*" />
<entry key-ref="yourSecondListener" value="*" />
</map>
</property>
</bean>
</property>
</bean>
<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>
public SingleFlowExecutionRepository(ConversationManager conversationManager,
FlowExecutionSnapshotFactory snapshotFactory) {
super(conversationManager, snapshotFactory);
/**
* Set to maintain single snapshot for a session.
*/
super.setAlwaysGenerateNewNextKey(false);
}