8. [TD]:[业务]层
关键词:多层架构、Spring、依赖注入。
![]() |
8.1. 支持
![]() | ![]() |
[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 Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- plugins -->
<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>
<!-- to install the project artifact in the local Maven repository -->
<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 行:我们启用缓存。我们没有重新定义 [CacheManager] Bean,因为它已经在 [DAO] 层中定义了;
- 第 9 行:[business] 层在 [elections.business.service] 包中定义了新的 Bean;
8.4. [ IElectionsMetier] 接口
![]() |
[业务]层将采用第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 {
// the access point to the [dao] layer instantiated by [Spring]
@SuppressWarnings("unused")
@Autowired
private IElectionsDao electionsDao;
// calculation of seats obtained
@Override
public ListeElectorale[] calculerSieges(ListeElectorale[] listesElectorales) {
throw new RuntimeException("[calculerSieges] not yet implemented");
}
// competing lists
@Override
public ListeElectorale[] getListesElectorales() {
throw new RuntimeException("[getListesElectorales] not yet implemented");
}
// saving results
@Override
public void recordResultats(ListeElectorale[] listesElectorales) {
throw new RuntimeException("[recordResultats] not yet implemented");
}
// number of seats to be filled
@Override
public int getNbSiegesAPourvoir() {
throw new RuntimeException("[getNbSiegesAPourvoir] not yet implemented");
}
// electoral threshold
@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 {
// business] layer
@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() {
// create a table of the 7 candidate lists (name, vote)
// the seats for each list are calculated
// we check the results (seats, votes, eliminates)
// temporary
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() {
// the table of 7 candidate lists is retrieved as a base
// the voices are hard-fixed
// the seats obtained by each list are calculated
// we check the results (seats, votes, eliminates)
// temporary
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)
// write a test that causes an exception of type [ElectionsException]
public void calculSieges3() {
// create a table of 25 candidate lists, each with 1 vote
// all 25 lists will have the same number of votes (4%)
// calculation of seats - normally there should be a ElectionsException
// with an electoral threshold of 5%
// temporary
throw new RuntimeException("[calculSieges3] not yet implemented");
}
/**
* enregistrement des résultats de l'élection
*/
@Test
public void ecritureResultatsElections() {
// create the table of 7 candidate lists
// the voices are hard-fixed
// the seats obtained by each list are calculated
// results are entered into the database
// reread lists in base
// we check the results (seats, votes, eliminates)
// temporary
throw new RuntimeException("[ecritureResultatsElections] not yet implemented");
}
}
任务:请参照注释编写四个测试方法。请注意,当这些方法运行时,第 19 行中的 [electionsMetier] 字段已经初始化。请验证 JUnit 测试是否通过。
8.7. 创建 [business] 层归档
与处理 [DAO] 层时一样,我们将 [elections-metier-dao-jdbc] 项目归档文件放入本地 Maven 仓库:
![]() |
注意:如果您的 Eclipse 项目关联的是 JRE(Java 运行时环境)而非 JDK(Java 开发工具包),此操作可能会失败。要检查这一点,请按照第 3.1 节中的说明进行操作。如果您发现项目关联的是 JRE 而不是 JDK,请按照该节中的说明将项目关联到 JDK。
8.8. 结论
让我们回顾一下正在构建的 [Elections] 应用程序的总体架构:
![]() |
我们已经构建了[业务]层和[DAO]层。接下来我们将构建[UI]层。针对这一层,我们将提出两种实现方案:
- 一种“控制台”实现方案
- 图形用户界面方案











