Spring boot 项目搭建

 一 spring 官网生成基本代码框架

        1 生成并下载框架代码

Spring | Quickstart https://start.spring.io/

        Project: 项目构建工具熟练哪个选哪个,这里选择Maven。

        Language: 开发使用的语言,熟练哪个选哪个,这里选择Java。

        SpringBoot: spring boot 的版本,选3.2.1。一般使用最新稳定版。

        Project Metadata:根据自己项目信息填写。Packaging一般选Jar,后续部署比较方便,如不需要灵活配置web容器的选择War。Java版本选择了17。

        Dependencies: 选择了基本的Spring Web和MyBatis Framework。

        参数填写完后,点下方EXPLORE按钮。在下发界面点击DOWNLOAD按钮,下载代码。

        2 关于spring-boot-starter-parent。

        自动生成的项目是作为spring-boot-starter-parent的子模块定义的,在pom.xml中有如下定义:

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

         在parent中定义了各种jar包版本和构建工具版本和配置。

        如果不想使用spring-boot-starter-parent中定义的相关版本,可以在自动生成的项目中去除parent(spring-boot-starter-parent),之后自己重新定义需要的版本,或者在子模块中直接重新定义新版本覆盖掉父模块中的定义。

        该模块中的pom.xml具体内容可以去maven的仓库中下载jar包查看。或者查看该链接spring自动生成框架代码依赖父模块的pom.xml内容

       

二  将项目导入到Intellij IEDA

        1 导入下载的代码。

        

         2  设置项目JDK

        IDEA中项目设置的快捷键ctrl+shfit+alt+s

        3 maven配置

 三 集成Mybatis + SQLite

        1 添加sqlite-jdbc

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.44.1.0</version>
</dependency>

         2 application.yml添加如下配置

spring:
  datasource:
    url: jdbc:sqlite:E:/sqlite-test.db
    driver-class-name: org.sqlite.JDBC
    username:
    password:
mybatis:
  mapper-locations: classpath*:mapper/*Mapper.xml
  type-aliases-package: com.xxx.xxx.api

        3  启动类增加注解

            @MapperScan("com.xxx.xxx.api.db.mapper")

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.xxx.xxx.api.db.mapper")
public class XXXXApiApplication {

	public static void main(String[] args) {
		SpringApplication.run(XXXXApiApplication.class, args);
	}

}

 四 配置动态数据源 

         动态数据源实现界面没新建一个分析项目,后端需要对应新增一个数据库,并且以后该分析项目的数据都要在新数据库中操作。即一个分析项目对应一个数据库,分析项目在运行时动态增加,还有一个默认数据库放所有分析项目通用数据。

        配置的总体结构

        相关文件

         application.yml

spring:
  datasource:
    druid:
      default:
        url: jdbc:sqlite:E:/sqlite-test.db
        driver-class-name: org.sqlite.JDBC
        username:
        password:
        validation-query: "select 1"
mybatis:
  mapper-locations: classpath*:mapper/*Mapper.xml
  type-aliases-package: com.xx.xxx.xx.api

         DataSourceConfig.java


import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    @Value("${mybatis.mapper-locations}")
    private String mapperXmlLocationPattern;
    @Value("${mybatis.type-aliases-package}")
    private String typeAliasesPackage;

    /**
     * 生成spring datasource bean
     * 根据spring配置文件生成默认datasource
     * @return default datasource
     */
    @Primary
    @Bean
    @ConfigurationProperties("spring.datasource.druid.default")
    public DataSource dataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 配置spring  dynamicDataSource bean
     * @param dataSource 默认数据源
     * @return dynamicDataSource
     */
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dynamicDataSource(DataSource dataSource) {
        DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance();
        dynamicDataSource.addDataSource("default",dataSource);
        dynamicDataSource.setDefaultTargetDataSource(dataSource);
        return dynamicDataSource;
    }

    /**
     * 配置mybatis 的sqlSessionFactory bean。
     * 如果没有手动配置,mybatis-spring-boot-starter中会自动配置,所有一般没有使用动态数据源都不会配置该bean。
     * @param dynamicDataSource 自己实现的动态数据源
     * @return 的sqlSessionFactory
     * @throws Exception exception
     */
    @Primary
    @Bean(name="sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(
            @Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dynamicDataSource);
        factory.setTypeAliasesPackage(typeAliasesPackage);
        factory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(mapperXmlLocationPattern));
        return factory.getObject();
    }

    /**
     * mybatis 需要的sqlSessionTemplate bean
     * sqlSessionFactory bean类似,没有手动配置时,mybatis-spring-boot-starter中会自动配置。
     * @param sqlSessionFactory
     * @return
     */
    @Primary
    @Bean(name = "sqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(
            @Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

        附加mybatis-spring-boot-autoconfigure 中关于 sqlSessionFactory自动配置的代码段

@Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    if (properties.getConfiguration() == null || properties.getConfiguration().getVfsImpl() == null) {
      factory.setVfs(SpringBootVFS.class);
    }
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    applyConfiguration(factory);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }
    if (this.databaseIdProvider != null) {
      factory.setDatabaseIdProvider(this.databaseIdProvider);
    }
    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }
    if (this.properties.getTypeAliasesSuperType() != null) {
      factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
    }
    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(this.typeHandlers)) {
      factory.setTypeHandlers(this.typeHandlers);
    }
    Resource[] mapperLocations = this.properties.resolveMapperLocations();
    if (!ObjectUtils.isEmpty(mapperLocations)) {
      factory.setMapperLocations(mapperLocations);
    }
    Set<String> factoryPropertyNames = Stream
        .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
        .collect(Collectors.toSet());
    Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
    if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
      // Need to mybatis-spring 2.0.2+
      factory.setScriptingLanguageDrivers(this.languageDrivers);
      if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
        defaultLanguageDriver = this.languageDrivers[0].getClass();
      }
    }
    if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
      // Need to mybatis-spring 2.0.2+
      factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
    }
    applySqlSessionFactoryBeanCustomizers(factory);
    return factory.getObject();
  }

         DataSourceContextHolder.java


/**
 * 保存当前线程中,需要使用的datasource名称。
 */
public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static synchronized void setDataSourceName(String dataSourceName) {
        contextHolder.set(dataSourceName);
    }

    public static String getDataSourceName() {
        return contextHolder.get();
    }

    public static void clearDataSourceName() {
        contextHolder.remove();
    }
}

        DynamicDataSource.java


import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 可用数据源集合
     */
    private static final Map<Object, Object> dataSourceMap = new HashMap<>();

    /**
     * 单例
     */
    private DynamicDataSource(){
        super.setTargetDataSources(dataSourceMap);
    }
    private static final class InstanceHolder {
        private static final DynamicDataSource instance = new DynamicDataSource();
    }
    public static synchronized DynamicDataSource getInstance() {
        return InstanceHolder.instance;
    }

    /**
     * 新增一个数据源
     * @param dsName 数据源名称
     * @param dataSource 数据源
     */
    public void addDataSource(String dsName,DataSource dataSource) {
        dataSourceMap.put(dsName,dataSource);
        super.afterPropertiesSet();// 必须添加该句,否则新添加数据源无法识别到
    }

    /**
     * 判断dsName是否已经存在
     * @param dsName
     * @return
     */
    public boolean existDataSource(String dsName) {
        return dataSourceMap.get(dsName) != null;
    }

    /**
     * 数据源长时间不使用,可以删除
     * @param dsName 数据源名称
     */
    public void removeDataSource(String dsName) {
        dataSourceMap.remove(dsName);
        super.afterPropertiesSet();// 必须添加该句,否则删除无效
    }

    /**
     * 必须实现其方法
     * 根据该方法返回值,确定使用哪个数据源
     */
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceName();
    }
}

         TestController.java


import com.alibaba.druid.pool.DruidDataSource;
import com.sourcefides.sourceye.defectmgmt.api.db.DataSourceContextHolder;
import com.sourcefides.sourceye.defectmgmt.api.db.DynamicDataSource;
import com.sourcefides.sourceye.defectmgmt.api.db.entity.TestTable;
import com.sourcefides.sourceye.defectmgmt.api.db.mapper.TestTableMapper;
import com.sourcefides.sourceye.defectmgmt.api.entity.out.BaseOutParam;
import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/api/v1/test")
public class TestController {

    @Autowired
    TestTableMapper testTableMapper;

    @GetMapping(value = "/name")
    public BaseOutParam searchOrderForm(@Parameter String dbName) {
        if (dbName != null) {
            DataSourceContextHolder.setDataSourceName(dbName);
        }
        BaseOutParam outParam = new BaseOutParam();
        TestTable testTable = testTableMapper.selectByPrimaryKey(1);
        outParam.setCode(200);
        outParam.setMessage("success");
        outParam.setData(testTable.getName());
        return outParam;
    }
    @GetMapping(value = "/changeDB")
    public BaseOutParam changeDB(@Parameter String dbName) {
        DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance();
        if (!dynamicDataSource.existDataSource(dbName)) {
            DruidDataSource dDataSource = new DruidDataSource();
            dDataSource.setUrl("jdbc:sqlite:E:/sqlite-test3.db");
            dDataSource.setDriverClassName("org.sqlite.JDBC");
            dDataSource.setUsername("");
            dDataSource.setPassword("");
            dynamicDataSource.addDataSource(dbName,dDataSource);
        }
        DataSourceContextHolder.setDataSourceName(dbName);
        TestTable testTable = testTableMapper.selectByPrimaryKey(1);
        BaseOutParam outParam = new BaseOutParam();
        outParam.setCode(200);
        outParam.setMessage("success");
        outParam.setData(testTable.getName());
        return outParam;
    }
}

        TestTableMapper.java


import com.xxx.xxx.xxx.api.db.entity.TestTable;

public interface TestTableMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(TestTable record);

    int insertSelective(TestTable record);

    TestTable selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(TestTable record);

    int updateByPrimaryKey(TestTable record);
}

                TestTable.java


public class TestTable {
    private Integer id;

    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }
}

        TestTableMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxx.xxx.xxx.api.db.mapper.TestTableMapper">
  <resultMap id="BaseResultMap" type="com.xxx.xxx.xxx.api.db.entity.TestTable">
    <id column="ID" jdbcType="INTEGER" property="id" />
    <result column="NAME" jdbcType="VARCHAR" property="name" />
  </resultMap>
  <sql id="Base_Column_List">
    ID, NAME
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from TEST_TABLE
    where ID = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from TEST_TABLE
    where ID = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.xxx.xxx.xxx.api.db.entity.TestTable">
    insert into TEST_TABLE (ID, NAME)
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="com.xxx.xxx.xxx.api.db.entity.TestTable">
    insert into TEST_TABLE
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        ID,
      </if>
      <if test="name != null">
        NAME,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.xxx.xxx.xxx.api.db.entity.TestTable">
    update TEST_TABLE
    <set>
      <if test="name != null">
        NAME = #{name,jdbcType=VARCHAR},
      </if>
    </set>
    where ID = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.xxx.xxx.xxx.api.db.entity.TestTable">
    update TEST_TABLE
    set NAME = #{name,jdbcType=VARCHAR}
    where ID = #{id,jdbcType=INTEGER}
  </update>
</mapper>

 五 利用mybatis-generator生成mybatis的配置文件

        1 generatorConfig.xml

        mybatis-generator配置文件内容如下,需要修改jdbc驱动包位置、数据库信息、生成文件位置(有3处)以及mybatis-generator生成策略的一些配置。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

    <!--指定特定数据库的jdbc驱动jar包的位置-->
    <classPathEntry location="xxx.jar"/>

    <context id="default" targetRuntime="MyBatis3">

        <!-- optional,旨在创建class时,对注释进行控制 -->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--jdbc的数据库连接 -->
        <jdbcConnection
                driverClass="com.mysql.cj.jdbc.Driver"
                connectionURL="jdbc:mysql://111.111.2.111:3306/111?useUnicode=true;characterEncoding=utf-8;serverTimezone=UTC"
                userId="1111"
                password="1111">
        </jdbcConnection>


        <!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>


        <!-- Model模型生成器,用来生成含有主键key的类,记录类 以及查询Example类
            targetPackage     指定生成的model生成所在的包名
            targetProject     指定在该项目下所在的路径
        -->
        <javaModelGenerator targetPackage="com.xxx.db.entity"
                            targetProject="src/main/java">

            <!-- 是否允许子包,即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否对model添加 构造函数 -->
            <property name="constructorBased" value="true"/>
            <!-- 是否对类CHAR类型的列的数据进行trim操作 -->
            <property name="trimStrings" value="true"/>
            <!-- 建立的Model对象是否 不可改变  即生成的Model对象不会有 setter方法,只有构造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>

        <!--Mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 -->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="src/main/resources">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码
                type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
                type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对 象
                type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口
        -->
        <javaClientGenerator targetPackage="com.xxx.db.mapper"
                             targetProject="src/main/java" type="XMLMAPPER">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!--配置要生成的表名和实体类名称-->
        <table tableName="A_TEST" domainObjectName="Test"
               enableCountByExample="false"
               enableUpdateByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               selectByExampleQueryId="false"
               enableSelectByPrimaryKey="true"
               enableUpdateByPrimaryKey="true"
               enableDeleteByPrimaryKey="true">
        </table>
    </context>
</generatorConfiguration>

        如果要覆盖数据库定义的字段类型,可以使用以下配置:

        <table tableName="tableName" domainObjectName="domainName"
               enableCountByExample="false"
               enableUpdateByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               selectByExampleQueryId="false"
               enableSelectByPrimaryKey="true"
               enableUpdateByPrimaryKey="true"
               enableDeleteByPrimaryKey="true">
            <!-- 将Integer字段的javaType设置为Long -->
            <columnOverride javaType="java.lang.Long" column="id"/>
        </table>

         2 新建一个空的maven项目

          新建一个空的maven项目,在pom.xml中加入mybatis-generator的jar包依赖


        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.2</version>
        </dependency>

        3 运行生成

         mybatis-generator其实是mave的一个插件,可以直接命令行运行插件命令,也可以在IntelliJ IDEA中配置一个Run Configurations。

        IntelliJ IDEA Run/Debug Configurations

        菜单路径:Run>Edit Configurations
        在弹出界面配置增加一个Maven配置项。
            Name输入:MybatisConfigGenerator
            Run输入: mybatis-generator:generate -e
            Working directory: 选择当前项目即可

        直接用mvn命令执行

        mvn mybatis-generator:generate -e

        mybatis-generator使用注意事项:

        1 如果表没有设置组件,mapper中只能生成insert方法。

        2 此参数设置为false,数据类不生成构造函数。 <property name="constructorBased" value="false"/>

        3 该命令要在项目的根目录下执行,因为这个mavne命令需要依赖pom.xml。

生成成功后,将文件copy到需要使用的项目中。

        4 IDEA插件加持

        使用MyBatisX和Database Tools and SQL(默认安装) 和更加方便的编写数据库操作部分代码。

六 配置日志

     1 logback-boot.xml

       日志使用logback。在项目的resources目录下增加一个logback-boot.xml文件

<configuration>
    <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,%i索引【从数字0开始递增】 -->
    <!-- 级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE  -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d %p (%file:%line\)- %m%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
    <!-- 以下的大概意思是:1.先按日期存日志,日期变了,将前一天的日志文件名重命名为XXX%日期%索引,新的日志仍然是sys.log -->
    <!--             2.如果日期没有发生变化,但是当前日志的文件大小超过1KB时,对当前日志进行分割 重命名-->
    <appender name="syslog"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>xxx.log</File>
        <!-- rollingPolicy:当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名。 -->
        <!-- TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 活动文件的名字会根据fileNamePattern的值,每隔一段时间改变一次 -->
            <fileNamePattern>xxx.%d.%i.log</fileNamePattern>
            <!-- 每产生一个日志文件,该日志文件的保存期限为30天 -->
            <maxHistory>30</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy  class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%d %p (%file:%line\)- %m%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
    <logger name="com.xxx.xxx" level="DEBUG">
        <appender-ref ref="syslog" />
    </logger>
</configuration>

         2 spring配置

logging:
  config: classpath:logback-boot.xml

        3 使用

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger logger = LoggerFactory.getLogger(Xxx.class);

七 根据openapi定义文件生成接口定义代码

        1 OpenAPI Generator

        openapi定义和维护的工具很多,例如swaggerEditor

swagger-editoricon-default.png?t=N7T8https://editor-next.swagger.io/IDEA插件 OpenAPI Generatoricon-default.png?t=N7T8https://plugins.jetbrains.com/plugin/8433-openapi-generatorapifoxicon-default.png?t=N7T8https://apifox.com/        后面2个工具生成代码引擎都是用的OpenAPI Generator。

        这里介绍OpenAPI Generator进行服务端spring代码生成。

        在OpenAPI Generator源代码中可以明显看出,这个工具可以多端运行,命令行、gradle插件、maven插件、在线生成。

        

                 这里介绍maven插件方式生成两种情况下的代码。

                1.1 完整的springboot项目代码

                   pom.xml增加如下插件配置:


    <build>
        <plugins>
            <plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <!-- RELEASE_VERSION -->
                <version>7.2.0</version>
                <!-- /RELEASE_VERSION -->
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
                            <generatorName>spring</generatorName>
                            <library>spring-boot</library>
                            <configOptions>
                                <sourceFolder>src/gen/java/main</sourceFolder>
                            </configOptions>
                            <output>${project.basedir}/target/codegen</output>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

         生成代码如下:

         其中包含项目的构建文件(gradle、mavne),api文档、api定义文件、api的springboot代码。api代码如下:

        其中包括服务端和客户端代码,启动类测试类等。 一般项目开发中不会使用这种方式生成,自己项目中只需要api定义controller和数据实体部分代码。

                1.2 只有spring controller api和数据实体类代码

                pom.xml中增加如下配置:


    <build>
        <plugins>
            <plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <!-- RELEASE_VERSION -->
                <version>7.2.0</version>
                <!-- /RELEASE_VERSION -->
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
                            <generatorName>spring</generatorName>
                            <library>spring-boot</library>
                            <apiPackage>com.xxx.api.define</apiPackage>
                            <modelPackage>com.xxx.api.define.model</modelPackage>
                            <generateApis>true</generateApis>
                            <generateModels>true</generateModels>
                            <generateSupportingFiles>false</generateSupportingFiles><!--此处设置为true,会生成完整的spring boot项目-->
                            <generateApiDocumentation>false</generateApiDocumentation>
                            <generateApiTests>false</generateApiTests>
                            <generateModelTests>false</generateModelTests>
                            <skip>false</skip>
                            <addCompileSourceRoot>false</addCompileSourceRoot>
                            <output>${project.basedir}</output>
                            <skipValidateSpec>true</skipValidateSpec>
                            <skipIfSpecIsUnchanged>true</skipIfSpecIsUnchanged>
                            <configOptions>
                                <dateLibrary>java17</dateLibrary>
                                <useJakartaEe>true</useJakartaEe>
                                <interfaceOnly>true</interfaceOnly>
                                <skipDefaultInterface>true</skipDefaultInterface>
                                <returnSuccessCode>true</returnSuccessCode>
                                <useResponseEntity>false</useResponseEntity>
<!--                                <delegatePattern>true</delegatePattern>-->
                            </configOptions>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

        skipIfSpecIsUnchanged:设置为true,在接口定义文件修改后,才重新生成接口代码。避免每次编译都重新生成。

        hideGenerationTimestamp: 设置为true,生成文件不带时间戳,避免未修改的接口在生成文件时,文件发生变动。生成文件带生成时间,每次生成都会造成文件的变动,对于代码同步十分不友好 。

        生成代码如下:

         

        生成的代码可以放在单独的模块。在具体实现接口的模块,引用该模块,实现相应接口即可。

        例如:

        在后续接口变更之后,重新编译接口定义模块。如果接口发生了变化,例如接口参数变化,新增了接口等,实现接口的模块一般都无法编译通过,这就一定程度上确保了代码和接口定义文件的一致性。 

        2 apifox生成代码按钮位置

         

 

八 jsonschema 转 pojo

jsonschema2pojoGenerate Plain Old Java Objects from JSON or JSON-Schema.icon-default.png?t=N7T8https://www.jsonschema2pojo.org/

        idea配置 run configuration

        pom.xml增加配置

    <build>
        <plugins>
            <plugin>
                <groupId>org.jsonschema2pojo</groupId>
                <artifactId>jsonschema2pojo-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <sourceDirectory>${basedir}/src/main/resources/schema</sourceDirectory>
                    <targetProject>${basedir}/src/main/java</targetProject>
                    <targetPackage>com.example.types</targetPackage>
                </configuration>
            </plugin>
        </plugins>
    </build>

 

 

 

 配置后,双击下面图标即可生成pojo类,生成的文件在target目录下,需要copy到需要的位置。

也可以直接在命令行运行命令: mvn jsonschema2pojo:generate

  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值