Skip to content

8. [TD]:[metier] 层

关键词:多层架构、Spring、依赖注入。

8.1. Support

 

文件 [support / chap-08] 包含本章的 Eclipse 项目。

8.2. Maven 配置

  

项目 [elections-metier-dao-jdbc] 将基于项目 [elections-dao-jdbc-01]。我们将把后者的 JAR 文件安装到本地 Maven 仓库中:

 

完成上述操作后,Eclipse 项目 [elections-metier-dao-jdbc] 的 Maven 配置如下:

  

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-metier-dao-jdbc</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.7.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- DAO -->
        <dependency>
            <groupId>istia.st.elections</groupId>
            <artifactId>elections-dao-jdbc-01</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- 插件 -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>config.AppConfig</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
            <!-- 用于将项目工件安装到本地Maven仓库 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </build>
</project>
  • 第 21-25 行:对项目 [elections-jdbc-01] 的 JAR 文件的依赖。我们将这些行直接从要导入的项目的 [pom.xml] 文件中提取:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-dao-jdbc-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
...
  • 第26-37行:测试所需的依赖项。 尽管它们存在于项目 [elections-jdbc-01] 的 [pom.xml] 文件中,但我们必须重新添加,因为其 [<scope>test</scope>] 属性导致它们未被包含在本地 Maven 仓库中的 JAR 包中;

8.3. Spring 配置

  

package elections.metier.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@Import({ elections.dao.config.AppConfig.class })
@EnableCaching
@ComponentScan(basePackages = { "elections.metier.service" })
public class MetierConfig {
}
  • 第 7 行:导入 [DAO] 层中定义的所有 Bean;
  • 第 8 行:启用缓存。不重新定义 Bean [CacheManager],因为它已在 [DAO] 层中定义;
  • 第 9 行:[métier] 层在 [elections.metier.service] 包中定义了新的 Bean;

8.4. 接口 [IElectionsMetier]

  

[metier] 层将采用第 4.2 节中介绍的接口。现将其重述如下:


public interface IElectionsMetier {

    public ListeElectorale[] getListesElectorales();

    public int getNbSiegesAPourvoir();

    public double getSeuilElectoral();

    public void recordResultats(ListeElectorale[] listesElectorales);

    public ListeElectorale[] calculerSieges(ListeElectorale[] listesElectorales);

}

8.5. 实现类 [ElectionsMetier]

  

该类实现了接口 [IElectionsMetier]。建议的实现将具有以下结构:


package elections.metier.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import elections.dao.entities.ListeElectorale;
import elections.dao.service.IElectionsDao;

@Component
public class ElectionsMetier implements IElectionsMetier {

    // 由 [Spring] 实例化的 [dao] 层的访问点
    @SuppressWarnings("unused")
    @Autowired
    private IElectionsDao electionsDao;

    // 计算获得的席位
  @Override
    public ListeElectorale[] calculerSieges(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[calculerSieges] not yet implemented");
    }

    // 竞争名单
  @Override
    public ListeElectorale[] getListesElectorales() {
        throw new RuntimeException("[getListesElectorales] not yet implemented");
    }

    // 保存结果
  @Override
    public void recordResultats(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[recordResultats] not yet implemented");
    }

    // 待分配席位数
    @Override
    public int getNbSiegesAPourvoir() {
        throw new RuntimeException("[getNbSiegesAPourvoir] not yet implemented");
    }

    // 选举门槛
    @Override
    public double getSeuilElectoral() {
        throw new RuntimeException("[getSeuilElectoral] not yet implemented");
    }

}
  • 第 10 行:类 [ElectionsMetier] 是一个 Spring 组件;
  • 第 11 行:类 [ElectionsMetier] 实现了接口 [IElectionsMetier];
  • 第 15-16 行:通过 Spring 注入 [DAO] 层的引用;
  • 第37行和第44行:待填补的席位数和选举门槛已被缓存;

待完成任务:编写类 [ElectionsMetier]。如有注释,请参考注释。不要尝试捕获(try / catch)从 [DAO] 层抛出的 [ElectionsException] 类型的异常。 允许其向上传播至 [UI] 层。由于 [ElectionsException] 类属于不受控异常类型,因此无需通过 try/catch 进行处理。


8.6. 测试类

  

测试类 JUnit 将采用以下形式:


package tests;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import dao.config.AppConfig;
import dao.entities.ElectionsException;
import metier.service.IElectionsMetier;

@SpringApplicationConfiguration(classes = MetierConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {

    // 图层 [métier]
    @Autowired
    static private IElectionsMetier electionsMetier;

    /**
     * vérification 1 : méthode de calcul des sièges on fixe en dur les listes
     */
    @Test
    public void calculSieges1() {
        // 创建包含7个候选名单的表格(名称、得票数)

        // 计算各候选名单的席位

        // 验证结果(席位、得票数、淘汰名单)

        // 临时
        throw new RuntimeException("[calculSieges1] not yet implemented");
    }

    /**
     * vérification 2 : méthode de calcul des sièges on demande les listes à la
     * couche [metier] puis on fixe en dur les voix
     */
    @Test
    public void calculSieges2() {
        // 将7个候选名单的数组导入数据库

        // 将得票数固定

        // 计算各候选名单获得的席位

        // 验证结果(席位、得票数、淘汰名单)

        // 临时
        throw new RuntimeException("[calculSieges2] not yet implemented");
    }

    /**
     * vérification 3 méthode de calcul des sièges on provoque une exception
     */
    @Test(expected = ElectionsException.class)
    // 编写一个会引发类型为 [ElectionsException] 的异常的测试
    public void calculSieges3() {
        // 创建包含25个候选名单的数组,每个名单各得1票
        // 这25个候选名单将获得相同数量的选票(4%)

        // 计算席位——通常应得到 ElectionsException
        // 选举门槛为5%

        // 临时
        throw new RuntimeException("[calculSieges3] not yet implemented");
    }

    /**
     * enregistrement des résultats de l'élection
     */
    @Test
    public void ecritureResultatsElections() {
        // 创建包含7个候选名单的表格

        // 固定票数

        // 计算各候选名单获得的席位

        // 将结果保存至数据库

        // 重新读取数据库中的候选名单

        // 验证结果(席位、得票数、淘汰名单)

        // 临时
        throw new RuntimeException("[ecritureResultatsElections] not yet implemented");
    }
}

待完成任务:请参考注释编写四个测试方法。需注意,当这些方法执行时,第 19 行中的字段 [electionsMetier] 已初始化。请验证测试 JUnit 是否通过。


8.7. 创建 [metier] 层归档

与处理 [DAO] 层时相同,我们将项目 [elections-metier-dao-jdbc] 的归档文件放入本地 Maven 仓库:

 

注意:如果您的 Eclipse 项目关联的是 JRE(Java 运行时环境)而非 JDK(Java 开发工具包),此操作可能会失败。 要确认这一点,请按照第 3.1 节中的说明进行操作。如果您发现项目关联的是 JRE 而不是 JDK,请按照本节中的说明将项目关联到 JDK。

8.8. Conclusion

让我们回顾一下我们正在构建的 [Elections] 应用程序的总体架构:

我们已经构建了 [metier] 和 [dao] 层。接下来我们将构建 [ui] 层。针对该层,我们将提供两种实现方案:

  • 一种“控制台”实现
  • 带图形界面的实现