美文网首页
SpringBoot+Mybatis攻略

SpringBoot+Mybatis攻略

作者: 逆风踏雷 | 来源:发表于2019-07-01 17:12 被阅读0次

    这是一篇框架搭建博客,属于入门级,持续完善中。

    新建SpringBoot工程。

    image.png
    image.png image.png
    • SpringBootDevTools 用来热部署。
    • Lambok 用来简化开发。
    • Configuration Processor 用来兼容旧项目配置。


      image.png
    image.png
    image.png

    支持web

    pom 文件添加web依赖,库中会多出web,hibernate,tomcat等配置


    image.png

    添加Interceptor

    package com.sjqi.design.pattern.demo.interceptor;
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    @Component
    public class AuthInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("asdfasdf");
            String name=request.getParameter("name");
            if("sjqi".equals(name)){
                return true;
            }
            return false;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("after");
        }
    }
    
    
    package com.sjqi.design.pattern.demo;
    
    import com.sjqi.design.pattern.demo.interceptor.AuthInterceptor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class InterceptorConfiguration implements WebMvcConfigurer {
        @Autowired
        AuthInterceptor authInterceptor;
    
        public void addInterceptors(InterceptorRegistry registry) {
    
            registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns("/user/register");
        }
    }
    
    

    添加Filter实现出入参打印

    //1.启动类添加注解
    @SpringBootApplication
    @ComponentScan(basePackages = {"com.sprucetec.pop"})
    @EnableDubboConfiguration
    @ServletComponentScan(basePackages = {"com.sprucetec.pop"})
    public class ApiApplication {
        
        public static void main(String[] args) {
            SpringApplication.run(ApiApplication.class, args);
        }
        
    }
    
    package com.sprucetec.pop.seller.app.gw.product.filter;
    
    import com.alibaba.fastjson.JSON;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.MDC;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Map;
    import java.util.UUID;
    
    /**
     * 过滤每个Controller的请求,打印接口的入参和出参,并加入traceId方便追踪单次请求的所有日志
     *
     * @author qishaojun 2019-08-15
     */
    @WebFilter(urlPatterns = "/*")
    @Slf4j
    public class ParamFilter implements Filter {
        //每行日志最长字符数
        private static final int LOG_MAX_LENGTH=5000;
        private static final String UNIQUE_ID = "traceId";
        //排除包含如下字符的url
        private static final String[] exclutions = new String[]{
                ".css", ".js", ".png", ".jpg", ".html",".woff2"
        };
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            boolean bInsertMDC = insertMDC();
    
            ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(Thread.currentThread().getId(), (HttpServletResponse) servletResponse);
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            Map params;
            String uri = httpServletRequest.getRequestURI();
            ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(httpServletRequest);
            for (String exclution : exclutions) {
                if (StringUtils.containsIgnoreCase(uri, exclution)) {
                    filterChain.doFilter(servletRequest, responseWrapper);
                    return;
                }
            }
            long startTime=System.currentTimeMillis();
            try {
                params = servletRequest.getParameterMap();
                if (null != params && params.size() != 0) {
                    // 打印from格式的入参信息
                    log.info("uri:{},入参:{}",uri, JSON.toJSONString(params));
                } else {
                    // 打印json格式的入参信息
                    String charEncoding = requestWrapper.getCharacterEncoding() != null ? requestWrapper.getCharacterEncoding() : "UTF-8";
                    log.info("uri:{},入参:{}",uri, new String(requestWrapper.getBody(), charEncoding));
                }
                filterChain.doFilter(requestWrapper, responseWrapper);
                String outParam = new String(responseWrapper.toByteArray(), responseWrapper.getCharacterEncoding());
                if(outParam.length()>LOG_MAX_LENGTH){
                    outParam.substring(0,LOG_MAX_LENGTH);
                    outParam+="...";
                }
                long endTime=System.currentTimeMillis();
                log.info("uri:{},接口耗时:{} ms,出参:{}",uri,(endTime-startTime),outParam);
            } finally {
                if (bInsertMDC) {
                    MDC.remove(UNIQUE_ID);
                }
            }
        }
    
        private boolean insertMDC() {
            StringBuilder sb = new StringBuilder(UNIQUE_ID);
            UUID uuid = UUID.randomUUID();
            String uniqueId = sb.append("-").append(uuid.toString().replace("-", "")).toString();
            MDC.put(UNIQUE_ID, uniqueId);
            return true;
        }
    
    
        @Override
        public void destroy() {
    
        }
    }
    
    
    //3.编写出入参数的包装器,方便打印日志
    package com.sprucetec.pop.seller.app.gw.product.filter;
    
    import org.apache.commons.io.IOUtils;
    
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
        private byte[] body;
    
        private BufferedReader reader;
    
        private ServletInputStream inputStream;
    
        public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException {
            super(request);
            loadBody(request);
        }
    
        private void loadBody(HttpServletRequest request) throws IOException {
            body = IOUtils.toByteArray(request.getInputStream());
            inputStream = new RequestCachingInputStream(body);
        }
    
        public byte[] getBody() {
            return body;
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            if (inputStream != null) {
                return inputStream;
            }
            return super.getInputStream();
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            if (reader == null) {
                reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
            }
            return reader;
        }
    
        private static class RequestCachingInputStream extends ServletInputStream {
    
            private final ByteArrayInputStream inputStream;
    
            public RequestCachingInputStream(byte[] bytes) {
                inputStream = new ByteArrayInputStream(bytes);
            }
    
            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
    
            @Override
            public boolean isFinished() {
                return inputStream.available() == 0;
            }
    
            @Override
            public boolean isReady() {
                return true;
            }
    
            @Override
            public void setReadListener(ReadListener readlistener) {
            }
    
        }
    
    }
    
    
    package com.sprucetec.pop.seller.app.gw.product.filter;
    
    
    import org.apache.commons.io.output.TeeOutputStream;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.ServletResponse;
    import javax.servlet.WriteListener;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
        private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
        private PrintWriter writer = new PrintWriter(bos);
        private long id;
    
        public ContentCachingResponseWrapper(Long requestId, HttpServletResponse response) {
            super(response);
            this.id = requestId;
        }
    
        @Override
        public ServletResponse getResponse() {
            return this;
        }
    
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            return new ServletOutputStream() {
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setWriteListener(WriteListener writeListener) {
    
                }
    
                private TeeOutputStream tee = new TeeOutputStream(ContentCachingResponseWrapper.super.getOutputStream(), bos);
    
                @Override
                public void write(int b) throws IOException {
                    tee.write(b);
                }
            };
        }
    
        @Override
        public PrintWriter getWriter() throws IOException {
            return new TeePrintWriter(super.getWriter(), writer);
        }
    
        public byte[] toByteArray(){
            return bos.toByteArray();
        }
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
    }
    
    package com.sprucetec.pop.seller.app.gw.product.filter;
    
    import java.io.PrintWriter;
    
     public class TeePrintWriter extends PrintWriter {
    
        PrintWriter branch;
    
        public TeePrintWriter(PrintWriter main, PrintWriter branch) {
            super(main, true);
            this.branch = branch;
        }
    
        public void write(char buf[], int off, int len) {
            super.write(buf, off, len);
            super.flush();
            branch.write(buf, off, len);
            branch.flush();
        }
    
        public void write(String s, int off, int len) {
            super.write(s, off, len);
            super.flush();
            branch.write(s, off, len);
            branch.flush();
        }
    
        public void write(int c) {
            super.write(c);
            super.flush();
            branch.write(c);
            branch.flush();
        }
    
        public void flush() {
            super.flush();
            branch.flush();
        }
    }
    

    pom文件配置打包

    //主类上增加配置
    <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.1.7.RELEASE</version>
                    <configuration>
                        <mainClass>com.sprucetec.pop.seller.app.gw.main.ApiApplication</mainClass>
                        <layout>ZIP</layout>
                    </configuration>
                    <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
    //顶级pom文件增加配置
    <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <filtering>true</filtering>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.19.1</version>
                    <configuration>
                        <skipTests>true</skipTests>    <!--默认关掉单元测试 -->
                    </configuration>
                </plugin>
    
            </plugins>
        </build>
    

    实现热部署

    https://blog.csdn.net/gang123123123/article/details/85689157
    这种方式是通过快速重启实现的热部署

    image.png
    image.png
    Ctrl+Alt+Shift+/ ->Registry
    image.png
    image.png

    添加Web页面

    https://blog.csdn.net/flygoa/article/details/56674769

    image.png

    添加mybatis并自动生成代码

    1.修改pom增加依赖和插件


    image.png
    image.png

    2.配置generatorConfig.xml,注意修改classPathEntry为驱动包位置,如果出现时区问题,有可能是mysql版本导致的,解决方案是在数据库url增加参数:?serverTimezone=UTC。如果generatorConfig.xml报红叉,点一下左边的小灯泡fetch下,配置内容如下:

    <?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>
        <!-- 数据库驱动:选择你的本地硬盘上面的数据库驱动包-->
        <classPathEntry  location="C:/mysql-connector-java-8.0.16.jar"/>
        <context id="DB2Tables"  targetRuntime="MyBatis3">
            <commentGenerator>
                <property name="suppressDate" value="true"/>
                <!-- 是否去除自动生成的注释 true:是 : false:否 -->
                <property name="suppressAllComments" value="true"/>
            </commentGenerator>
            <!--数据库链接URL,用户名、密码 -->
            <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/design_pattern?serverTimezone=UTC" userId="root" password="root">
                <property name="nullCatalogMeansCurrent" value="true"/>
            </jdbcConnection>
            <javaTypeResolver>
                <property name="forceBigDecimals" value="false"/>
            </javaTypeResolver>
            <!-- 生成模型的包名和位置-->
            <javaModelGenerator targetPackage="com.sjqi.design.pattern.demo.model" targetProject="src/main/java">
                <property name="enableSubPackages" value="true"/>
                <property name="trimStrings" value="true"/>
            </javaModelGenerator>
            <!-- 生成映射文件的包名和位置-->
            <sqlMapGenerator targetPackage="mapping" targetProject="src/main/resources">
                <property name="enableSubPackages" value="true"/>
            </sqlMapGenerator>
            <!-- 生成DAO的包名和位置-->
            <javaClientGenerator type="XMLMAPPER" targetPackage="com.sjqi.design.pattern.demo.mapper" targetProject="src/main/java">
                <property name="enableSubPackages" value="true"/>
            </javaClientGenerator>
            <!-- 要生成的表 tableName是数据库中的表名或视图名 domainObjectName是实体类名-->
            <table tableName="category" domainObjectName="Category" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"></table>
        </context>
    </generatorConfiguration>
    

    3.确保数据库,表正确:

    SET FOREIGN_KEY_CHECKS = 0;
    DROP TABLE IF EXISTS `category`;
    CREATE TABLE `category`
    (
        `id`         int(11)      NOT NULL DEFAULT '0',
        `code`       varchar(20)  NOT NULL COMMENT '编码',
        `name`       varchar(255) NOT NULL COMMENT '名称',
        `parent_id`  int(11)       NOT NULL DEFAULT '-1',
        `is_fresh`   tinyint(1)   NOT NULL COMMENT '是否生鲜:1是 0否',
        `is_package` tinyint(1)   NOT NULL COMMENT '是否包装物:1是 0否',
        `c_t`        int(11)      NOT NULL COMMENT '创建时间',
        `u_t`        int(11)      NOT NULL COMMENT '最后修改时间',
        `status`     tinyint(4)   NOT NULL COMMENT '状态:1 启用 0 封存',
        PRIMARY KEY (`id`)
    ) ENGINE = InnoDB
      DEFAULT CHARSET = utf8 COMMENT ='分类表';
    
    INSERT INTO `category`
    VALUES ('1', '11', '蔬菜','-1', '1', '0', '1458806819', '1458806819', '1');
    
    

    4.配置application.properties文件如下:

    spring.devtools.livereload.enabled=true
    spring.devtools.livereload.port=35729
    spring.devtools.restart.enabled=true 
    
    #设置数据源
    #数据库连接用户名
    spring.datasource.username=root
    #数据库连接密码
    spring.datasource.password=root
    #驱动
    spring.datasource.driver-class-name= com.mysql.jdbc.Driver
    #数据库连接路径
    spring.datasource.url=jdbc:mysql://localhost:3306/design_pattern?serverTimezone=UTC
    #连接池类型
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    
    #连接池配置,因为springboot默认是开启了连接池的,它有默认配置,这一段可以忽略
    # 初始化大小,最小,最大
    spring.datasource.initialSize=5
    spring.datasource.minIdle=5
    spring.datasource.maxActive=20
    # 配置获取连接等待超时的时间
    spring.datasource.maxWait=60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    spring.datasource.timeBetweenEvictionRunsMillis=60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    spring.datasource.minEvictableIdleTimeMillis=300000
    spring.datasource.validationQuery=SELECT 1 FROM DUAL
    spring.datasource.testWhileIdle=true
    spring.datasource.testOnBorrow=false
    spring.datasource.testOnReturn=false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    spring.datasource.poolPreparedStatements=true
    spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    spring.datasource.filters=stat,wall,log4j
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    
    #配置mybatis
    mybatis.mapper-locations=classpath:mapping/*Mapper.xml
    #全局的映射,不用在xml文件写实体类的全路径
    mybatis.type-aliases-package=com.sjqi.design.pattern.demo.model
    #开启驼峰映射
      mybatis.configuration.map-underscore-to-camel-case=true
    #配置分页插件
    #pagehelper分页插件
      pagehelper.helper-dialect=mysql
      pagehelper.reasonable=true
      pagehelper.support-methods-arguments=true
      pagehelper.params=count=countSql
    

    5.点击第1步图中所示的generator 按钮,生成接口,model,mapper.xml文件。

    日志

    每个请求日志通过filter中,利用MDC加入traceId。
    MDC可以理解为一个日志全局的上下文,能够设置单个应用内日志的全局变量,使用分两步:
    1.、定义日志格式,其中%X{}代表去MDC取值。
    2.、通过拦截器或者AOP在方法调用链最开始,设置MDC的值。MDC.put(UNIQUE_ID, uniqueId);
    参考:
    https://www.jianshu.com/p/8f6c74381dc3
    https://segmentfault.com/a/1190000020618973

    logback.xml的参考配置如下

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!--这个标签能够读取Spring 中的上下文, LOG_PATH 是本地变量名,source是application.yml中的配置名
            defaultValue 还能指定默认值,scope不太理解,说的是: 如果需要将属性存储在local范围之外的其他位置,则可以使用该属性
        -->
        <springProperty scope="context" name="LOG_PATH" source="logging.path"/>
        <springProperty scope="context" name="APPLICATION_NAME" source="spring.application.name"/>
    
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <layout class="ch.qos.logback.classic.PatternLayout">
                <!--%d日期,%t线程 %p级别 %.40c类 %M方法 %L行 %X{traceId}取MDC放进来的参数-->
                <!--%20.40c 名称空间长度小于20左边填充空格,如果加入-则代表右边填充,长度超过40截取 -->
                <!-- 2019-10-09 11:09:47.545 [http-nio-8082-exec-4] INFO  seller.app.gw.product.filter.ParamFilter doFilter:70 [traceId-3c2132b692eb4102997b06e11bf35d25] - uri:/app/t->
                <!--%m输出具体的日志内容  %n换行  %.-5000m 限制日志长度为5000个字符,.代表截取,默认从右边开始截取,加上- 变成从左边开始截取-->
        <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %.40c %M:%L [%X{traceId}] - %.-5000m%n</Pattern>
            </layout>
        </appender>
        <!-- 日志按照文件大小进行分割-->
        <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>INFO</level>
            </filter>
            <file>${LOG_PATH}/bone.log</file>
            <!--日志的备份策略,会每天备份,当天的日志如果达到了最大20M也会拆分文件,会在指定的日志目录生成类似:bone-2019-09-26-0.log-->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${LOG_PATH}/bone-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
                <!-- 最大文件数量为50个-->
                <maxHistory>50</maxHistory>
                <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <!--每个文件最大20M -->
                    <MaxFileSize>20MB</MaxFileSize>
                </TimeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <encoder>
                <!-- traceId是通过MDC加入进来的-->
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %.40c %M:%L [%X{traceId}] - %m%n</pattern>
            </encoder>
        </appender>
    
        <root>
            <!--level 可以控制日志输出的最低级别-->
            <level value="INFO"/>
            <appender-ref ref="STDOUT" />
            <appender-ref ref="RollingFile"/>
        </root>
    </configuration>
    

    引入swagger

     <!-- 用于JSON API文档的生成-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
    
            <!--用于文档界面展示-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
    
    package com.sprucetec.pop.seller.app.gw.main.configer;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.ParameterBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.schema.ModelRef;
    import springfox.documentation.service.Parameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Configuration
    @EnableSwagger2
    //在application.yml中配置swagger的开关
    @ConditionalOnExpression("${swagger.enable:true}")
    /**
     * swagger配置
     */
    public class SwaggerConfig {
        @Bean
        public Docket createDocket() {
            ParameterBuilder ticketPar = new ParameterBuilder();
            //在所有的接口上添加公共参数:token,pop
            List<Parameter> pars = new ArrayList<Parameter>();
            ticketPar.name("token").description("token")
                    .modelRef(new ModelRef("string")).parameterType("header")
                    .required(true).build(); //header中的ticket参数非必填,传空也可以
            ParameterBuilder popIdPar = new ParameterBuilder();
            popIdPar.name("pop").description("popId")
                    .modelRef(new ModelRef("string")).parameterType("header")
                    .required(true).build();
            pars.add(ticketPar.build());
            pars.add(popIdPar.build());
            Docket docket = new Docket(DocumentationType.SWAGGER_2)
                    .globalOperationParameters(pars)
                    .apiInfo(new ApiInfoBuilder().title("美掌柜---移动端APP接口API")
                            .description("备注:所有的接口必须带有 popID,token")
                            .version("1.0").build())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.sprucetec.pop.seller.app.gw.product"))
                    .paths(PathSelectors.any())
                    .build();
            return docket;
        }
    }
    
    

    AOP的使用

    参考:https://www.cnblogs.com/bigben0123/p/7779357.html

    参数的校验

    参考:https://blog.csdn.net/qq_40325734/article/details/81782955

    打包

    https://www.cnblogs.com/feibazhf/p/7886617.html

    编写Starter。

    参考:https://www.jianshu.com/p/d48142dc0441
    https://www.jianshu.com/p/bbf439c8a203

    starter的用处:1.封装某个模块的依赖;2.提供默认配置且可被使用者覆盖。

    编写步骤:1.新建工程,删除test等多余的依赖。 2.编写配置读取类(@ConfigurationProperties(prefix = "hello")),并添加相应的配置。3.编写对应的Service正常使用相应的配置。
    4.编写自动配置类,按条件加载配置类(@Import+@Conditional)并使之生效(@EnableConfigurationProperties({HelloProperties.class}))
    5.新建元配置文件。

    1.新建工程结构如下


    image.png

    spring.factories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.sjqi.hello.service.HelloAutoConfiguration
    
    package com.sjqi.hello.service;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    @Configuration
    
    @ConditionalOnWebApplication
    //启用HelloProperties配置功能,并加入IOC容器
    @EnableConfigurationProperties({HelloProperties.class})
    //导入HelloService组件
    @Import(HelloService.class)
    public class HelloAutoConfiguration {
    }
    
    package com.sjqi.hello.service;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    @ConfigurationProperties(prefix = "hello")
    @Data
    public class HelloProperties {
        private String msg="World!";
        private String value="happy ";
    }
    
    package com.sjqi.hello.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class HelloService {
        @Autowired
        private HelloProperties helloProperties;
    
        public void sayHello(String name) {
            System.out.println("Hello " + name + " " + helloProperties.getValue());
        }
    }
    
    

    深入SpringBoot原理

    https://www.cnblogs.com/hjwublog/p/10332042.html
    https://www.jianshu.com/p/603d125f21b3

    //TODO:再度一遍,编写自定义的Conditional
    SpringBoot最大的优势是减少配置的工作量,通过提供许多starter,封装了常用的依赖,并提供了相应的配置。 主要原理:配置发现,类的注入。

    Conditional 注解 https://blog.csdn.net/pengjunlee/article/details/79700127

    1.加载一个类的三个方法:@Compant @Configuration+@Bean @Import

    SpringBoot如何修改主配置文件的路径

    1.SpringBoot会打成jar包,在jar包之外有个启动脚本和config配置文件存放目录
    打包配置文件如下:

    <assembly>
        <id>${project.artifactId}-${project.version}</id>
        <includeBaseDirectory>true</includeBaseDirectory>
        <baseDirectory>${project.artifactId}</baseDirectory>
        <formats>
            <format>zip</format>
        </formats>
        <fileSets>
            <fileSet>
                <directory>src/main/bin</directory>
                <outputDirectory>bin</outputDirectory>
                <fileMode>0755</fileMode>
                <includes>
                    <include>**.sh</include>
                </includes>
            </fileSet>
            <fileSet>
                <directory>${basedir}/target/classes</directory>
                <outputDirectory>/conf</outputDirectory>
                <includes>
                    <include>config/*.yml</include>
                </includes>
            </fileSet>
            <fileSet>
                <directory>${basedir}/target</directory>
                <outputDirectory>.</outputDirectory>
                <includes>
                    <include>${project.artifactId}-*.jar</include>
                </includes>
            </fileSet>
        </fileSets>
    </assembly>
    

    2.start.sh脚本编写如下:

    #!/bin/sh
    source /etc/profile
    BASEDIR=`dirname $0`/..
    BASEDIR=`(cd "$BASEDIR"; pwd)`
    MAIN_JAR=$BASEDIR"/pop-seller-ledger-main*.jar"
    CONF_DIR=$BASEDIR/conf
    
    
    # If a specific java binary isn't specified search for the standard 'java' binary
    if [ -z "$JAVACMD" ] ; then
      if [ -n "$JAVA_HOME"  ] ; then
          JAVACMD="$JAVA_HOME/bin/java"
      else
        JAVACMD=`which java`
      fi
    fi
    
    LOGDIR="/data/logs/pop-seller-ledger/"
    
    if [ -z "$OPTS_MEMORY" ] ; then
        OPTS_MEMORY="-server -Xms4G -Xmx4G -Xss512k -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M"
    fi
    
    JAVA_OPTS="${JAVA_OPTS} -jar"
    JAVA_OPTS="${JAVA_OPTS} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:+DisableExplicitGC"
    JAVA_OPTS="${JAVA_OPTS} -verbose:gc -Xloggc:/data/logs/pop-seller-ledger/bone%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
    JAVA_OPTS="${JAVA_OPTS} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
    
    nohup $JAVACMD $JAVA_OPTS \
      $OPTS_MEMORY \
      -Dbasedir="$BASEDIR" \
      -Dfile.encoding="UTF-8" \
      -Djava.awt.headless="true" \
      -Dsun.net.client.defaultConnectTimeout="60000" \
      -Dsun.net.client.defaultReadTimeout="60000" \
      -Djmagick.systemclassloader="no" \
      -Dnetworkaddress.cache.ttl="300" \
      -Dspring.config.location="$CONF_DIR/config/" \
      -Dsun.net.inetaddr.ttl=300 \
      -XX:+HeapDumpOnOutOfMemoryError \
      -XX:HeapDumpPath="$LOGDIR/" \
      -XX:ErrorFile="$LOGDIR/java_error_%p.log" \
      $MAIN_JAR \
      "$@" &
    

    3.spring.config.addtional-location 这个配置和spring.config.location之间是不兼容的。

    SpringBoot 调用外部接口

    https://blog.csdn.net/polo2044/article/details/85002282

    TODO dozzer

    TODO SpringBoot+MybatisPlus+Druid+Sharding-jdbc 整合与分库分表总结

    强制路由:
    1.获取指定表的路由控制权——自定义算法

     t_pop_ledger_order:
              #物理数据节点,Grovey范围表达式:$->{0..1}
              actual-data-nodes: ds$->{0..4}.t_pop_ledger_order
              database-strategy: #分库策略
                hint:
                  algorithmClassName: com.sprucetec.pop.config.ModuloHintShardingAlgorithm
              #          table-strategy:
              #            inline:
              #              sharding-column: order_id
              #              algorithm-expression: t_order_$->{order_id % 2}
    
     defaultDatabaseStrategy: #分库策略
            inline:
              sharding-column: seller_id
              algorithm-expression: ds${seller_id%5}
    
    package com.sprucetec.pop.config;
    
    import org.apache.shardingsphere.api.sharding.hint.HintShardingAlgorithm;
    import org.apache.shardingsphere.api.sharding.hint.HintShardingValue;
    
    import java.util.ArrayList;
    import java.util.Collection;
    
    /**
     * @author qishaojun
     */
    public final class ModuloHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
    
        @Override
        public Collection<String> doSharding(final Collection<String> availableTargetNames, final HintShardingValue<Long> shardingValue) {
            Collection<String> result = new ArrayList<>();
            for (String each : availableTargetNames) {
                for (Long value : shardingValue.getValues()) {
                    if (each.endsWith(String.valueOf(value % 5))) {
                        result.add(each);
                    }
                }
            }
            return result;
        }
    }
    

    2.代码中指定路由因子

    HintManager hintManager = HintManager.getInstance();
            hintManager.addDatabaseShardingValue("t_pop_ledger_order",new Long(4));
    //这里是被指定了数据库的代码
    hintManager.close();
    

    https://www.jianshu.com/p/9eebfae039c9
    https://github.com/huangjian888/jeeweb-mybatis-springboot/blob/v3.0-master/x-micro-service/x-spring-boot-nacos/x-spring-boot-shardingsphere-seata/pom.xml
    https://github.com/apache/incubator-shardingsphere/issues/650
    https://shardingsphere.apache.org/document/current/cn/features/sharding/other-features/key-generator/
    https://www.cnblogs.com/wade-luffy/p/6096578.html

    相关文章

      网友评论

          本文标题:SpringBoot+Mybatis攻略

          本文链接:https://www.haomeiwen.com/subject/yjawoqtx.html