This is the 11th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021

1. Why SpringBoot

The advantage of SpringBoot over traditional SSM frameworks is that it provides default boilerplate configuration and simplifies the initial setup process of Spring applications. If you don’t want to be bothered by a lot of XML configuration files, you can consider using SpringBoot instead

2. What kind of environment

This article will integrate Mybatis and Swagger2 frameworks based on the official fast start project template provided by Spring, and explain the configuration method of Mybatis Generator one-click code generation plug-in, Logback, one-click document generation and multi-environment. Finally, I’ll cover custom configured annotation fetching, global exception handling, and more.

3. Development environment

I use IDEA as a development tool. The quick start project integrated with SpringBoot by default can be directly created when DOWNLOADING IDEA. If you use Eclipse, you can consider installing the SpringBoot plug-in or directly configure and download the SpringBoot quick start project from here. It should be noted that this environment is built with the fast startup framework of SpringBoot2.0, which requires JDK version 1.8 or above.

4. Import the quick start project

Whether the template project is imported by IDEA or downloaded in real life, it is necessary to initialize the configuration of the quick start project. If IDEA is used, choose Spring Initializr when creating a project. The main configuration is shown in the following figure

Click Next and finish. IDEA shows that the template project is being downloaded. After downloading, the template project is created successfully according to the pom. XML download package

Enter Web, Mysql, and Mybatis from the Search for Dependencies box to add dependencies, click Generate Project to download the quick start Project, and then import the Maven Project from the IDE. After importing the Project, you can see its directory structure as shown in the following figure

The first Java class is the entry function that launches the project, the second “Properties” file is the project configuration file, and the third is the project dependency package and the configuration that executes the plug-in

5. Prepare for integration

Yml is more simplified than properties, and many official demos are the configuration form of YML. Here, we use the form of YML instead of properties. There are two main differences compared to the form of properties

  1. The description of the key is separated from the original “.” into a tree shape
  2. All keys must be followed by a space, otherwise the startup project will report a configuration resolution error
Name = mysql spring. Datasource. Url = JDBC :mysql://localhost:3306/db? characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = 123Spring: datasource: name: mysql//localhost:3306/db? characterEncoding=utf-8
        username: root
        password: 123
Copy the code

6. Configure required dependencies

After the quick start project was successfully created, we observed the dependencies in the pem. XML file as shown below, including the Web, Mybatis and Mysql we selected

<! -- spring web mvc --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <! -- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.31.</version> </dependency> <! -- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <! -- test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>Copy the code

In this case, I chose Ali’s Druid and PageHelper as the pagination plugin. In addition, we also need to integrate Swagger2 document automation framework, so we added the following four dependencies

<! <dependency> <groupId>com.github. Pagehelper </groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.23.</version> </dependency> <! > <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.11.</version> </dependency> <! <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.231.</version> </dependency> <! Springfox </groupId> <artifactId> Springfox-swagger2 </artifactId> <version>2.5. 0</version>
        </dependency>
Copy the code

7. Integrated Mybatis

Mybatis includes druID database connection pool, PageHelper pagination plug-in, Mybatis – Generator code reverse generation plug-in, Mapper, POJO scanning configuration

Add the following configuration to the application.yml file

Spring: datasource: # JDBC :mysql://localhost:3306/db? characterEncoding=utf-8Username: root username: root password:123Druid data source type: Com. Alibaba. Druid. Pool. # # DruidDataSource extensions monitoring statistics with the filter: stat logging with the filter: log4j defense SQL injection filter: wall filters: Stat # maxActive:20Number of physical connections established during initialization. Initialization occurs when the init method is called, or the first getConnection initialSize is displayed:1MaxWait:60000MinIdle:1
        timeBetweenEvictionRunsMillis: 60000# connect remain idle without being expelled the longest minEvictableIdleTimeMillis:300000SQL to check whether the connection is valid, the requirement is a query statement # if validationQuery isnullTestOnBorrow, testOnReturn, and testWhileIdle will not function. ValidationQuery:select count(1)The from 'table' # test application connection, if free time is more than timeBetweenEvictionRunsMillis, perform effective testWhileIdle validationQuery detects whether connection:trueTestOnBorrow: Execute validationQuery when applying for a connection to check whether the connection is valid.falseTestOnReturn: Execute validationQuery when returning a connection to check whether the connection is valid.falsePreparedstatements PSCache poolPreparedStatementsfalseTo enable PSCache, the configuration must be greater than 0. When greater than 0, poolPreparedStatements automatically changes totrue
        maxOpenPreparedStatements: -1
Copy the code

8. Configure the PageHelper paging plug-in

# pageHelper pagerocdialect: Mysql > select * from pageNum where pageNum < 1 where pageNum > pages = 'reasonable: true'Copy the code

9. Configuration and operation of the code reverse generation plug-in Mybatis – Generator

There are three main steps to use mybatis- Generator plug-in

  1. Add mybatis- Generator plug-in to POM.xml
    <build>
        <plugins>
            <! Package the Spring Boot application as an executable JAR or WAR file -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <! Mybatis Generator plugin for automatic code generation
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <configuration>
                    <! Generatorconfig. XML configuration in resources/ Generator directory
                    <configurationFile>
                        ${basedir}/src/main/resources/generator/generatorConfig.xml
                    </configurationFile>
                    <overwrite>true</overwrite>
                    <verbose>true</verbose>
                </configuration>
            </plugin>
        </plugins>
    </build>
Copy the code
  1. Create the reverse code generation configuration file generatorconfig.xml

Create a Generator folder in the Resources directory, following the scan location in the pom.xml plug-in configuration. Create a GeneratorConfig.xml configuration file in the newly created folder with the following configuration details


      
<! DOCTYPEgeneratorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <! Mybatis -generator: generate-e -->
    <! Database driver: Select the database driver package on your local hard drive -->
    <properties resource="generator/generator.properties"/>
    <classPathEntry location="${classPathEntry}"/>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <! -- Database link URL, username, password -->
        <jdbcConnection
                driverClass="com.mysql.jdbc.Driver"
                connectionURL="jdbc:mysql://localhost:3306/${db}? characterEncoding=utf-8"
                userId="${userId}"
                password="${password}">
        </jdbcConnection>
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <javaModelGenerator targetPackage="${pojoTargetPackage}" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <! Generate mapping file package name and location -->
        <sqlMapGenerator targetPackage="${mapperTargetPackage}" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <! The package name and location where the DAO was generated
        <javaClientGenerator type="XMLMAPPER" targetPackage="${daoTargetPackage}" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <! TableName is the name of the table or the name of the view in the database.
        <table tableName="%" schema="${db}"/>
    </context>
</generatorConfiguration>
Copy the code

To templatethe GeneratorConfig.xml configuration, here the more volatile configuration items are isolated as a generatorConfig.xml configuration file, and the configuration of this file is read using the properties tag. The advantage of doing this is that you only need to focus on a few configuration items when you need to reuse this XML in multiple places. Create the generatorconfig. XML file at the same level as generatorConfig. XML. Configure the generator.properties file as follows

Please manually configure the following options
# Database driver: Select the database driver package on your local hard drive
classPathEntry = D: / CJH/maven repository/mysql/mysql - connector - Java / 5.1.30 / mysql connector - Java - 5.1.30. Jar
Database name, username, password
db = db
userId = root
password = 123
The poJOs are generated in SRC /main/ Java
pojoTargetPackage = com.spring.demo.springbootexample.mybatis.po
The package name that generated the DAO is in the SRC /main/ Java directory
daoTargetPackage = com.spring.demo.springbootexample.mybatis.mapper
The Mapper package name is located in SRC /main/resources
mapperTargetPackage = mapper
Copy the code

10. Run mybatis -Generator plug-in to generate Dao, Model and Mapping

Run MVN mybatis-generator:generate -eCopy the code

Mybatis scan package configuration Has generated the entity and mapping class corresponding to the specified database, but it cannot be used directly. Only after the myBatis scan address is configured, can it be called normally

  1. Configure mapper. XML and poJO package addresses in application.yml
Mybatis: # mybatis: # mybatis: # mybatis: # mybatis: # mybatis: # mybatis: # mybatis: # mybatis: # mybatis: # com.spring.demo.springbootexample.mybatis.poCopy the code
  1. In SpringBootExampleApplication. Open Mapper scanning annotations in Java
@SpringBootApplication
@MapperScan("com.spring.demo.springbootexample.mybatis.mapper")
public class SpringBootExampleApplication {

    public static void main(String[] args) { SpringApplication.run(SpringBootExampleApplication.class, args); }}Copy the code

Test the effectiveness of mapper

@Controller
public class TestController {
    // Replace it with a self-generated mapper
    @Autowired
    UserMapper userMapper;

    @RequestMapping("/test")
    @ResponseBody
    public Object test(a){
        // Query all the data in this table
        return userMapper.selectByExample(null); }}Copy the code

Start SpringBootExampleApplication. Java main function, if not in the application. The yml specially configured for server port springboot would run using the default port 8080, operation success will print log as follows

Tomcat started on port(s): 8080 (http) with context path ''
Copy the code

Enter the address in the browser. If all data in the table is returned, mybatis integration is successful

http://localhost:8080/test
Copy the code

11. Integration Swagger2

Swagger2 is a quick document construction tool, which can automatically generate a Restful JSON interface document through annotations, and can generate HTML web interface document through tools such as Swagger-UI. The integration of Swagger2 is relatively simple, so you need to be familiar with it. Integration, annotations, and usage are divided into four steps

  1. Build the SwaggerConfig file
@Configuration
public class SwaggerConfig {
    // Interface version number
    private final String version = "1.0";
    // Interface header
    private final String title = "SpringBoot Sample Project";
    // Specific description
    private final String description = "Example of automatic API Document Generation";
    // Service description URL
    private final String termsOfServiceUrl = "http://www.kingeid.com";
    // licence
    private final String license = "MIT";
    // licnce url
    private final String licenseUrl = "https://mit-license.org/";
    // Contact information of the interface author
    private final Contact contact = new Contact("calebman"."https://github.com/calebman"."[email protected]");

    @Bean
    public Docket buildDocket(a) {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(buildApiInf())
                .select().build();
    }

    private ApiInfo buildApiInf(a) {
        return newApiInfoBuilder().title(title).termsOfServiceUrl(termsOfServiceUrl).description(description) .version(version).license(license).licenseUrl(licenseUrl).contact(contact).build(); }}2.In SpringBootExampleApplication. Enable Swagger2 annotations in Java@SpringBootApplicationUnder the notes add@EnableSwagger2Comments Examples of common comments ~~~ XML// Sample annotations in Contorller
@Controller
@RequestMapping("/v1/product")
// Indicates that this class is a swagger resource
@api (value = "DocController", tags = {" Api "})
public class DocController extends BaseController {

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    @ResponseBody
    // Represents an HTTP request operation
    @apiOperation (value = "Modify specified product ", httpMethod = "PUT", Produces = "application/json")
    // @apiIMPLICITParams is used for methods that contain multiple @APIIMPLicitParam for separate request parameters
    @APIIMPLICITParams ({@APIIMPLICITParam (name = "id", value = "product ID", Required = true, paramType = "path")})
    public WebResult update(@PathVariable("id") Integer id, @ModelAttribute Product product) {
        logger.debug("Modify specified product to receive product ID and product information =>%d,{}", id, product);
        if (id == null || "".equals(id)) {
            logger.debug("Product ID cannot be empty");
            return WebResult.error(ERRORDetail.RC_0101001);
        }
        returnWebResult.success(); }}// Sample annotations in Model
// Specifies the class for parameters received by the entity class
@apiModel (value = "product information ")
public class Product {
    // represents a description or data manipulation change to model properties
    @APIModelProperty (Required = true, name = "name", value = "product name", dataType = "query")
    private String name;
    @APIModelProperty (name = "type", value = "product type", dataType = "query")
    private String type;
}
Copy the code

After the integration is successful, the project console prints logs at the INFO level. The intercept part is as follows, indicating that you can access the v2/ apI-docs interface of the application to obtain the JSON data of the document API. You can enter the specified address in the browser to verify the integration

 Mapped "{[/v2/api-docs],methods=[GET],produces=[application/json || application/hal+json]}" 
 http://localhost:8080/v2/api-docs
Copy the code

12. Configure multiple environments

Application research and development process of multiple environments is inevitable, assuming that we now have development, presentation, production three different environment configuration is also different, if every time in the packaging link to configure the inevitable error, SpringBoot support by command to start different environments, However, the configuration file must meet the format of application-{profile}. Properties. Profile indicates the id of the corresponding environment.

Properties: application-dev. Properties: application-test. Properties: application-prod.properties: Production environment # Run the demo environment command Java -jar spring-boot-example-0.0.1 -snapshot --spring.profiles.active=testCopy the code

Yml, application-test. Yml and application-prod. Yml. Put the unchanging public configuration, such as most of druid, the PageHelper paging plug-in, and the Mybatis package scanning configuration, into application.yml, and configure the default development environment in application.yml. If you do not start the application with –spring.profiles.active, the application will start in the development environment by default

Spring: Profiles: # Uses the development environment active: dev by defaultCopy the code

Configuration here our project directory structure looks like the figure below

So far we have completed the integration of Mybatis, Swagger2 and multi-environment respectively. Next we configure logger in multi-environment. For logger we always hope that in the process of project research and development, the more the better, can give enough information to locate the bug, the project is in a state of presentation or online to keep log printed affect application performance we only need a warning or error log, and need to write to the file, then the next based on logback logging configuration to realize more environment

13. Log configuration in multiple environments

Create logback-spring. XML in the same directory as application.yml. For Springboot, it is recommended to use logback-spring. XML instead of logback. XML


      
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <! -- Brief description Log format => %d{HH:mm: ss.sss}(time) [%-5level](log level) % Logger {36}(Maximum 36 characters in logger name, - % MSG %n(specific log information and newline) Development environment => ${basepackage} package console print DEBUG level and above, other package console print INFO level and above demo (test) environment => ${basepackage} Console for package printing at INFO level or higher, console for other packages and file printing at WARN level or higher in production environments => Console and file printing at ERROR level or higher Log files are generated as follows: File generation directory => ${logdir} Log file name of the current day => ${appName}. Log Log file name of other times => ${AppName}.%d{YYYY-MM-DD}. Maximum reserved => ${maxdays} days -->
    <! -- Custom parameters -->
    <! -- Specifies the maximum size of the log file, at which the old log file will be deleted -->
    <property name="maxsize" value="30MB" />
    <! Save only logs generated in the last 90 days.
    <property name="maxdays" value="90" />
    <! --application. Yml pass parameters -->
    <! --log file generated directory -->
    <springProperty scope="context" name="logdir" source="resources.logdir"/>
    <! -- Application name -->
    <springProperty scope="context" name="appname" source="resources.appname"/>
    <! -- Project Base Package -->
    <springProperty scope="context" name="basepackage" source="resources.basepackage"/>

    <! Output to console ConsoleAppender
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <! Display layout-->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                <pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n</pattern>
            </pattern>
        </layout>
    </appender>
    <! Output to FileAppender-->
    <appender name="fileLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <! If there is both <File> and <FileNamePattern>, then the current log is <File>, and tomorrow it will automatically rename today's log to today's date. That is, the logs in <File> are from the current day. -->
        <File>${logdir}/${appname}.log</File>
        <! TimeBasedRollingPolicy-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <! File path, which defines how to split logs -- archive logs for each day into a file to prevent logs from filling the disk space -->
            <FileNamePattern>${logdir}/${appname}.%d{yyyy-MM-dd}.log</FileNamePattern>
            <maxHistory>${maxdays}</maxHistory>
            <totalSizeCap>${maxsize}</totalSizeCap>
        </rollingPolicy>
        <! -- Log output encoding formatting -->
        <encoder>
            <charset>UTF-8</charset>
            <pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <! -- Development environment -->
    <springProfile name="dev">
        <root level="INFO">
            <appender-ref ref="consoleLog"/>
        </root>
        <! Additivity is the flag bit of whether the child Logger inherits the parent Logger's output source (appender). Additivity is set to false to print the child looger if there are INFO level logs in ${basepackage} Root does not print -->
        <logger name="${basepackage}" level="DEBUG" additivity="false">
            <appender-ref ref="consoleLog"/>
        </logger>
    </springProfile>

    <! -- Demo (Test) environment -->
    <springProfile name="test">
        <root level="WARN">
            <appender-ref ref="consoleLog"/>
            <appender-ref ref="fileLog"/>
        </root>
        <logger name="${basepackage}" level="INFO" additivity="false">
            <appender-ref ref="consoleLog"/>
            <appender-ref ref="fileLog"/>
        </logger>
    </springProfile>

    <! -- Production environment -->
    <springProfile name="prod">
        <root level="ERROR">
            <appender-ref ref="consoleLog"/>
            <appender-ref ref="fileLog"/>
        </root>
    </springProfile>
</configuration>
Copy the code

Yml consists of logdir, AppName, and basepackage. Logdir is the address for writing log files and can be passed to a relative path. Appname is the application name. Basepackage is a packet filtering configuration. For example, in the development environment, logs of higher debug level need to be printed, but if you want to prevent the debug from being printed except for the logger I wrote, you can filter the package name of this project and print the debug. In addition, the package name is printed at the INFO level. Create these three configurations in application.yml and configure different properties in different environments

# appname: spring-boot-example # basepackage: com.spring.demo.springbootexampleCopy the code

Use different environments to test whether the Logger configuration takes effect. In the development environment, four logger records at DEBUG level or higher are printed, in the demo environment, three logger records at INFO level or higher are printed and written to the file, and in the production environment, only one logger record at ERROR level or higher is printed and written to the file

@requestMapping ("/logger") @responseBody public WebResult Logger () {logger.trace(" log output {}", "trace"); Logger. debug(" log output {}", "debug"); Logger. info(" log output {}", "info"); Logger. warn(" log output {}", "warn"); Logger. error(" log output {}", "error"); return "00"; }Copy the code

14. Common configurations

Load a custom configuration

@Component @PropertySource(value = {"classpath:application.yml"}, encoding = "utf-8") public class Config { @Value("${resources.midpHost}") private String midpHost; public String getMidpHost() { return midpHost; }}Copy the code

Global exception handler

@ControllerAdvice
public class GlobalExceptionResolver {

    Logger logger = LoggerFactory.getLogger(GlobalExceptionResolver.class);

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public WebResult exceptionHandle(HttpServletRequest req, Exception ex) {
        ex.printStackTrace();
        logger.error("Unknown exception", ex);
        returnWebResult.error(ERRORDetail.RC_0401001); }}Copy the code