Idea 配置阿里云初始化创建 springboot 项目

创建项目 - Spring Initializr - Server URL 设置,修改为 https://start.aliyun.com/

SpringBoot瘦身(lib和程序分离)

http://doc.jeecg.com/2043890

druid 数据库密码加密

http://doc.jeecg.com/2043967

跨域

http://www.spring4all.com/article/177

  • 前端 JSONP
  • 反向代理
  • CORS 「跨域资源共享」
    1. 浏览器支持 CORS
    2. 服务端增加一个特殊的 Header [Access-Control-Allow-Origin]

下面介绍 4 种方法:

1. 返回新的CorsFilter(全局跨域)

在任意配置类中返回一个新的CorsFilter Bean,并添加映射路径和具体的CORS配置信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.bjtcrj.files.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1.添加CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否发送Cookie信息
config.setAllowCredentials(true);
//放行哪些原始域(请求方式)
config.addAllowedMethod("*");
//放行哪些原始域(头部信息)
config.addAllowedHeader("*");
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
config.addExposedHeader("*");

//2.添加映射路径
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);

//3.返回新的CorsFilter.
return new CorsFilter(configSource);
}
}

2. 重写WebMvcConfigurer(全局跨域)

在任意配置类中返回一个新的WebMvcConfigurer Bean,并重写其提供的跨域请求处理的接口,目的是添加映射路径和具体的CORS配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.bjtcrj.files.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* 跨域配置
*/
@Configuration
public class GlobalCorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
//重写父类提供的跨域请求处理的接口
public void addCorsMappings(CorsRegistry registry) {
//添加映射路径
registry.addMapping("/**")
//放行哪些原始域
.allowedOrigins("*")
//是否发送Cookie信息
.allowCredentials(true)
//放行哪些原始域(请求方式)
.allowedMethods("GET","POST", "PUT", "DELETE")
//放行哪些原始域(头部信息)
.allowedHeaders("*")
//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
.exposedHeaders("Header1", "Header2");
}
};
}
}

3. 使用注解(局部跨域)

在方法上(@RequestMapping)使用注解 @CrossOrigin

1
2
3
4
5
6
@RequestMapping("/hello")
@ResponseBody
@CrossOrigin("http://localhost:8080")
public String index( ){
return "Hello World";
}

或者在控制器(@Controller)上使用注解 @CrossOrigin

1
2
3
4
5
6
7
8
9
@Controller
@CrossOrigin(origins = "http://xx-domain.com", maxAge = 3600)
public class AccountController {
@RequestMapping("/hello")
@ResponseBody
public String index( ){
return "Hello World";
}
}

4. 手工设置响应头(局部跨域 )

使用HttpServletResponse对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里Origin的值也可以设置为”*” ,表示全部放行

1
2
3
4
5
6
@RequestMapping("/hello")
@ResponseBody
public String index(HttpServletResponse response){
response.addHeader("Access-Control-Allow-Origin", "http://localhost:8080");
return "Hello World";
}

RequestMapping

DeleteMapping

1
2
3
4
@DeleteMapping
public R delete(@RequestParam("idList") List<Long> idList) {
xxxx
}

请求示例:

1
2
DELETE /cmsTContent?idList=1,2,3 HTTP/1.1
Host: http://localhost:8088/museum

配置

Tomcat

默认 Tomcat 的最大线程数是 200,最大连接数是 10000

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
tomcat:
min-spare-threads: 10
#最大线程数
max-threads: 500
#连接超时时间-如果单个请求处理时间较长「>500ms」,则应调整为较大值;如果单个请求处理很快,则应调整为较小值
connection-timeout: 180000
#最大连接数
max-connections: 10000
#post提交数据最大大小,设置为0不限制
max-http-form-post-size: 0
#编码方式
uri-encoding: UTF-8

配置内存

  • -Xms : 设置 Java 堆栈的初始化大小
  • -Xmx : 设置最大的 java 堆大小
1
java -server -Xms512m -Xmx4096m  -jar springboot-1.0.jar

设置文件编码 UTF-8

1
java -server -Xms512m -Xmx4096m -Dfile.encoding=utf-8 -jar fast-module-basic-mock-start.jar

配置OracleDriver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 默认Hikari连接池
spring:
datasource:
username: scm
password: scm
url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
driver-class-name: oracle.jdbc.OracleDriver

# hikari 配置
type: com.zaxxer.hikari.HikariDataSource
hikari:
auto-commit: true
connection-test-query: SELECT 1 FROM DUAL
connection-timeout: 30000
idle-timeout: 30000
max-lifetime: 1800000
maximum-pool-size: 15
minimum-idle: 5
pool-name: DatebookHikariCP

#druid 连接池
spring:
datasource:
druid:
driver-class-name: oracle.jdbc.OracleDriver
username: cmsczce
password: cmsczce
url: jdbc:oracle:thin:@127.0.0.1:1521:orcl
# 连接池的配置信息
# 初始化大小,最小,最大
initial-size: 5
min-idle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# Spring 监控,利用aop 对指定接口的执行时间,jdbc数进行记录,配置多个英文逗号分隔
aop-patterns: org.jeecg.modules.*.mapper.*,org.jeecg.modules.*.service.*
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000

stat-view-servlet:
enabled: true
loginUsername: admin
loginPassword: 123456
allow:
web-stat-filter:
enabled: true

配置 MySQLDriver

1
2
3
4
5
6
7
8
9
10
# 通用数据源配置-默认Hikari连接池
# jdbc_config datasource
spring.datasource.username=root
spring.datasource.password=root
# 低于 MySQL 8.0
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/datebook?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
# >= MySQL 8.0
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring-boot-jdbc?charset=utf8mb4&useSSL=false&allowPublicKeyRetrieval=true

配置 H2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:file:./emdata/camera
username: sa
password: 123456
h2:
console:
path: /h2-console
enabled: true
settings:
web-allow-others: true
# trace: true
jpa:
hibernate:
ddl-auto: update
# ddl-auto:create----每次运行该程序,没有表格会新建表格,表内有数据会清空
# ddl-auto:create-drop----每次程序结束的时候会清空表
# ddl-auto:update----每次运行程序,没有表格会新建表格,表内有数据不会清空,只会更新
# ddl-auto:validate----运行程序会校验数据与数据库的字段类型是否相同,不同会报错

扫描 mapper 接口

在启动类加上注解

1
@MapperScan("com.example.demo.demo.mapper")

扫描 mybatis mapper xml 文件

application.properties

1
mybatis.mapper-locations=classpath*:/mappers/*.xml

修改端口为 8088application.properties 文件

1
server.port=8088

get 请求头参数大小

Tomcat和Jetty的实际默认值为8kB,Undertow的默认值为1MB。
要修改最大 HTTP header大小,在application.yml文件中进行如下配置:

1
2
server:
max-http-header-size: 20MB

json或x-www-form-urlencoded 请求体大小不限制

1
2
3
4
5
6
server:
tomcat:
# http 请求体最大大小,设置为-1不限制
max-swallow-size: -1
# post 请求体最大大小, -1不限制
max-http-post-size: -1

form-data 最大请求、最大文件大小

1
2
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB

中文乱码

1
2
3
4
5
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.force=true
spring.http.encoding.enabled=true
spring.http.encoding.charset=UTF-8
spring.messages.encoding=UTF-8

自定义配置

1
2
3
4
5
# 自定义配置参数,在启动时可以指定,此处有默认值
params.db=mydb

# 引用自定义配置:${params.db}
spring.datasource.url=jdbc:mysql://jeecg-boot-mysql:3306/${params.db}?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai

编译、打包插件-build:plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<build>
<plugins>
<!--编译插件:maven-compiler-plugin-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- springboot 打包插件:spring-boot-maven-plugin,会打包成可执行的 springboot 启动包-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<!--跳过此打包插件,打成基本的 jar 包-->
<!--<skip>true</skip>-->
<mainClass>com.example.pdfjava.PdfjavaApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

打包时指定 profile 类型

1
mvn clean package -Pdev

打包时排除指定目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<build>
<!--其它配置-->
<!--打包时排除指定目录-->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>static/**</exclude>
<exclude>jeecg/**</exclude>
</excludes>
</resource>
</resources>
</build>

静态资源

  • 默认发布以下4个目录的内容

    1
    2
    3
    4
    classpath:/public/
    classpath:/resources/
    classpath:/static/
    classpath:/META-INFO/resouces/
  • 指定静态资源发布目录

    注意:这个配置会覆盖Spring boot默认的静态资源目录,无法再访问static、public、resources等目录下的资源

    1
    2
    3
    4
    5
    spring:
    mvc:
    static-path-pattern: /image/**
    resources:
    static-locations: classpath:/images/

获取静态资源绝对路径

1
2
ResourceUtils.getURL("classpath:static").getPath();
ResourceUtils.getURL("classpath:resources").getPath();

启动参数

启动类

1
2
3
4
5
6
System.setProperty("nacos.standalone", standalone);
System.setProperty("nacos.core.auth.enabled", enabled);
System.setProperty("server.tomcat.basedir","logs");
//自定义启动端口号
System.setProperty("server.port","8848");
SpringApplication.run(JeecgNacosApplication.class, args);

启动配置参数

1
2
3
4
5
6
7
//修改端口为 9000
//java 程序启动参数:优先级最高
java -jar files-0.0.1-SNAPSHOT.jar --server.port=9000
//JVM 参数第二
java -Dserver.port=9000 -jar files-0.0.1-SNAPSHOT.jar
//优先级最低
//配置文件:server.port=9000

启动时指定外部配置文件

注意:启动 jar 时,默认会从当前目录找 application.yml 或 application-xxx.yml 配置文件

1
2
# 使用外部配置文件
java -jar exam-full-api.jar -Dspring.config.location=D:/ls/application.yml

启动时指定 profile 文件

1
2
3
4
5
# application-dev.yml
java -jar xxx.jar --spring.profiles.active=dev

java -jar --spring.profiles.active=test app.jar --server.port=8081
java -jar -Dspring.profiles.active=test app.jar --server.port=8081

启动时指定 MySQL 数据库地址

  • application.yml 中通过变量指定,当然也有默认值

    MYSQL-HOST: MySQL ip 地址

    MYSQL-PORT:MySQL 端口

    MYSQL-DB:MySQL 数据库名

    1
    2
    3
    4
    5
    6
    7
    spring:
    #配置数据库
    datasource:
    url: jdbc:mysql://${MYSQL-HOST:127.0.0.1}:${MYSQL-PORT:3306}/${MYSQL-DB:jimureport}?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  • 启动命令

    1
    java -DMYSQL-HOST=jimureport-mysql -DMYSQL-PORT=3307 -DMYSQL-DB=jimureport -jar jimureport-example-1.4.jar

读取 application.yml 自定义配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
params:
name: portal

# 事件相关自定义配置:每个系统自定义
event:
# 综合执法队机构编码
departZhifaduiOrgcode: 610204123
# 扩展字段配置
extend-fields:
eventFlag: true
# 测试命名
zfEventDocTableValueQuote: true
zfEventDocConstantValueQuote: true

方式一:通过 ConfigurationProperties

示例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.jeecg.modules.event.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


/**
* 加载项目配置
*/
@Data
@Component("eventConfig")
@ConfigurationProperties(prefix = "event")
public class EventConfig {
// 综合执法队机构编码
private String departZhifaduiOrgcode = "默认值";

// 事件扩展字段
private Map<String, Boolean> extendFields;

// 测试命名
private boolean zfEventDocTableValueQuote = false; // 执法文书表单字段引用功能是否开启
private boolean zfEventDocConstantValueQuote = false; // 执法文书表单常量引用功能是否开启
}

使用

1
2
@Resource
private EventConfig eventConfig;

示例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Data
@Component
@ConfigurationProperties(prefix = "wx.mp")
public class WxMpProperties {
/**
* 是否使用redis存储access token
*/
private boolean useRedis;

/**
* redis 配置
*/
private RedisConfig redisConfig;

@Data
public static class RedisConfig {
/**
* redis服务器 主机地址
*/
private String host;

/**
* redis服务器 端口号
*/
private Integer port;

/**
* redis服务器 密码
*/
private String password;
}

/**
* 多个公众号配置信息
*/
private List<MpConfig> configs;

@Data
public static class MpConfig {
/**
* 设置微信公众号的appid
*/
private String appId;

/**
* 设置微信公众号的app secret
*/
private String secret;

/**
* 设置微信公众号的token
*/
private String token;

/**
* 设置微信公众号的EncodingAESKey
*/
private String aesKey;
}

@Override
public String toString() {
return JsonUtils.toJson(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
wx:
# 微信公众号配置
mp:
useRedis: false
redisConfig:
host: tcboot-redis
port: 6379
configs: # 多个公众号请复制appId、secret、token、aesKey扩展
- appId: xxx # 第一个公众号的appid
secret: xxx # 公众号的appsecret
token: zyxx # 接口配置里的Token值
aesKey: xxx # 接口配置里的EncodingAESKey值
1
2
@Resource
private WxMpProperties wxMpProperties;

方式二:通过 @Value 注解

spring bean 定义,成员变量通过 @Value 获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Component
@PropertySource(value = "classpath:test.yml")

public class YamlProperties {
@Value("${params.name}")
private String name;

// 默认值空
@Value("${params.name:}")
private String name;

// 默认值 hello
@Value("${some.key:hello}")
private String stringWithDefaultValue;

// 默认值 true
@Value("${some.key:true}")
private Boolean booleanWithDefaultValue;

// 默认值 42
@Value("${some.key:42}")
private Integer intWithDefaultValue;

// 默认值字符串数组
@Value("${some.key:one,two,three}")
private String[] stringArrayWithDefaults;

// 默认值数值数组
@Value("${some.key:1,2,3}")
private int[] intArrayWithDefaults;
}

方式三:通过 applicationContext 获取

1
2
3
4
5
6
7
8
9
10
11
12
13
@Resource
ApplicationContext applicationContext;

@NotNull
private String getAppCode() {
return applicationContext.getEnvironment().getProperty("params.name");
}

ConfigurableApplicationContext application = SpringApplication.run(JeecgSystemApplication.class, args);
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = oConvertUtils.getString(env.getProperty("server.servlet.context-path"));

常用Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping; // 接口

Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();

for (Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethodEntry : handlerMethods.entrySet()) {
RequestMappingInfo requestMappingInfo = handlerMethodEntry.getKey();
Set<String> patterns = requestMappingInfo.getPatternsCondition().getPatterns();
// 方法完整的path,如: /goods/listGoods,/users/user/get
String path = patterns.iterator().next();

Open open = handlerMethodEntry.getValue().getMethodAnnotation(Open.class);

}




@Autowired
private Environment environment; // 环境

String serviceId = environment.getProperty("spring.application.name");

SpringBoot修改内置tomcat版本

https://blog.csdn.net/HcJsJqJSSM/article/details/116401670

容器 Tomcat 替换为 Undertow

Spring Boot内嵌容器默认为Tomcat

  1. 移除 tomcat 依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
    <exclusion>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    </exclusion>
    </exclusions>
    </dependency>
  2. 添加 Undertow 依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>
  3. 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    server:  
    port: 8084
    http2:
    enabled: true
    undertow:
    io-threads: 16
    worker-threads: 256
    buffer-size: 1024
    buffers-per-region: 1024
    direct-buffers: true

注解

@DependsOn - Bean 执行顺序

1
2
3
4
5
@Bean
// 在创建之后 eventConfig bean 之后创建当前bean
@DependsOn("eventConfig")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
}

应用启停管理

windows

启动

1
2
3
4
5
6
7
8
9
10
11
12
13
java -server -Xms1g -Xmx2g -jar xxxxx-0.0.1-SNAPSHOT.jar

#注意:jvm 参数要放在 jar 文件之前,作为 Java 启动参数,而不是程序参数

# 指定加载 lib 目录 jar 包
-Dloader.path=C:\libs

# 指定编码
-Dfile.encoding=utf-8


# 指定cmd 窗口名字
start cmd /c "title emergency && java -jar -Dfile.encoding=utf-8 C:\tongchuan\svn\emergency\jeecg-module-emergency-start\target\jeecg-module-emergency-start.jar"

停止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# windows-关闭所有 Java 进程「不推荐」
taskkill /f /im java.exe

# windows 根据端口,关闭进程 【推荐】
@echo off
setlocal enabledelayedexpansion
set port=7010
for /f "tokens=1-5" %%a in ('netstat -ano ^| find ":%port%"') do (
if "%%e%" == "" (
set pid=%%d
) else (
set pid=%%e
)
echo !pid!
taskkill /f /pid !pid!
)

# windows 根据端口关闭进程,批量
for %%a in (8848,7011) do (
set pid=0
for /f "tokens=2,5" %%b in ('netstat -ano ^| findstr ":%%a"') do (
set temp=%%b
for /f "usebackq delims=: tokens=1,2" %%i in (`set temp`) do (
if %%j==%%a (
taskkill /f /pid %%c
set pid=%%c
echo 端口号【%%a】相关进程以杀死
) else (
echo 不是本机占用端口【%%a】
)
)
)
if !pid!==0 (
echo 端口号【%%a】没有占用
)
)

# Linux 杀死指定端口的进程
kill -9 `lsof -t -i:7010`

Linux

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
APP_NAME=app.jar
usage() {
echo "Usage: sh 执行脚本.sh [start|stop|restart|status]"
exit 1
}
is_exist() {
pid=$(ps -ef | grep $APP_NAME | grep -v grep | awk '{print $2}')
if [ -z "$pid" ]; then
return 1
else
return 0
fi
}
start() {
is_exist
if [ $? -eq "0" ]; then
echo "$APP_NAME is already running. pid=$pid ."
else
nohup java -jar $APP_NAME >/dev/null 2>&1 &
fi
}
stop() {
is_exist
if [ $? -eq "0" ]; then
kill -9 $pid
else
echo "$APP_NAME is not running"
fi
}
status() {
is_exist
if [ $? -eq "0" ]; then
echo "$APP_NAME is running. Pid is $pid"
else
echo "$APP_NAME is NOT running."
fi
}
restart() {
stop
start
}
case "$1" in
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
*) usage ;;
esac

Windows 服务形式运行

目标:实现服务随系统自启动

  1. 下载Windows Service Wrapper 工具

  2. 文件重命名为 xxxxx-service.exe

  3. 同目录下新建同名 xxxxx-service.xml 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <service>
    <id>xxxxx</id>
    <name>xxxxx</name>
    <description>xxxxx</description>
    <logpath>C:\xxxxx</logpath>
    <logmode>roll</logmode>
    <depend></depend>
    <executable>C:\xxxxx\start.bat</executable>
    <stopexecutable>C:\xxxxx\stop.bat</stopexecutable>
    </service>
  4. 使用

    1. 安装

      1
      xxxxx-service.exe install
    2. 卸载

      1
      xxxxx-service.exe uninstall
    3. 启动

      1
      xxxxx-service.exe start
    4. 停止

      1
      xxxxx-service.exe stop

日志注解 @Slf4j(基于 lombok)

SpringBoot 默认集成了 logback + slf4j,所以只需添加 lombok 即可

pom.xml

1
2
3
4
5
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

application.yml

1
2
3
4
5
6
7
8
logging:
level:
com.bjtcrj.files: debug # 业务包日志输出
com.baomidou.mybatisplus: DEBUG # mybatis plus 日志输出
file:
path: ./log
name: files.log
max-history: 100

使用

1
2
3
@Slf4j

log.info("xxx");

JVM 工具远程连接

  1. 启动

    1
    java -server -Xms512m -Xmx768m -jar -Djava.rmi.server.hostname=192.168.0.110 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9000 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false files-0.0.1-SNAPSHOT.jar
  2. 连接

    image-20200617183556150

文件上传

方案一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 将调整好格式的文档导入、解析、保存
* @param type
* @param file
* @return
*/
@PostMapping(value = "/addBatch/{type}")
public R addBatch(@PathVariable("type") String type, @RequestParam("file") MultipartFile file) {
Iterable<Law> iterable = lawService.getAll();
Set<String> result = StreamSupport.stream(iterable.spliterator(), false).map(Law::getLawtype).collect(Collectors.toSet());
if(result.contains(type)) {
return R.fail(ResultCode.ERROR, "type 字段已存在");
}
return R.success(lawService.add(file, type));
}

方案二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@PostMapping("/uploadFiles")
@ResponseBody
public ResponseBean upload(FilesuploadReq entity, HttpServletRequest request) {
ResponseBean resultRes = new ResponseBean();
int code = ResponseBean.CODE_SUCCESS;
String msg = "上传成功";
String project = entity.getProject();
String module = entity.getModule();

MultipartResolver resolver = new StandardServletMultipartResolver();
MultipartHttpServletRequest mRequest = resolver.resolveMultipart(request);
Map<String, MultipartFile> fileMap = mRequest.getFileMap();

List<String> imgs = new ArrayList<>();
Collection<MultipartFile> files = fileMap.values();
for (MultipartFile file : files) {
String fileName = file.getOriginalFilename();

String tempFilePath = FileGenerator.getFilePath(project, module, fileName);
String filepathname = FileGenerator.ROOTPATH + tempFilePath;
File dest = new File(filepathname);
try {
file.transferTo(dest);

//给图片添加水印
String dateStr = FileGenerator.getDateStringYMDHMS(new Date());
PicDealTools.createWaterMarkByText(filepathname, dateStr, filepathname,0);
imgs.add(tempFilePath);
} catch (IOException e) {
code = ResponseBean.CODE_FAILED;
msg = "上传失败:"+e.getMessage();
dest.deleteOnExit();
e.printStackTrace();
} finally {
resultRes.setCode(code);
resultRes.setMsg(msg);
String join = StringUtils.join(imgs, '|');
resultRes.setData(join);
}
}

return resultRes;
}

工具类

RequestContextHolder - 获取 request

1
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

获取所有接口信息:类名、方法名、接口路径、请求Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
// 获取url与类和方法的对应信息
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();

List<Map<String, String>> list = new ArrayList<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> m : map.entrySet()) {
Map<String, String> map1 = new HashMap<>();
RequestMappingInfo info = m.getKey();
HandlerMethod method = m.getValue();
PatternsRequestCondition p = info.getPatternsCondition();
for (String url : p.getPatterns()) {
map1.put("url", url);
}
map1.put("className", method.getMethod().getDeclaringClass().getName()); // 类名
map1.put("method", method.getMethod().getName()); // 方法名
RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
for (RequestMethod requestMethod : methodsCondition.getMethods()) {
map1.put("type", requestMethod.toString());
}

// 在控制台打印,方便粘贴到 Excel 中,不需要可以关掉。
System.out.println(String.format("%s\t%s\t%s\t%s",
map1.get("url"), map1.get("className"), map1.get("method"), map1.get("type")));

}

问题

HTTP method names must be tokens

解决:检查请求协议类型是否为 https,也许应该是 http

The request is larger than the server is willing or able to process

1
2
3
spring:
codec:
max-in-memory-size: 2MB