SpringBoot application-project construction guide

Article Directory


1. Engineering structure specification

Insert picture description here1. Data persistence layer: the underlying MySQL, Oracle, Hbase, etc. for data interaction
2. Each level has its own data encapsulation entity classes: DO, DTO, VO, query, etc.
3. The dao layer introduces database information, in addition to entity classes, but also Including mapper
4. From the perspective of jar import,
manager introduces dao, service introduces manager
web (controller layer), introduces service
external interface api layer and introduces service, but it is recommended not to introduce controller layer.
5. From the call relationship, it is the call hierarchy relationship as shown in the figure.
6. The common package
does not need to introduce the dao layer, which contains various tools, enumerations or constants, which are directly referenced by the manager


Two, create a springBoot project

1. In Idea, we can directly use Maven to create

2. Continue to create various sub-modules in the large outer module

Insert picture description hereInsert picture description here

3. Introduce the package according to the engineering structure specification

<!--这是service层,由于没有manager,则需要直接引入common和dao-->
<dependencies>
    <dependency>
        <groupId>com.rj</groupId>
        <artifactId>common</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>com.rj</groupId>
        <artifactId>dao</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

<!--这是api层-->
<!--可以将项目中其他模块jar包引入,版本号与当前模块的parent一致-->
<dependency>
    <groupId>com.rj</groupId>
    <artifactId>service</artifactId>
    <version>${project.version}</version>
</dependency>

4. Introduce springBoot dependency for version control

  • 1. Version control must be controlled in the pom file of the parent module, that is, the SpringBootTestmodule
<!--版本号控制-->
<properties>
    <spring-boot-parent.version>2.0.1.RELEASE</spring-boot-parent.version>
    <spring-boot-mybatis.version>2.0.0</spring-boot-mybatis.version>
    <mybatis-plus-boot-starter>3.4.2</mybatis-plus-boot-starter>
    <mybatis-plus-generator>3.4.0</mybatis-plus-generator>
    <hutool-all.version>5.6.6</hutool-all.version>
    <spring-boot-druid.version>1.1.10</spring-boot-druid.version>
    <mysql.version>5.1.29</mysql.version>
    <lombok.version>1.16.10</lombok.version>
    <junit4.version>4.12</junit4.version>
    <java.version>1.8</java.version>
    <spring-boot-maven-plugin.version>2.2.5.RELEASE</spring-boot-maven-plugin.version>
</properties>

<!--这个是版本控制管理,不会引入jar包,而只是声明名称和版本,具体的引用在子模块-->
<dependencyManagement>
    <dependencies>
        <!--引入需要的springBoot组件,需要指定版本号${version}-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot-parent.version}</version>
        </dependency>
        <!--log日志,内置的是logback-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
            <version>${spring-boot-parent.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${spring-boot-mybatis.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
            <version>${spring-boot-parent.version}</version>
        </dependency>
        <!--自动生成domain/dao层的框架,可以不需要-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus-generator}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring-boot-parent.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${spring-boot-druid.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool-all.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit4.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
The biggest difference between use dependencyManagementand direct use dependenciesis that the former only declares what dependencies to use and the corresponding version number, but does not actually import the dependencies, even if it is red, it does not matter; the latter will actually import the dependencies.
Therefore, using the former, we can introduce the actual jar package in the sub-module according to the actual situation of the sub-module; while the latter will be introduced in all sub-modules, which is very non-compliant
  • 2. Really introduce jar package dependencies in the submodules according to the actual situation
<!--这是api层,我们只需要引入spring-boot-starter-web的jar包-->
<dependencies>
    <!--子模块根据需要实际引入jar包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-test</artifactId>
	</dependency>
</dependencies>
Insert picture description hereIn the same way, the database, mybatis, mybatis-plus and other corresponding jar packages
can be introduced in the dao layer. Log dependencies can be introduced in the common. According to the principle of transitive dependency, there are dependencies in the top api layer.

5. Configuration file

Configure the yml file in the resource directory of the web or api layer, pay attention to the naming convention:application.yml

#dataSource
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mydb3?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
    druid:
      initial-size: 3
      min-idle: 3
      max-active: 10
      max-wait: 60000
      filter:
        stat:
          log-slow-sql: true
          slow-sql-millis: 1

  #环境:env就是pom中的环境选择标签
  profiles:
    active: ${env}

#tomcat端口号
server:
  port: 8089
  servlet:
    context-path: /rj
  tomcat:
    uri-encoding: utf-8

#mybatis
mybatis-plus:
  type-aliases-package: com.rj.domain
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:com/rj/*/mapper/*.xml

#log日志配置
logging:
  config: classpath:logback.xml

同级位置Configure the corresponding development or generate environment configuration files, pay attention to the specifications application-dev.ymland application-pro.yml
configure the environment selection in the corresponding pom file

<!-- 开发环境,默认激活 -->
<profiles>
    <profile>
    	<!--id要和env中的一致-->
        <id>dev</id>
        <properties>
        	<!--这个只是针对的文件后缀,即application-后面的-->
            <env>dev</env>
        </properties>
        <!--默认环境 -->
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <!-- 生产环境 -->
    <profile>
        <id>pro</id>
        <properties>
            <env>pro</env>
        </properties>
    </profile>
</profiles>

同级位置Introduce our log configurationlogback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <!-- 日志路径 -->
    <property name="log_dir" value="/export/Logs/iceStatistic"/>

    <!-- 控制台 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern><!--%black(控制台-)--> %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{10}) - %cyan(%msg%n)
            </pattern>
        </encoder>
    </appender>

    <!-- debug日志 -->
    <appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log_dir}/debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log_dir}/debug-%d{yyyy-MM-dd-HH}.log</fileNamePattern>
            <maxHistory>48</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %thread %X{invokeNo} %logger{40} %msg%n</pattern>
        </encoder>
    </appender>

    <!-- info日志 -->
    <appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log_dir}/info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log_dir}/info-%d{yyyy-MM-dd-HH}.log</fileNamePattern>
            <maxHistory>72</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %thread %X{invokeNo} %logger{40} %msg%n</pattern>
        </encoder>
    </appender>

    <!-- error日志 -->
    <appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log_dir}/error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log_dir}/error-%d{yyyy-MM-dd-HH}.log</fileNamePattern>
            <maxHistory>72</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %thread %X{invokeNo} %logger{40} %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印错误日志 -->
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <logger name="DEBUG_LOG" level="debug" additivity="false">
        <appender-ref ref="debug"/>
    </logger>

    <logger name="INFO_LOG" level="info" additivity="false">
        <appender-ref ref="info"/>
    </logger>

    <logger name="ERROR_LOG" level="error" additivity="false">
        <appender-ref ref="error"/>
    </logger>

    <logger name="com.jd.flight" level="debug" additivity="false">
        <appender-ref ref="stdout"/>
        <appender-ref ref="debug"/>
    </logger>
    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="info"/>
        <appender-ref ref="error"/>
    </root>
</configuration>

6, springBoot startup class

java/com/rj/Create a startup class under the web or api layer

@SpringBootApplication
@MapperScan("com.rj.*.mapper")
public class AppStart {
    public static void main(String[] args) {
        SpringApplication.run(AppStart.class,args);
    }
}

springBootApplication is automatic assembly, MapperScan is a scan of mapper files

Since springBoot has built-in tomcat, start this class directly to start the project

springBoot test class

@SpringBootTest(classes = AppStart.class)
@RunWith(SpringRunner.class)
@Slf4j
public class StudentTest {
	
	//这个是在yml文件中配置的,虽然yml是冒号分隔,但是这里还是需要使用“.”来调用
    @Value("${vipuser.name}")
    private String name;

    @Test
    public void testStudent() {
        log.error("name = {}", name);
    }
}

7. In the web or api layer, the package build needs to be configured in the pom

<!--build-->
<!-- Package as an executable jar -->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <!--可以访问的资源文件路径-->
            <includes>
                <include>application.yml</include>
                <include>application-${env}.yml</include>
                <include>*.txt</include>
                <include>**/*.xml</include>
                <include>**/messages*.properties</include>
                <include>**/*.csv</include>
                <include>**/*.zip</include>
                <include>**/*.ftl</include>
                <include>*.properties</include>
            </includes>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring-boot-maven-plugin.version}</version>
            <configuration>
                <executable>true</executable>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

spring-boot-maven-plugin.versionAnd java.versionversion can also be controlled in the parent pom

8. Packing

This operation is very important. Due to package conflicts, the project fails to start. It is recommended to use plug-ins Maven Helperto eliminate package conflicts. It is recommended to exclude low-version jar packages.

9. Mybatis automatically generates tools

(Optional) Automatically generate domain, mapper, mapper.xml, service and controller according to the database table

//需要引入`mybatis-plus-generator`和`spring-boot-starter-freemarker`

package codegenerate;

import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * 根据数据库表自动生成domain/mapper/service/controller
 */
@RunWith(JUnit4.class)
public class CodeGenerator {

    /**
     * 运行测试生成
     */
    @Test
    public void doGenerator() {
        generator("user",  "student");
    }

    /**
     * 自动生成的方法
     * @param nodeName 业务模块的名称,比如用户user、商品good...,同名会追加类
     * @param tables 表名,可多个,建议单个生成
     */
    @SuppressWarnings("all")
    private static void generator(String nodeName, String... tables) {
        final String fNodeName = StringUtils.isEmpty(nodeName) ? "test" : nodeName;

        //代码生成器
        AutoGenerator mpg = new AutoGenerator();

        //局配置
        GlobalConfig gc = new GlobalConfig();
        
        //获取工程的绝对路径
        final String parentPath = "D:\\Java\\ProjectJ\\springBootTest";
        gc.setAuthor("rj");
        gc.setOpen(false);
        gc.setSwagger2(false);
        mpg.setGlobalConfig(gc);

        //数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mydb3?useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        //包配置: src\main\java\com\rj\domain
        PackageConfig pc = new PackageConfig();

        //生成文件夹
        Map<String, String> configPathInfo = new HashMap(8) {
            {
                put(ConstVal.XML_PATH, parentPath + "\\dao\\src\\main\\java\\com\\rj\\"+fNodeName+"\\mapper");
                put(ConstVal.MAPPER_PATH, parentPath + "\\dao\\src\\main\\java\\com\\rj\\"+fNodeName+"\\mapper");
                put(ConstVal.ENTITY_PATH, parentPath + "\\dao\\src\\main\\java\\com\\rj\\domain\\"+fNodeName+"\\entity");
                put(ConstVal.SERVICE_PATH, parentPath + "\\service\\src\\main\\java\\com\\rj\\"+fNodeName+"\\service");
                put(ConstVal.SERVICE_IMPL_PATH, parentPath + "\\service\\src\\main\\java\\com\\rj\\"+fNodeName+"\\service\\impl");
                put(ConstVal.CONTROLLER_PATH, parentPath + "\\api\\src\\main\\java\\com\\rj\\"+fNodeName+"\\controller");
            }
        };

        //生成对应的类
        pc.setPathInfo(configPathInfo);
        pc.setParent("com.rj");
        pc.setXml(fNodeName+".mapper");
        pc.setMapper(fNodeName+".mapper");
        pc.setEntity("domain."+fNodeName+".entity");
        pc.setService(fNodeName+".service");
        pc.setServiceImpl(fNodeName+".service.impl");
        pc.setController(fNodeName+".controller");
        mpg.setPackageInfo(pc);

        //策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setInclude(tables);

        //表前缀,根据具体情况修改
        strategy.setTablePrefix("");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

10. About @Configurationand@Bean

//@Configuration 创建自己的线程池,并交给spring管理
@Configuration
public class MyThreadExecutorConfig {

    /**
     * 核心线程数
     */
    private static final int CORE_POOL_SIZE = 5;

    /**
     * 最大线程数
     */
    private static final int MAX_POOL_SIZE = 8;

    /**
     * 最大等待时间
     */
    private static final long KEE_PALIVE = 60L;

    /**
     * 线程队列
     */
    private static final int QUEUE_SIZE = 5;

    /**
     * 方法上打上@Bean注解,并指定方法名id,就可以使用方法名直接调用
     */
    @Bean("threadpool")
    public ThreadPoolExecutor getExecutor() {
        return new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEE_PALIVE,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_SIZE),
                new ThreadPoolExecutor.AbortPolicy());
    }
}

//我们也可以使用yml配置文件注入参数
/**
 * 核心线程数
 */
@Value("${mythread.core.size}")
private int CORE_POOL_SIZE;

//调用线程池:这里使用的是@Autowired,所以会根据类名去找,而与我们指定的bean名称id无关
//如果我们使用@Resource,则必须将这个类的名字与我们指定的@Bean的名称id一致
@Autowired
private ThreadPoolExecutor threadpool1324251;
@Resource
private ThreadPoolExecutor threadpool;

//这样就可以直接使用线程池了