Springboot 工具相关

启动

image-20211108101012797

image-20211108101044170

POM文件:

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
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>

<mysql-connector-java.version>5.1.41</mysql-connector-java.version>
<mybatis-spring-boot-starter.version>2.5.4</mybatis-spring-boot-starter.version>
<spring-boot-starter.version>2.5.4</spring-boot-starter.version>
<mapper-spring-boot-starter.version>2.1.5</mapper-spring-boot-starter.version>
<pagehelper-spring-boot-starter.version>1.2.12</pagehelper-spring-boot-starter.version>

<okhttp.version>4.2.2</okhttp.version>
<jackson.version>2.10.2</jackson.version>

<commons-codec.version>1.11</commons-codec.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons-fileupload.version>1.4</commons-fileupload.version>
<google-guava.version>28.2-jre</google-guava.version>

<slf4j.version>1.7.21</slf4j.version>
<joda-time.version>2.10.6</joda-time.version>

</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot-starter.version}</version>
</dependency>
</dependencies>

Application

1
2
3
4
5
6
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Controller

1
2
3
4
5
6
7
8
@RestController
public class HelloController {

@GetMapping("hello")
public Object hello() {
return "hello";
}
}

Mybatis逆向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>1.2.4</version>
</dependency>
<!-- mybatis 逆向生成工具 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
1
2
public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {
}
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
public class GeneratorDisplay {

public void generator() throws Exception {

List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定 逆向工程配置文件
// File configFile = new File("mybatis_reverse"
// + File.separator
// +"generatorConfig.xml");
File configFile = new File("E:\\project\\mybatis_reverse\\generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);

}

public static void main(String[] args) throws Exception {
try {
GeneratorDisplay generatorSqlmap = new GeneratorDisplay();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}

}
}

image-20211109124140879

最外层 genenratorConfig.xml

image-20211109003800094

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
<?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>
<context id="MysqlContext" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>

<!-- 通用mapper所在目录 -->
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.imooc.my.mapper.MyMapper"/>
</plugin>

<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/imooc-red-book-dev"
userId="root"
password="123456">
</jdbcConnection>

<!-- 对应生成的pojo所在包 -->
<javaModelGenerator targetPackage="com.imooc.pojo" targetProject="src/main/java"/>

<!-- 对应生成的mapper所在目录 -->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/>

<!-- 配置mapper对应的java映射 -->
<javaClientGenerator targetPackage="com.imooc.mapper" targetProject="src/main/java" type="XMLMAPPER"/>

<!-- 数据库表 -->
<table tableName="comment"></table>
<table tableName="fans"></table>
<table tableName="my_liked_vlog"></table>
<table tableName="users"></table>
<table tableName="vlog"></table>

</context>
</generatorConfiguration>

整合配置mybatis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- 阿里开源数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>

application.properties

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
spring:
datasource: # 数据源的相关配置
type: com.zaxxer.hikari.HikariDataSource # 数据源的类型,可以更改为其他的数据源配置,比如druid
driver-class-name: com.mysql.jdbc.Driver # mysql/MariaDB 的数据库驱动类名称
url: jdbc:mysql://192.168.0.104:3306/imooc-red-book-dev?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
username: root
password: 123456
hikari:
connection-timeout: 30000 # 等待连接池分配连接的最大时间(毫秒),超过这个时长还没有可用的连接,则会抛出SQLException
minimum-idle: 5 # 最小连接数
maximum-pool-size: 20 # 最大连接数
auto-commit: true # 自动提交
idle-timeout: 600000 # 连接超时的最大时长(毫秒),超时则会被释放(retired)
pool-name: DataSourceHikariCP # 连接池的名字
max-lifetime: 18000000 # 连接池的最大生命时长(毫秒),超时则会被释放(retired)
connection-test-query: SELECT 1
# 整合mybatis
mybatis:
type-aliases-package: com.imooc.pojo # 所有pojo类所在的包路径
mapper-locations: classpath:mapper/*.xml # mapper映射文件

# 通用mapper工具的配置
mapper:
mappers: com.imooc.my.mapper.MyMapper # 配置MyMapper,包含了一些封装好的CRUD方法
not-empty: false # 在进行数据库操作的时候,username != null 是否会追加 username != ''
identity: MYSQL

# 分页插件助手的配置
pagehelper:
helper-dialect: MYSQL
support-methods-arguments: true
1
2
3
4
5
6
7
@SpringBootApplication
@MapperScan(basePackages = "com.imooc.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Swagger

1
2
3
4
5
6
7
8
9
<!-- knife4j 接口文档工具 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-springdoc-ui</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfig {

@Bean
public Docket defaultApi2() {
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
//.title("swagger-bootstrap-ui-demo RESTful APIs")
.description("慕红薯短视频实战接口文档")
.termsOfServiceUrl("http://www.xx.com/")
.contact(new Contact("lee", "http://www.imooc.com/", "abc@imooc.com"))
.version("1.0")
.build())
//分组名称
.groupName("2.X版本")
.select()
//这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("com.imooc.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
}
1
2
3
4
5
6
7
8
9
@Slf4j
@Api(tags = "Hello 测试的接口")
@RestController
public class HelloController {

@ApiOperation(value = "hello - 这是一个hello的测试路由")
public Object hello() {
}
}

腾讯云短信

1
2
3
4
5
6
<!-- 第三方云厂商相关的依赖 -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.270</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
@Component
@Data
@PropertySource("classpath:tencentcloud.properties")
@ConfigurationProperties(prefix = "tencent.cloud")
public class TencentCloudProperties {

private String secretId;
private String secretKey;

}
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
@Component
public class SMSUtils {
@Autowired
private TencentCloudProperties tencentCloudProperties;

public void sendSMS(String phone, String code) throws Exception {
try {
/* 必要步骤:
* 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。
* 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
* 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,
* 以免泄露密钥对危及你的财产安全。
* CAM密匙查询获取: https://console.cloud.tencent.com/cam/capi*/
Credential cred = new Credential(tencentCloudProperties.getSecretId(),
tencentCloudProperties.getSecretKey());

// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();

// httpProfile.setReqMethod("POST"); // 默认使用POST

/* SDK会自动指定域名。通常是不需要特地指定域名的,但是如果你访问的是金融区的服务
* 则必须手动指定域名,例如sms的上海金融区域名: sms.ap-shanghai-fsi.tencentcloudapi.com */
httpProfile.setEndpoint("sms.tencentcloudapi.com");

// 实例化一个client选项
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
SmsClient client = new SmsClient(cred, "ap-nanjing", clientProfile);

// 实例化一个请求对象,每个接口都会对应一个request对象
SendSmsRequest req = new SendSmsRequest();
String[] phoneNumberSet1 = {"+86" + phone};//电话号码
req.setPhoneNumberSet(phoneNumberSet1);
req.setSmsSdkAppId("1400568450"); // 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId
req.setSignName("风间影月"); // 签名
req.setTemplateId("1108902"); // 模板id:必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看

/* 模板参数(自定义占位变量): 若无模板参数,则设置为空 */
String[] templateParamSet1 = {code};
req.setTemplateParamSet(templateParamSet1);

// 返回的resp是一个SendSmsResponse的实例,与请求对象对应
SendSmsResponse resp = client.SendSms(req);
// 输出json格式的字符串回包
// System.out.println(SendSmsResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
System.out.println(e.toString());
}
}

// public static void main(String[] args) {
// try {
// new SMSUtils().sendSMS("18812345612", "7896");
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
}
1
2
3
4
5
6
7
8
9
10
11
@Autowired
private SMSUtils smsUtils;

@GetMapping("sms")
public Object sms() throws Exception {

String code = "123456";
smsUtils.sendSMS("", code);

return GraceJSONResult.ok();
}

redis

工具类

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
<!-- apache 工具类 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>

<!-- google 工具类 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${google-guava.version}</version>
</dependency>

<!-- joda-time 时间工具 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${joda-time.version}</version>
</dependency>

问题:

启动redis服务,可以使用127.0.0.1配置并使用访问redis,但是换成IP地址就无法访问 

解决:

1. 打开 redis.windows.config文件(linux对应redis.conf文件)将 NETWORK 下 bind 127.0.0.1 注释掉

    并将 protected-mode  yes    改为 protected-mode  no;

2. 同理修改 redis.windows.server.config 文件中相应内容;

3. 重启 Redis 服务,即可使用 IP 访问 Redis了;
1
2
3
4
5
<!-- 引入 redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

拦截器

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
@Slf4j
public class PassportInterceptor extends BaseInfoProperties implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {

// 获得用户的ip
String userIp = IPUtil.getRequestIp(request);

// 得到是否存在的判断
boolean keyIsExist = redis.keyIsExist(MOBILE_SMSCODE + ":" + userIp);

if (keyIsExist) {
GraceException.display(ResponseStatusEnum.SMS_NEED_WAIT_ERROR);
log.info("短信发送频率太大!");
return false;
}

/**
* true: 请求放行
* false: 请求拦截
*/
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

@Bean
public PassportInterceptor passportInterceptor() {
return new PassportInterceptor();
}

@Bean
public UserTokenInterceptor userTokenInterceptor() {
return new UserTokenInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(passportInterceptor())
.addPathPatterns("/passport/getSMSCode");

}
}

异常封装

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
public class GraceException {

public static void display(ResponseStatusEnum responseStatusEnum) {
throw new MyCustomException(responseStatusEnum);
}

}
public class MyCustomException extends RuntimeException {

private ResponseStatusEnum responseStatusEnum;

public MyCustomException(ResponseStatusEnum responseStatusEnum) {
super("异常状态码为:" + responseStatusEnum.status()
+ ";具体异常信息为:" + responseStatusEnum.msg());
this.responseStatusEnum = responseStatusEnum;
}

public ResponseStatusEnum getResponseStatusEnum() {
return responseStatusEnum;
}

public void setResponseStatusEnum(ResponseStatusEnum responseStatusEnum) {
this.responseStatusEnum = responseStatusEnum;
}
}
/**
* 统一异常拦截处理
* 可以针对异常的类型进行捕获,然后返回json信息到前端
*/
@ControllerAdvice
public class GraceExceptionHandler {

@ExceptionHandler(MyCustomException.class)
@ResponseBody
public GraceJSONResult returnMyException(MyCustomException e) {
e.printStackTrace();
return GraceJSONResult.exception(e.getResponseStatusEnum());
}
}