SpringBoot+Mybatis攻略

2019-07-01  本文已影响0人  逆风踏雷

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

新建SpringBoot工程。

image.png
image.png 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

上一篇下一篇

猜你喜欢

热点阅读