# 10. 系统设置

## 10.1.导言

本章将向你展示如何设置在任何 Web 环境中使用的 Web 流系统。

## 10.2.Java 配置和 XML 命名空间

Web 流为基于 Java 和 XML 的配置提供了专门的配置支持。

要开始使用基于 XML 的配置，请声明 WebFlow Config XML 名称空间：

```
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:webflow="http://www.springframework.org/schema/webflow-config"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config.xsd">

    <!-- Setup Web Flow here -->

</beans>
		
```

要在`@Configuration`类中开始使用 Java 配置扩展`AbstractFlowConfiguration`:

```
import org.springframework.context.annotation.Configuration;
import org.springframework.webflow.config.AbstractFlowConfiguration;

@Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {

}
		
```

## 10.3.基本系统配置

下一节将展示在应用程序中设置 Web 流系统所需的最小配置。

### 10.3.1.FlowRegistry

在`FlowRegistry`的 XML 中注册你的流：

```
<webflow:flow-registry id="flowRegistry">
    <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>
			
```

在 Java 中的`FlowRegistry`中注册你的流：

```
@Bean
public FlowDefinitionRegistry flowRegistry() {
    return getFlowDefinitionRegistryBuilder()
        .addFlowLocation("/WEB-INF/flows/booking/booking.xml")
        .build();
}
			
```

### 10.3.2.流动执行器

部署 FlowExecutor，这是用于执行 XML 流的中心服务：

```
<webflow:flow-executor id="flowExecutor" />
			
```

部署 FlowExecutor，这是用于在 Java 中执行流的中心服务：

```
@Bean
public FlowExecutor flowExecutor() {
    return getFlowExecutorBuilder(flowRegistry()).build();
}
			
```

请参阅本指南的 Spring MVC 和 Spring Faces 部分，分别介绍如何将 Web 流系统与 MVC 和 JSF 环境集成。

## 10.4.flow-registry 选项

本节将探讨流注册中心配置选项。

### 10.4.1.指定流位置

使用`location`元素指定要注册的流定义的路径。默认情况下，流被分配的注册表标识符等于其文件名减去文件扩展名，除非定义了注册表池路径。

在 XML 中：

```
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
			
```

在 Java 中：

```
return getFlowDefinitionRegistryBuilder()
        .addFlowLocation("/WEB-INF/flows/booking/booking.xml")
        .build();
			
```

### 10.4.2.分配自定义流标识符

指定一个 ID，以便将自定义注册表标识符分配给 XML 中的流：

```
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" id="bookHotel" />
			
```

指定一个 ID，以便将自定义注册表标识符分配给 Java 中的流：

```
return getFlowDefinitionRegistryBuilder()
        .addFlowLocation("/WEB-INF/flows/booking/booking.xml", "bookHotel")
        .build();
			
```

### 10.4.3.分配流元属性

使用`flow-definition-attributes`元素将自定义的元属性分配给已注册的流。

在 XML 中：

```
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml">
    <webflow:flow-definition-attributes>
        <webflow:attribute name="caption" value="Books a hotel" />
    </webflow:flow-definition-attributes>
</webflow:flow-location>
			
```

在 Java 中：

```
Map<String, Object> attrs = ... ;

return getFlowDefinitionRegistryBuilder()
        .addFlowLocation("/WEB-INF/flows/booking/booking.xml", null, attrs)
        .build();
			
```

### 10.4.4.使用位置模式注册流

使用`flow-location-patterns`元素来注册与特定资源位置模式匹配的流：

在 XML 中：

```
<webflow:flow-location-pattern value="/WEB-INF/flows/**/*-flow.xml" />
			
```

在 Java 中：

```
return getFlowDefinitionRegistryBuilder()
        .addFlowLocationPattern("/WEB-INF/flows/**/*-flow.xml")
        .build();
			
```

### 10.4.5.流量定位基准路径

使用`base-path`属性为应用程序中的所有流定义一个基本位置。然后，所有的流动位置都相对于基本路径。基本路径可以是资源路径，例如“/WEB-INF”，或者是 Classpath 上的位置，例如“ Classpath:org/springframework/webflow/samples”。

在 XML 中：

```
<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF">
    <webflow:flow-location path="/hotels/booking/booking.xml" />
</webflow:flow-registry>
			
```

在 Java 中：

```
return getFlowDefinitionRegistryBuilder()
        .setBasePath("/WEB-INF")
        .addFlowLocationPattern("/hotels/booking/booking.xml")
        .build();
			
```

定义了基本路径后，分配流标识符的算法会略有变化。流现在将被分配的注册表标识符等于它们的基本路径和文件名之间的路径段。例如，如果流定义位于“/WEB-INF/hotels/booking/booking-flow.xml”，而基本路径是“/WEB-INF”，则该流的剩余路径是“Hotels/booking”，它将成为流 ID。

|[[提示]（images/tip.png）|Directory per flow definition|
|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------|
|回想一下，将每个流定义打包在一个唯一的目录中是一种最佳实践。<br/>这提高了模块化，允许使用流定义打包依赖的资源。<br/>它还防止了两个流在使用约定时具有相同的标识符。|                             |

如果没有指定基路径，或者流定义直接位于基路径上，则使用文件名（减去扩展名）中的流 ID 分配。例如，如果流定义文件是’booking.xml’，那么流标识符只是’booking’。

与注册表基路径相结合时，位置模式的功能特别强大。而不是流标识符成为“\*-流”，它们将基于目录路径。例如，在 XML 中：

```
<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF">
    <webflow:flow-location-pattern value="/**/*-flow.xml" />
</webflow:flow-registry>
			
```

在 Java 中：

```
return getFlowDefinitionRegistryBuilder()
        .setBasePath("/WEB-INF")
        .addFlowLocationPattern("/**/*-flow.xml")
        .build();
			
```

在上面的示例中，假设在`/user/login`、`/user/registration`、`/hotels/booking`和`/flights/booking`目录中有位于`WEB-INF`中的流 ID，则最终得到的流 ID 分别为`user/login`、`hotels/booking`和`flights/booking`。

### 10.4.6.配置 FlowRegistry 层次结构

使用`parent`属性在层次结构中将两个流注册中心链接在一起。当查询子注册中心时，如果它找不到请求的流，它将委托给其父注册中心。

在 XML 中：

```
<!-- my-system-config.xml -->
<webflow:flow-registry id="flowRegistry" parent="sharedFlowRegistry">
    <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>

<!-- shared-config.xml -->
<webflow:flow-registry id="sharedFlowRegistry">
    <!-- Global flows shared by several applications -->
</webflow:flow-registry>
			
```

在 Java 中：

```
@Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {

    @Autowired
    private SharedConfig sharedConfig;

    @Bean
    public FlowDefinitionRegistry flowRegistry() {
        return getFlowDefinitionRegistryBuilder()
                .setParent(this.sharedConfig.sharedFlowRegistry())
                .addFlowLocation("/WEB-INF/flows/booking/booking.xml")
                .build();
    }
}

@Configuration
public class SharedConfig extends AbstractFlowConfiguration {

    @Bean
    public FlowDefinitionRegistry sharedFlowRegistry() {
        return getFlowDefinitionRegistryBuilder()
                .addFlowLocation("/WEB-INF/flows/shared.xml")
                .build();
    }
}
			
```

### 10.4.7.配置自定义 FlowBuilder 服务

使用`flow-builder-services`属性定制用于在流注册中心中构建流的服务和设置。如果没有指定 flow-builder-services 标记，则使用默认的服务实现。定义标记时，只需引用要自定义的服务。

在 XML 中：

```
<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
    <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>

<webflow:flow-builder-services id="flowBuilderServices" />
			
```

在 Java 中：

```
@Bean
public FlowDefinitionRegistry flowRegistry() {
	return getFlowDefinitionRegistryBuilder(flowBuilderServices())
            .addFlowLocation("/WEB-INF/flows/booking/booking.xml")
            .build();
}

@Bean
public FlowBuilderServices flowBuilderServices() {
    return getFlowBuilderServicesBuilder().build();
}
			
```

可配置的服务是`conversion-service`、`expression-parser`和`view-factory-creator`。这些服务是通过引用你定义的自定义 bean 来配置的。

例如，在 XML 中：

```
<webflow:flow-builder-services id="flowBuilderServices"
    conversion-service="conversionService"
    expression-parser="expressionParser"
    view-factory-creator="viewFactoryCreator" />

<bean id="conversionService" class="..." />
<bean id="expressionParser" class="..." />
<bean id="viewFactoryCreator" class="..." />
			
```

在 Java 中：

```
@Bean
public FlowBuilderServices flowBuilderServices() {
    return getFlowBuilderServicesBuilder()
            .setConversionService(conversionService())
            .setExpressionParser(expressionParser)
            .setViewFactoryCreator(mvcViewFactoryCreator())
            .build();
}

@Bean
public ConversionService conversionService() {
    // ...
}

@Bean
public ExpressionParser expressionParser() {
    // ...
}

@Bean
public ViewFactoryCreator viewFactoryCreator() {
    // ...
}
			
```

#### 转换-服务

使用`conversion-service`属性定制 Web 流系统使用的`ConversionService`。类型转换用于在流执行期间（例如在处理请求参数、调用操作等时）需要从一种类型转换为另一种类型。支持许多常见的对象类型，如数字、类和枚举。但是，你可能需要为自定义数据类型提供自己的类型转换和格式逻辑。有关如何提供自定义类型转换逻辑的重要信息，请阅读[第 5.7 节，“执行类型转换”](views.html#view-type-conversion)。

#### 表达式分析器

使用`expression-parser`属性定制 Web 流系统使用的`ExpressionParser`。默认表达式分析器使用统一的 EL（如果在 Classpath 上可用），否则使用 Spring EL。

#### 视图-工厂-创建者

使用`view-factory-creator`属性定制 Web 流系统使用的`ViewFactoryCreator`。默认的 ViewFactoryCreator 生成 Spring MVC 视图工厂，它能够呈现 JSP、Velocity 和 Freemarker 视图。

可配置设置为`development`。这些设置是可以在流构建过程中应用的全局配置属性。

#### 发展

将此设置为`true`以打开流*发展模式*。开发模式切换到热重载流定义的更改，包括对依赖流资源（如消息包）的更改。

## 10.5.flow-executor 选项

本节将探讨流执行器配置选项。

### 10.5.1.附加流执行侦听器

使用`flow-execution-listeners`元素来注册观察流执行生命周期的侦听器。例如，在 XML 中：

```
<webflow:flow-execution-listeners>
    <webflow:listener ref="securityListener"/>
    <webflow:listener ref="persistenceListener"/>
</webflow:flow-execution-listeners>
			
```

在 Java 中：

```
@Bean
public FlowExecutor flowExecutor() {
    return getFlowExecutorBuilder(flowRegistry())
            .addFlowExecutionListener(securityListener())
            .addFlowExecutionListener(persistenceListener())
            .build();
}
			
```

你也可以将监听器配置为只观察某些流。例如，在 XML 中：

```
<webflow:listener ref="securityListener" criteria="securedFlow1,securedFlow2"/>
			
```

在 Java 中：

```
@Bean
public FlowExecutor flowExecutor() {
    return getFlowExecutorBuilder(flowRegistry())
            .addFlowExecutionListener(securityListener(), "securedFlow1,securedFlow2")
            .build();
}
			
```

### 10.5.2.调优流执行持久性

使用`flow-execution-repository`元素调优流执行持久性设置。例如，在 XML 中：

```
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
    <webflow:flow-execution-repository max-executions="5" max-execution-snapshots="30" />
</webflow:flow-executor>
			
```

在 Java 中：

```
@Bean
public FlowExecutor flowExecutor() {
    return getFlowExecutorBuilder(flowRegistry())
            .setMaxFlowExecutions(5)
            .setMaxFlowExecutionSnapshots(30)
            .build();
}
			
```

#### 最大处决

调整`max-executions`属性，以限制每个用户可以创建的流执行数量会话。当超过最大执行次数时，删除最老的执行。

|[[note]（images/note.png）|Note|
|:--------------------------------------------------------------------------------------------------------:|:---|
|`max-executions`属性是每个用户会话，即它在任何流定义的实例之间工作。|    |

#### max-execution-snapshots

调整`max-execution-snapshots`属性，以限制每个流执行可以获取的历史快照的数量。要禁用快照，请将该值设置为 0.要启用无限数量的快照，请将该值设置为-1.

|[[note]（images/note.png）|Note|
|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---|
|历史快照启用浏览器后退按钮支持。<br/>当快照被禁用时，按下浏览器后退按钮将不起作用。<br/>它将导致使用指向尚未记录的快照的执行键。|    |