SpringBoot+Mybatis攻略
这是一篇框架搭建博客,属于入门级,持续完善中。
新建SpringBoot工程。
image.pngimage.png image.png
- SpringBootDevTools 用来热部署。
- Lambok 用来简化开发。
-
Configuration Processor 用来兼容旧项目配置。
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
Ctrl+Alt+Shift+/ ->Registry
image.png
image.png
添加Web页面
https://blog.csdn.net/flygoa/article/details/56674769
添加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