03 the First Spring Boot Microservice Member Service

03 The First Spring Boot Microservice- Member Service #

After the analysis and design work of the previous two chapters, I believe you have a clearer understanding of the overall structure of the project. The remaining work is to pull out the project skeleton based on the design and add flesh to it.

Setting up the project skeleton #

The project is named “parking-project”. Create a Maven project with packaging set to “pom” to manage all the modules. Under the “parking-project” project, create Maven module sub-projects based on the functional modules. Use Eclipse IDE Photon Release (4.8.0) as the IDE tool.

  1. Create the parent project as a Maven Project to manage the sub-module functionalities.

img

  1. Under the parent project, create a sub-module as a Maven Module.

img

As you can see, the sub-module automatically sets the parent project as the “parking-project” parent project. Since the sub-project is built using Spring Boot, select “jar” as the packaging option. Create various sub-modules in the same way, and the final result is as follows:

img

A brief introduction to the functions of each module:

  1. parking-base-serv: A pom project that includes two sub-modules: parking-admin and parking-gateway.
  2. parking-admin: Monitors the running status of the sub-projects.
  3. parking-gateway: Gateway sub-service that works with JWT to implement session management and authentication.
  4. parking-carwash: Car wash sub-service that connects to the park-carwash database.
  5. parking-card: Points sub-service that connects to the park-card database.
  6. parking-charging: Charging sub-service that connects to the parking-charging repository.
  7. parking-finance: Financial sub-service that connects to the parking-finance repository.
  8. parking-member: Membership sub-service that connects to the park-member repository.
  9. parking-resource: Resource sub-service that connects to the park-resource repository.
  10. parking-message: Message sub-service that connects to the park-message repository and stores message data with RocketMQ.
  11. parking-common: Stores common utility classes, entity packages, etc.

This is a manual way of creating the project, and all dependencies need to be manually added. Here, another method is introduced: use the official Spring Initializr to initialize the project directly, generate the sub-projects separately, and then assemble them into a certain project structure.

  1. Choose whether it’s a Maven project or a Gradle project (two different build methods).
  2. Choose the project language: Java, Kotlin, or Groovy, three JVM-based languages.
  3. Choose the Spring Boot version.
  4. Set the project’s groupId and artifact.
  5. Dependency search: after searching, directly click the “+” button to add the dependencies.
  6. Click the “Generate” button below to generate the zip file, then import the project into Eclipse/IntelliJ IDEA.

Official website: https://start.spring.io/

Creating the Spring Boot sub-service #

Add spring-boot-starter-parent dependency #

Each sub-module is a Spring Boot project. If we add the dependency in each sub-module, a lot of duplicate work will be created, and it will be difficult to maintain a unified version, leading to a confusing situation with multiple versions. Therefore, the Spring Boot version needs to be maintained globally.

Each sub-project needs to be built as a jar file. The sub-projects already rely on the configuration of the parent project. Each sub-project’s pom.xml has the following dependencies:

<parent>
    <groupId>com.mall.parking.root</groupId>
    <artifactId>parking-project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

If we add the spring-boot-starter-parent dependency in the typical way, using the parent method, it would violate the standard that there should be only one parent tag in a single pom file, and the compilation will fail.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

To solve this problem, the parking-project parent project imports the spring-boot-starter-parent using dependencyManagement, and the sub-projects depend on the parent’s configuration.

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </dependency>
    </dependencies>
</dependencyManagement>

This is a way to manually create the project, where all the components needed for the project need to be added manually. Another method is to directly use the official Spring Initializr to initialize the project, generate the sub-projects separately, and then assemble them into a certain project structure.

  1. Choose whether it’s a Maven project or a Gradle project (two different build methods).
  2. Choose the project language: Java, Kotlin, or Groovy, three JVM-based languages.
  3. Choose the Spring Boot version.
  4. Set the project’s groupId and artifact.
  5. Dependency search: after searching, directly click the “+” button to add the dependencies.
  6. Click the “Generate” button below to generate the zip file, then import the project into Eclipse/IntelliJ IDEA.

Official website: https://start.spring.io/

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Some people may suggest directly using the parent tag to import in the root project’s pom, and the submodules can be directly dependent through Maven, which works fine when running the Spring Boot project independently. However, when using the same way to import Spring Cloud or Spring Cloud Alibaba, one parent tag is clearly not enough to meet this requirement. Using dependencyManagement can solve this problem.

Importing MBG Plugin #

The MBG Plugin can automatically generate mapper interfaces, mapper XML configurations, and corresponding entity classes, mainly for rapid development, eliminating unnecessary code writing. For more details, please refer to the documentation: https://mybatis.org/generator/

Configure the dependency of the MBG plugin in the pom:

<build>
<finalName>parking-member-service</finalName>
<plugins>
    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.4.0</version>
        <configuration>
            <!-- The configuration file for MyBatis to generate code -->
            <configurationFile>src/test/resources/generatorConfig.xml</configurationFile>
            <verbose>true</verbose>
            <overwrite>true</overwrite>
        </configuration>
    </plugin>
</plugins>
</build>

Write the generatorConfig.xml file in the src/test/resources directory, and configure the basic configuration items required by the MBG plugin:

<generatorConfiguration>
<!-- The location of the local mysql driver -->
  <classPathEntry location="/Users/apple/.m2/repository/mysql/mysql-connector-java/5.1.42/mysql-connector-java-5.1.42.jar" />

  <context id="mysqlTables" targetRuntime="MyBatis3">
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/park-member?useUnicode=true" userId="root"
                        password="root">
                        <property name="useInformationSchema" value="true"/>
    </jdbcConnection>

    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

<!-- Generating model entity class file location -->
    <javaModelGenerator targetPackage="com.mall.parking.member.entity" targetProject="src/test/java">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>
<!-- Generating mapper.xml configuration file location -->
    <sqlMapGenerator targetPackage="mybatis.mapper"  targetProject="src/test/resources">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>
<!-- Generating mapper interface file location -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.mall.parking.member.mapper"  targetProject="src/test/java">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

<!-- The table name corresponding to the entity class that needs to be generated, copy this configuration multiple times for multiple entity classes -->
     <table tableName="member" domainObjectName="Member">
        <generatedKey column="tid" sqlStatement="SELECT REPLACE(UUID(), '-', '')"/>
    </table>
    <table tableName="vehicle" domainObjectName="Vehicle">
        <generatedKey column="tid" sqlStatement="SELECT REPLACE(UUID(), '-', '')"/>
    </table>
    <table tableName="month_card" domainObjectName="MonthCard">
        <generatedKey column="tid" sqlStatement="SELECT REPLACE(UUID(), '-', '')"/>
    </table>
  </context>
</generatorConfiguration>

After the configuration is completed, right-click on the project name “parking-member” and select “Run As” -> “Maven build…” in the popup menu. In the Goals column, enter the following command:

mybatis-generator:generate

img

After the command is successfully executed, find the corresponding file in the corresponding directory, then copy it to the corresponding directory under src/java, and delete the generated files under the test directory.

Note:

  • For versions before 1.4, the xml files generated by MBG plugin are in append mode, not overwrite mode, which may cause duplicate tags.
  • MBG does not generate controller/service-related code, which needs to be completed manually.

Introducing Lombok to simplify code #

The official definition of Lombok:

“Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.”

Lombok installation #

Official download address of lombok.jar: https://projectlombok.org/download

Since lombok needs to be used during the compilation phase, you need to install the lombok plugin in your IDE in order to compile it normally.

  1. Install lombok in Eclipse

Double-click the downloaded lombok.jar, a selection box will pop up, select “Specify location”, select the installation directory of eclipse, select the execution file, and perform the install/update operation. After the installation is complete, exit. You can see an additional configuration line in the eclipse.ini file:

-javaagent:${eclipse-home}\lombok.jar
  1. Restart Eclipse.

  2. Install lombok in IntelliJ IDEA

Select “Settings”, click the “Plugins” option, click “Browse repositories”, search for “lombok”, select the “lombok plugin” that is filtered out, and install it.

After the installation is complete, introduce the corresponding jar in the pom.xml.

sl4j logging annotations #

If you don’t want to write the following every time:

private final Logger logger = LoggerFactory.getLogger(CurrentClass.class);

You can use the [@Slf4j] annotation to print logs.

Introducing MyBatis #

To introduce MyBatis more efficiently, starter is used here. The component’s version is maintained in the root pom.xml file.

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

Set up the database connection in the application.properties configuration file. By default, Spring Boot 2.x uses HikariCP as the JDBC connection pool.

mybatis.type-aliases-package=com.mall.parking.member.entity

#if you want to switch to Druid connection pool, you need to add the following configuration:
#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#use new driver replace deprecated driver:com.mysql.jdbc.Driver.
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/park_member?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root

To allow the mapper interface files to be scanned by the system, use the [@MapperScan] annotation in the main class or directly use [@Mapper] annotation on the mapper interface file.

Write a simple test method to verify if the framework can run properly.

@RestController
@RequestMapping("member")
@Slf4j
public class MemberController {

    @Autowired
    MemberService memberService;

    @RequestMapping("/list")
    public List<Member> list() {
        List<Member> members = memberService.list();
        log.debug("query member list = " + members);
        return members;
    }

}

MemberService interface:

List<Member> list();

MemberServiceImpl implementation:

@Service
public class MemberServiceImpl implements MemberService {

    @Autowired
    MemberMapper memberMapper;

    @Override
    public List<Member> list() {
        MemberExample example = new MemberExample();
        List<Member> members = memberMapper.selectByExample(example);
        return members;
    }

}

After starting the project, a success.log is displayed:

2019-12-30 16:45:13.496 INFO 9784 --- [ main] c.mall.parking.member.MemberApplication : Started MemberApplication in 6.52 seconds (JVM running for 7.753)

Open the Postman plugin and test the previous method to see if it works properly. The operation is as follows:

img

Multiple Environment Configuration #

In daily product development, it is inevitable to involve multiple deployments, such as development environment, testing environment, production environment, etc. This requires that the code deployment be able to meet the requirements of multiple environments. It is not only easy to make mistakes by manually modifying it, but also wastes manpower cost. Automated building must be combined to improve accuracy. Spring Boot provides multi-environment configuration based on profiles, which allows multiple configuration files to be added to each microservice project, such as:

  • application.properties/yml: Basic common configuration
  • application-dev.properties/yml: Development environment configuration
  • application-test.properties/yml: Test environment configuration
  • application-pro.properties/yml: Production environment configuration

In the common configuration file application.properties, determine which configuration to enable by configuring spring.profiles.active = dev, or add commands to activate different environment configurations when starting the build package: java -jar parking-member.jar --spring.profiles.active=dev

Thus, the first simple Spring Boot module is built. The next step is to complete the coding of the normal business functions of the park-member module. In order to facilitate the smooth progress of the subsequent courses, you can refer to the framework configuration in this article to complete the basic configuration of the remaining sub-modules and achieve the goal of normal use.

A thinking question #

Usually, when a war project is built, it can handle HTTP requests at runtime. How does the Spring Boot project, which builds a jar, also handle HTTP requests? How does it do this?