Spring Boot实践

目录

  • 建立项目结构
  • application.properties
  • logback配置
  • ehcache配置
  • Swagger2配置
  • MessageResources配置
  • 统一异常处理
  • liquibase
  • JPA data access
  • FeignClient
  • actuator
  • jacoco
  • javadoc
  • plantuml
  • 发布到私有maven仓库
  • spring test & Mockito

建立项目结构

一切从spring-boot-starter-parent开始,建立一个maven的pom项目,项目名称:parent ,这个项目只包括一个pom.xml文件,用于管理整个项目的maven dependency。

parent project

Parent项目

关于pom.xml的内容

  • pom文件的packaging类型必须是pom
...
<packaging>pom</packaging>
...
  • spring-boot-starter-parent,因为这是一个SpringBoot项目,所以项目必须继承spring-boot-starter-parent
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.0.1.RELEASE</version>
</parent>
  • dependencyManagement里加入spring-cloud-dependencies,在这个项目中,我们使用的FeignClient是依赖 spring-clound-dependencies
<properties>
...
<spring.cloud.version>Finchley.RELEASE</spring.cloud.version>
</properties>
...
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-dependencies</artifactId>
	<version>${spring.cloud.version}</version>
    <type>pom</type>
	<scope>import</scope>
</dependency>
...
  • 我们有使用流的方式读取excel,在dependencyManagement中加入关于excel的dependency
...
<org.apache.poi.version>3.16</org.apache.poi.version>
<poi.streaming.version>1.2.0</poi.streaming.version>
...
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>${org.apache.poi.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>${org.apache.poi.version}</version>
</dependency>

<dependency>
	<groupId>com.monitorjbl</groupId>
	<artifactId>xlsx-streamer</artifactId>
	<version>${poi.streaming.version}</version>
</dependency>
...
  • 关于JPA使用的读取database的依赖,apache commons依赖,web appliation依赖加入到dependencyManager
...
<jdbc.sqlserver.version>5.0</jdbc.sqlserver.version>
<jdbc.mysql.version>6.0.6</jdbc.mysql.version>
<commons.lang3.version>3.2.1</commons.lang3.version>
<commons.collections.version>3.2.1</commons.collections.version>
<servlet.api.version>2.5</servlet.api.version>
...
<dependency>
	<groupId>com.microsoft.sqlserver</groupId>
	<artifactId>sqljdbc4</artifactId>
	<version>${jdbc.sqlserver.version}</version>
</dependency>

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>${jdbc.mysql.version}</version>
</dependency>

<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-lang3</artifactId>
	<version>${commons.lang3.version}</version>
</dependency>
<dependency>
	<groupId>commons-collections</groupId>
	<artifactId>commons-collections</artifactId>
	<version>${commons.collections.version}</version>
</dependency>
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>servlet-api</artifactId>
	<version>${servlet.api.version}</version>
</dependency>

<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>javax.servlet-api</artifactId>
	<version>${servlet.api.version}</version>
</dependency>
  • dependencies里加入所有子项目都需要用到的依赖

项目用到spring-test , mockito , junit 来构建测试框架,加入以下相关依赖包

...
<mockito.version>1.10.19</mockito.version>
...
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>${mockito.version}</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<scope>test</scope>
</dependency>
  • 使用sonarQue扫描代码缺陷,作为质量内建的工具之一,在dependenies里加入sonarQue的依赖
...
<sonar.maven.plugin.version>3.4.0.905</sonar.maven.plugin.version>
...
<dependency>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>${sonar.maven.plugin.version}</version>
</dependency>
  • 子项目作为jar包的方式提供组件服务,在build plugins加入 maven-jar-plugin,同时在jar包打包时包括源代码,加入maven-source-plugin
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-source-plugin</artifactId>
	<executions>
     	<execution>
				<id>attach-sources</id>
				<goals>
					<goal>jar</goal>
				</goals>
		</execution>
	</executions>
</plugin>
  • build plugins加入maven-surefire-plugin,用来运行系统的test case,时同加入jacoco-maven-plugin,用来生成代码覆盖率统计数据
...
<surefireArgLine></surefireArgLine>
<maven.test.skip>false</maven.test.skip>
<skipTests>false</skipTests>
<skipITs>false</skipITs>
...
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-surefire-plugin</artifactId>
	<version>2.18.1</version>
	<configuration>
			<failIfNoTests>false</failIfNoTests>
			<argLine>${surefireArgLine}</argLine>
			<includes>
				<include>**/*Test.java</include>
				<include>**/Test*.java</include>
			</includes>
			<excludes>
				<exclude>**/*IT.java</exclude>
				<exclude>**/IT*.java</exclude>
				<exclude>**/*ITCase.java</exclude>
			</excludes>
			<skip>${maven.test.skip}</skip>
			<skipTests>${skipTests}</skipTests>
	</configuration>
	<executions>
			<execution>
				<id>integration-tests</id>
				<goals>
					<goal>test</goal>
				</goals>
            	<configuration>
					<skip>${skipITs}</skip>
					<includes>
						<include>**/*IT.java</include>
						<include>**/IT*.java</include>
						<include>**/*ITCase.java</include>
					</includes>
					<excludes>
						<exclude>**/*Test.java</exclude>
						<exclude>**/Test*.java</exclude>
					</excludes>
				</configuration>
			</execution>
	</executions>
</plugin>
<plugin>
	<groupId>org.jacoco</groupId>
	<artifactId>jacoco-maven-plugin</artifactId>
	<version>0.8.1</version>
	<configuration>
		<destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile>
		<dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
		<skip>${skipTests}</skip>
		<output>file</output>
		<append>true</append>
	</configuration>
	<executions>
		<execution>
			<id>pre-unit-test</id>
			<goals>
				<goal>prepare-agent</goal>
			</goals>
            <phase>compile</phase>
			<configuration>
				<propertyName>surefireArgLine</propertyName>
		    </configuration>
		</execution>
			<execution>
				<id>post-unit-test</id>
				<phase>test</phase>
				<goals>
		        	<goal>report</goal>
				</goals>
				<configuration>
					<outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
				</configuration>
			</execution>
		</executions>
</plugin>

surefireArgLinejacoco-maven-plugin设置值,提供maven-surefire-plugin使用,使jacoco可以抓取test case执行时的统计数据

pluginManagement plugins加入liquibase-maven-plugin用来管理database的持续开发与集成

...
<pluginManagement>
     <plugins>
          <plugin>
              <groupId>org.liquibase</groupId>
              <artifactId>liquibase-maven-plugin</artifactId>
          </plugin>
     </plugins>
....
</pluginManagement>
...

以上为parent项目的基本结构完成,但是一些其他应用的plugin还没有加入,比如maven-javadoc-plugin 等,我在用到这些插件再添加。 开始下一步之前,首先在parent项目里运行mvn install命令,下载相关依赖和插件。

ERP项目

新建 ERP项目 ,这是一个聚合项目ERP包括CRM子项目,项目结构如下:

erp

pom.xml文件内容

maven项目类型

<packaging>pom</packaging>

ERP继承Parent项目

<parent>
      <groupId>com.ay</groupId>
      <artifactId>Ay_Parent</artifactId>
      <version>1.0-SNAPSHOT</version>
</parent>

erp作为一个web application,为每个子项目引入spring-boot-starter-web,同时使用logback日志,引用logback-classic。 我们使用前后面后离的开发模式,后端应用引用swagger UI管理rest-API,引入swagger-annotations。 在dependencies加入以下依赖。

<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>io.swagger</groupId>
	<artifactId>swagger-annotations</artifactId>
	<version>1.5.3</version>
</dependency>
  • 默认情况下,引入spring-boot-starter-web后,Spring Boot还会为我们引入以下几个包

    org.springframework.boot:spring-boot-starter

    • –org.springframework.boot:spring-boot
    • –org.springframework.boot:spring-boot-autoconfigure
    • –org.springframework.boot:spring-boot-starter-logging
    • –org.springframework.boot:spring-core
    • –org.yaml:snakeyaml

新建ERP子模块

erp-module

按功能职责分成以下几个模块

  • Ay_CRM核心CRM业务逻辑,通过restfull-API对外提供服务
  • ERP_Client 提供FeignClient接口
  • ERP_DataAccess 提供持久层服务
  • ERP_Web负责打包运行web application服务

ERP_DataAccess模块

负责ERP项目的持久层服务,DataAccess使用 Spring JAP抽象关系统数据库的访问。首先作为Spring Boot项目,先引入依赖:spring-boot-starter,同时因为maven的传递依赖,同时也会引入Spring Boot的几个核心包:spring-boot,spring-boot-autoconfugure,spring-boot-starter-loggin,spring-core,snakeyaml

...
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter</artifactId>
 </dependency>
...

引入spring-boot-starter-data-jpa包与sqljdbc4包,基于sql server关系数据库开发

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.microsoft.sqlserver</groupId>
    <artifactId>sqljdbc4</artifactId>
</dependency>

database unit test使用liquibase与h2内存数据库,引入相关的包

 <dependency>
      <groupId>org.liquibase</groupId>
      <artifactId>liquibase-core</artifactId>
      <scope>test</scope>
 </dependency>
 <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>test</scope>
 </dependency>

因为只在test阶段使用,所以liuqibase-core, h2scope是test

引入liquibase-maven-plugin插件,管理database的持续集成与开发

 <properties>
        <liquibase.action>updateSQL</liquibase.action>
    </properties>
    <profiles>
        <profile>
            <id>updateSQL</id>
            <properties>
                <liquibase.action>updateSQL</liquibase.action>
            </properties>
        </profile>
        <profile>
            <id>update</id>
            <properties>
                <liquibase.action>update</liquibase.action>
            </properties>
        </profile>
    </profiles>
    <build>
        <plugins>
            <plugin>
                <groupId>org.liquibase</groupId>
                <artifactId>liquibase-maven-plugin</artifactId>
                <configuration>
                    <propertyFileWillOverride>true</propertyFileWillOverride>
                    <propertyFile>src/main/resources/liquibase.properties</propertyFile>
                    <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
                </configuration>
                <executions>
                    <execution>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>${liquibase.action}</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

运行mvn package -P update就可以将liuqibase的change set执行并应用到db;mvn package -P updateSQL就可以对比change set与db之间的差异并生成sql 脚本,以便检查并手动执行chnage set。

通过以上的配置,我们就可以使用liquibase管理和续持集成开发database。但在实际开过程中,我们会发现:当我们在java persistence object里定义了db的结构,再把这种定义用xml的形式翻译成liquibase的changelog,liquibase再把changelog应用到db。在java persistence object,xml,db之间,用三种不同的形式描述相同的db结构。我们期望只维护一种表达形式(java),其他两种表达形式可以由工具帮我们自动维护,xml与db之间由liquibase自动帮我们管理,但是java与xml之间到目前为止,还没有自动管理手段。liquibase提供了一个插件帮我们从java persistence object生成changelog:liquibase-hibernate5,我们修改liquibase-maven-plugin配置加入相关依赖,如下:

....
			<liquibase-hibernate5.version>3.6</liquibase-hibernate5.version>
        	<spring.jpa.version>2.0.6.RELEASE</spring.jpa.version>	
			<plugin>
                <groupId>org.liquibase</groupId>
                <artifactId>liquibase-maven-plugin</artifactId>
                <configuration>
                    <propertyFileWillOverride>true</propertyFileWillOverride>
                    <propertyFile>src/main/resources/liquibase.properties</propertyFile>
                    <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
                </configuration>
                <executions>
                    <execution>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>${liquibase.action}</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.liquibase.ext</groupId>
                        <artifactId>liquibase-hibernate5</artifactId>
                        <version>${liquibase-hibernate5.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-beans</artifactId>
                        <version>${spring.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.springframework.data</groupId>
                        <artifactId>spring-data-jpa</artifactId>
                        <version>${spring.jpa.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-core</artifactId>
                        <version>${spring.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-context</artifactId>
                        <version>${spring.version}</version>
                    </dependency>
                </dependencies>
            </plugin>

liquibase.properties配置文件加入配置:

referenceUrl: hibernate:spring:com.ay.erp.dao.persistence?dialect=org.hibernate.dialect.SQLServerDialect
diffChangeLogFile: src/main/resources/liquibase-diff-changeLog.xml

运行maven命令mvn liquibase:diff,即可以liquibase-diff-changeLog.xml文件中生成关于spring:com.ay.erp.dao.persistence包下的java persistence object与db之间的差异了,最后打开文件liquibase-diff-changeLog.xml作出必要的修改,整合成我们需要的changelog格式。

java persistence object的格式大概如下:

@Entity()
@Table(name = "ayErpCustomer"
,indexes = {
        @Index(name = "pk_ayErpCustomer_id",columnList = "id asc",unique = true),
        @Index(name = "index_ayErpCustomer_id_name_birthday",columnList = "id desc,name desc,birthday desc",unique = false)
}
,uniqueConstraints = {
        @UniqueConstraint(columnNames = {"id"},name ="constraint_ayErpCustomer_id")
}
)
@Setter
@Getter
public class ErpCustomerPo extends BasePo {

    @TableGenerator(name = "ayErpCustomerGenerator", table = TABLE_NAME, pkColumnName = PK_COLUMN_NAME, valueColumnName = VALUE_COLUMN_NAME, initialValue = 0, allocationSize = 100)
    @GeneratedValue(generator = "ayErpCustomerGenerator",strategy = GenerationType.TABLE)
    @Id
    @Column(name = "id",columnDefinition = DaoConstant.BIGINT,nullable = false)
    private Long id;

    @Column(name = "name", columnDefinition = DaoConstant.VARCHAR, length = 100, nullable = false)
    private String name;

    @Column(name = "sex",columnDefinition = DaoConstant.CHAR,length = 1,nullable = false)
    private String sex;

    @Column(name = "birthday",columnDefinition = TIMESTAMP,nullable = true)
    private Date birthday;

    @Column(name = "personalPhone",columnDefinition = VARCHAR,nullable = true,length = 20)
    private String personalPhone;

    @Column(name = "busniessPhone",columnDefinition = VARCHAR,nullable = true,length = 20)
    private String busniessPhone;

    @Column(name = "officeAddress",columnDefinition = VARCHAR,nullable = true,length = 150)
    private String officeAddress;

    @Column(name = "homeAddress",columnDefinition = VARCHAR,nullable = true,length = 150)
    private String homeAddress;
}
  • DB unit test

DB unit test是基于Spring Boot使用liquibase 和h2内存数据库构建不依赖外部的测试环境,首先在test->java包下建Spring Boot的启动类仅用于DB unit test

@SpringBootApplication
@ComponentScan(basePackages = "com.ay.erp.dao",useDefaultFilters = false,
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class)
        })
@EntityScan(basePackages = "com.ay.erp.dao")
public class DbUnitConfig {
}

DbUnitConfig

DB unit test需要依赖DbUnitConfig的配置启动

添加Spring Boot和liquibase的启动配置文件test/java/resources/application.yml,如上图所示,内容如下,

logging:
  level:
    org.springframework: DEBUG
    com.ay: DEBUG
    liquibase: DEBUG
    org.h2: DEBUG

spring:
  liquibase:
    change-log: classpath:db.changeog.erp.test.master.xml
    contexts: UT
    enabled: true
    drop-first: false
  jpa:
    generate-ddl: false
    show-sql: true
    hibernate:
      naming:
#      This is needed, otherwise hibernate will change the table name in SQL. Such as: change TblAddress to tbl_address
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

建立liquibase changelog配置文件db.changeog.erp.test.master.xml,这个文件直接引用main/resources/db.changelog.erp.master.xml文件,使已经配置好的changelog文件集合

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
    <include file="db.changelog.erp.master.xml"></include>
</databaseChangeLog>

配置完成,开始第一个test case

@RunWith(SpringRunner.class)
@DataJpaTest
@Transactional
public class CustomerPoRepositoryTest {

    @Autowired
    CustomerPoRepository repository;

    @Test
    public void should_save_and_find_success() {
        //given
        ErpCustomerPo po = createPersistenceObject();

        //when
        repository.save(po);
        Optional<ErpCustomerPo> result = repository.findById(po.getId());
        //then
        Assertions.assertThat(repository.count()).isEqualTo(1);
        Assertions.assertThat(result.get().getId()).isEqualTo(po.getId());
    }

    @Test
    public void should_delete_success() {
        //given
        ErpCustomerPo po = createPersistenceObject();
        repository.save(po);
        //when
        repository.delete(po);
        //then
        Assertions.assertThat(repository.count()).isEqualTo(0);
    }

    private ErpCustomerPo createPersistenceObject() {
        ErpCustomerPo po = new ErpCustomerPo();
        po.setBirthday(new Date());
        po.setBusniessPhone("123");
        po.setHomeAddress("11");
        po.setName("AndyLi");
        po.setOfficeAddress("chinase");
        po.setSex("M");
        po.setRecordStatus("A");
        return po;
    }
}

ERP_Web模块

Web模块负责配置打包运行ERP应用

  • application.propertes配置文件

在maven的resources目录下新建application.properties配置文件。

关于application.properties的详细配置项信息,请点这里查看

properties

完成Spring Boot配置文件后,新建Spring Boot启动类@SpringBootApplication

@SpringBootApplication(scanBasePackages = "com.ay")
@EnableCaching
@EnableTransactionManagement
@Slf4j
public class ErpApp {
    public static void main( String[] args ){
        SpringApplication.run(ErpApp.class, args);
    }
    /**
     *  create a CommandLineRunner to initiate application during application context starting
     * @param context spring application context
     * @return CommandLineRunner using to take action of initiation
     */
    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext context) {
        return args -> {
            log.info("inspect the beans provided by Spring Boot:");

            String[] beanNames = context.getBeanDefinitionNames();
            Arrays.sort(beanNames);
            for (String beanName : beanNames) {
                log.info(beanName);
            }

        };
    }
}

使用SwaggerUI管理restful API

Swagger相关依赖包

...
		<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.6.1</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.6.1</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.6-jre</version>
        </dependency>
...

新增Swagger关于Spring Boot的配置

@Configuration
@EnableSwagger2
public class SwaggerConfig {
	
	@Bean
	public Docket createDocket() {
		return new Docket(DocumentationType.SWAGGER_2)
				.apiInfo(createApiInfo())
				.select()
				//select()method return back  ApiSelectorBuilder instance to control which API should be shown using Swagger , in this configuration we will scan the the class underlyying specifical package
				//,Swagger will scan all the controller under package:com.aiatss.coast to create document except for method annotated @ApiIgnore
				.apis(RequestHandlerSelectors.basePackage("com.ay"))
				.paths(PathSelectors.any())
				.build();
	}

	private ApiInfo createApiInfo() {
		return  new ApiInfoBuilder()
				.title("restful API")
				//.description("")
				.termsOfServiceUrl("http://google.com")
				//.contact("COAST NFR")
				.version("0.0.1-SNAPSHOT")
				.build();
	}
}

通过Spring Boot的启动类启动ErpApp,并在浏览器输入地址:http://localhost:8080/swagger-ui.html 即可访问swagger ui主界面

Ay_Aggregate 项目

Ay_Aggreate项目的作用与Ay_Parent类似,是<packaging>pom</packaging>类型项目,不负责具体的功能代码。主要作为项目的聚合根,聚合所有的项目模块,负责一些跟聚合相关的操作和配置。

例如下面要介绍的关于生成所有项目的测试覆盖率报告。我们通过jacoco-maven-plugin生成所有项目的测试报告,但是最早期时jacoco-maven-plugin只能在单个项目的target目录下生成单个项目的测试报告,但是随着复杂的多模块项目的出现,为了方面管理,我们需要把所有模块或者子项目的测试覆盖数据都聚合在同一份报告里生成,所以jacoc-maven-plugin插件又新增了一个golareport-aggregate 用来把所有子项目(子模块)测试数据聚合在同一份报告里。jacoco-maven-plugin插件使用的测试数据是依赖插件:maven-surefire-plugin生成的,所以report-aggregate需要依赖每个单独项目下,由jacoco-maven-plugin集成maven-surefire-plugin生成的测试数据文件:jacoco.exec。所以关于这个数据文件的路径和名称的配置,如果不太清楚的话,就使用默认配置,如果report-aggregate没有找到对应的exec文件,生成的测试数据都错误的。

关于jacoco-maven-pluginreport-aggregate的官方文档,请查看这里 «««< HEAD

=======

2e99269f3aae6146008d0ee647086a49918676a9

关于report-aggregate在多模块项目中的演化过程,请看这里

Ay_Aggregate项目结构如下:

aggregate

Ay_Aggreate通过继承Ay_Parent获得Ay_Parent的所有特性,同时把所有项目配置为Ay_Aggregate的子模块

<?xml version="1.0" encoding="UTF-8"?>
<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>com.ay</groupId>
    <artifactId>Ay_Aggregate</artifactId>
    <packaging>pom</packaging>

    <parent>
        <groupId>com.ay</groupId>
        <artifactId>Ay_Parent</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>com.ay</groupId>
            <artifactId>Ay_CRM</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.ay</groupId>
            <artifactId>ERP_DataAccess</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.ay</groupId>
            <artifactId>ERP_Client</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <modules>
        <module>../Ay_ERP</module>
        <module>../Ay_Security</module>
    </modules>

    <build>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>report-aggregate</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>report-aggregate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

在上面的代码中,需要注意的是:

  1. packaging类型为pom
  2. 通过以下配置继承Ay_Parent项目
<parent>
    <groupId>com.ay</groupId>
    <artifactId>Ay_Parent</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
  1. 配置测试覆盖率报告生成插件,
<plugin>
      <groupId>org.jacoco</groupId>
      <artifactId>jacoco-maven-plugin</artifactId>
      <executions>
            <execution>
                  <id>report-aggregate</id>
                  <phase>verify</phase>
                   <goals>
                       <goal>report-aggregate</goal>
                   </goals>
            </execution>
       </executions>
</plugin>
  1. 只配置了jacoco-maven-plugin,暂时是不能生效生成覆盖率报告,还需要添加以下配置
    • 添加项目到将要生成的覆盖率报告中
<dependencies>
        <dependency>
            <groupId>com.ay</groupId>
            <artifactId>Ay_CRM</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.ay</groupId>
            <artifactId>ERP_DataAccess</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.ay</groupId>
            <artifactId>ERP_Client</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

  • 把需要生成覆盖率的项目作为子模块引入
  <modules>
        <module>../Ay_ERP</module>
        <module>../Ay_Security</module>
    </modules>
  1. 上面提到report-aggregate需要集成maven-surefire-plugin来给每个单独子模块(子项目)生成测试数据:jacoco.exec 。所在,在Ay_Parent配置好maven-surefire-pluginjacoco-maven-plugin确保每个单独的项目可以正确生成测试数据文件
 <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
                <configuration>
                    <failIfNoTests>false</failIfNoTests>
                    <argLine>@{surefireArgLine}</argLine>
                    <skip>${maven.test.skip}</skip>
                    <skipTests>${skipTests}</skipTests>
                </configuration>
                <executions>
                    <execution>
                        <id>unit-tests</id>
                        <phase>test</phase>
                        <goals>
                            <goal>test</goal>
                        </goals>
                       
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.1</version>
                <configuration>
                    <skip>${skipTests}</skip>
                    <output>file</output>
                    <append>true</append>
                </configuration>
                <executions>
                    <execution>
                        <id>pre-unit-test</id>
                        <phase>initialize</phase>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <propertyName>surefireArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>post-unit-test</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
  1. 生成的测试覆盖率大概如下:

coverage-coverage coverage-coverage

如果希望在生成的aggregate report里排除不需要测试的包或java,比如DTO包/bean包等java bean类,把jacoco-maven-plugin配置调整如下,加入excludes元素,用来拓除指定的类或者包

 <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>report-aggregate</id>
                        <phase>verify</phase>
                        <configuration>
                            <excludes>
                                <exclude>com/**/*</exclude>
                            </excludes>
                        </configuration>
                        <goals>
                            <goal>report-aggregate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>