SpringBoot3(一)

Springboot3后端知识点汇总

一、SpringBoot依赖以及用法

1、springboot核心起步依赖

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

2、spring-boot-starter-web起步依赖

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

3、mybatis

1
2
3
4
5
6
7
8
9
10
11
	<!--    mybatis起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- mysql驱动依赖-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>

修改application.yaml

1
2
3
4
5
6
7
#配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis
username: root
password: 2003917

使用方法:

​ 创建mapper接口,写上@mapper注解

  1. 定义方法在方法上写出要调用增删改查哪一种方法,并且在里面写SQL语句

    1
    2
    3
    4
    5
    6
    7
    8
    @Mapper
    public interface ArticleMapper {
    @Select("select * from article where id=#{id}")
    Article detail(Integer id);

    //需要创建映射配置文件来写 动态sql
    List<Article> list(Integer userId, Integer categoryId, String state);
    }
  2. 或者是使用动态sql

    在resources文件夹下创建与mapper接口同路径同名的xml文件

    1

具体格式与写法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.itheima.mapper.ArticleMapper">
<!-- 动态sql-->
<select id="list" resultType="org.itheima.pojo.Article">
select * from article
<where>
<if test="categoryId!=null">
category_id = #{categoryId}
</if>

<if test="state!=null">
and state = #{state}
</if>

and create_user = #{userId}
</where>
</select>
</mapper>
  1. 开启驼峰命名

1
2
3
4
mybatis:
configuration:
#开启驼峰命名
map-underscore-to-camel-case: true

4、pageHelper

导入依赖

1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>

使用步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean <T>{
private Long total;//总条数
private List<T> items;//当前页数据集合
}
//1.创建pageBean对象 封装查询好的数据
PageBean<Article> pb = new PageBean<>();

//2.开启分页查询 PageHelper 导入对应坐标
PageHelper.startPage(pageNum,pageSize);

//3、查询数据库获得为分页的结果
List<Article> as = articleMapper.list(userId,categoryId,state);

//4、将结果交给PageHelper的自带类Page处理
Page<Article> p = (Page<Article>) as;

//5、取出并使用结果
pb.setTotal(p.getTotal());
pb.setItems(p.getResult());

5、Redis

1
2
3
4
5
<!--    redis坐标-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.yaml中:

1
2
3
4
5
6
spring:
data:
redis:
host: localhost
port: 6379
password: root@123456

Java中Redis的set和get的基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Autowired
private StringRedisTemplate stringRedisTemplate;

public void testSet(){
//往redis中存储一个键值对
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.set("username","zhangsan");
//设置过期时间
operations.set("id","1",15, TimeUnit.SECONDS);
}

public void testGet(){
//从redis中获取一个键值对
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
String username = operations.get("username");
System.out.println(username);
}

StringRedisTemplate 这个名字清晰地表达了它的功能:这是一个用于操作 Redis 字符串类型数据的模板。继承自 RedisTemplate 并加以简化,它专注于字符串数据的处理,使得开发者能够更方便、快速地进行 Redis 字符串类型操作。

二、application.yaml配置文件修改

1、修改端口和访问根路径

1
2
3
4
server:
port: 9191
servlet:
context-path:/start2

2、配置信息的读取

@Value(“${键名}”)

在application.yaml文件中写下

1
2
3
email:
user: 2636939357@qq.com
code: 123

java代码中写下

1
2
3
4
5
6
7
8
@Component
public class EmailProperties{
@Value("${email.user}")
public String user;

@Value("${email.code}")
public String code;
}

@ConfigurationProperties(prefix=“前缀”)

1
2
3
4
5
6
7
@ConfigurationProperties(prefix=“email”)
@Component
public class EmailProperties{
//会自动注入
public String user;
public String code;
}

使用时自动将对应的配置类自动注入即可

1
2
3
4
5
6
private final EmailProperties emailProperties;

@Autowired
public ServerController(EmailProperties emailProperties) {
this.emailProperties = emailProperties;
}

三、SpringBoot注解

1、Bean扫描

@ComponentScan(basePackages = “com.xinzhe”)

SpringBoot默认扫描启动类所在的包及其子包

2、注册生效条件的注解 @Conditional

@condition注解有很多用法,具体需要自己去试,这里不做展开

注解 说明
@ConditionalOnProperty 配置文件中存在对应的属性,才声明该bean
@ConditionalOnMissingBean 当不存在当前类型的bean时,才声明该bean
@ConditionalOnClass 当前环境存在指定的这个类时,才声明该bean

3、Spring Validation参数校验

①对传入的参数进行合法性校验@

  1. 在对应的controller类上加入@Validated注解

  2. 对应的接口加上@Pattern(regexp = “^\S{5,16}$”) regexp是一个正则表达式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @PostMapping("/register")
    public Result register(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$")String password){
    //查询用户
    User user = userService.findByUserName(username);
    if(user == null){
    //用户名没有占用
    //注册
    userService.register(username,password);
    return Result.success();
    }else{
    //被占用
    return Result.error("用户名已被占用");
    }

    //注册
    }
  3. 可以搭配全局异常处理使用

②可以在接收的java对象前加上@Validated以及在对象的私有字段中加上以下注解等等

注解 说明
@NotNull 值不能为null
@NotEmpty 值不能为null,且不能为空
@Email 满足邮箱格式
@Pattern(regexp=“正则”) 匹配正则表达式
@URL 满足链接格式

1

1

③分组校验。我们可以把检验项进行分组,在完成不同功能的时候,用不同的分组

  1. 在@Validated中指定自己的分组

    1

  2. 在对象中定义检验分组,以及在参数校验条件中指定生效的分组

    1

定义校验项时如果没有指定分组,则属于Default分组,分组可以继承

④自定义参数验证注解

  1. 自定义注解State

    选择好提供检验规则的类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //元注解
    @Documented
    //标注元注解可以用在哪个地方
    @Target({ElementType.FIELD})
    //保留在哪个时候,元注解
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(
    validatedBy = {stateValidation.class}//制定提供校验规则的类
    )
    public @interface State {

    //提供校验失败后的提示信息
    String message() default "state参数的值只能是已发布或者草稿";

    //指定分组
    Class<?>[] groups() default {};
    //负载 获取到State注解的附加信息
    Class<? extends Payload>[] payload() default {};
    }
  2. 提供校验规则

    写好具体的校验规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class stateValidation implements ConstraintValidator</*给那个注解提供校验规则 */State,String> {
    @Override
    /**
    * @param value 将来要校验的数据
    * @param context
    * @return 如果返回false 则校验不通过 如果返回true 则校验不通过
    */
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
    //提供校验规则
    if(value == null){
    return false;
    }

    if(value.equals("已发布") || value.equals("草稿")){
    return true;
    }
    return false;
    }
    }
  3. 在需要的地方使用自定义注解

    1

4、GlobalExceptionHandler全局异常处理

@RestControllerAdvice

1
2
3
4
5
6
7
8
9
10
//全局异常处理器处理参数校验失败异常。
@RestControllerAdvice
public class GlobalExceptionHandler {
//此方法会接收所有异常类。然后统一返回Result.error()
@ExceptionHandler(Exception.class)
public Result handleException(Exception e){
e.printStackTrace();
return Result.error(StringUtils.hasLength(e.getMessage())?e.getMessage():"操作失败");
}
}

自定义错误类

1、定义通用错误信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public enum CommonError {

UNKOWN_ERROR("执行过程异常,请重试。"),
PARAMS_ERROR("非法参数"),
OBJECT_NULL("对象为空"),
QUERY_NULL("查询结果为空"),
REQUEST_NULL("请求参数为空");

private String errMessage;

public String getErrMessage() {
return errMessage;
}

private CommonError( String errMessage) {
this.errMessage = errMessage;
}

}

2、自定义异常类型

异常类型名可以自定义,使用的时候用

new XueChengPlusException(“错误信息”)

new XueChengPlusException().cast(“错误信息”)

new XueChengPlusException().cast(CommonError.UNKOWN_ERROR.getErrMessage())

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
public class XueChengPlusException extends RuntimeException {

private String errMessage;

public XueChengPlusException() {
super();
}

public XueChengPlusException(String errMessage) {
super(errMessage);
this.errMessage = errMessage;
}

public String getErrMessage() {
return errMessage;
}

public static void cast(CommonError commonError){
throw new XueChengPlusException(commonError.getErrMessage());
}
public static void cast(String errMessage){
throw new XueChengPlusException(errMessage);
}

}

5、自动配置原理

  1. 进入主类的SpringBootApplication注解

    1

  2. 发现我们的启动类其实也是一个配置类,并且有@EnableAutoConfiguration表示开启自动配置

    1

  3. 再次点开,其实它导入了一个AutoConfigurationImportSelector.class

    1

  4. AutoConfigurationImportSelector.class实现了selectImports方法

    1

  5. 这个方法最终会读取META-INF 目录下的 后缀名 为imorts的文件,当然了,boot2.7以前的版本,读取的是spring.factories文件,读取到全类名了之后,会解析注册条件,也就是@Conditional及其衍生注解,把满足注册条件的Bean对象自动注入到IOC容器中,

    1

6、自定义start

  1. 首先我们要创建2个工程,autoconfigure工程用于配置需要自动导入的Bean starter工程用于依赖管理

    1

  2. 在autoconfigure下创建好需要的自动配置类

    类上方加入@AutoConfiguration注解代表自动注入

    在类中要写需要自动配置成@bean的类,那么当然对应需要的依赖要在pom文件中引入

    1

  3. 在META-INF文件夹下创建.import文件org.springframework.boot.autoconfigure.AutoConfiguration.imports

    在里面指定自动配置类名

    1

  4. 在start项目pom文件中引入自动配置依赖,还要引入全部用到依赖

    1

  5. 若报错不支持的发布版本

    1

    则需要在2个pom文件中都加上下列代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
    <configuration>
    <source>17</source>
    <target>17</target>
    </configuration>
    </plugin>
    </plugins>
    </build>

7、自动获取Header中的信息

@RequestHeader(name=“Authorization”)

1
2
3
4
5
6
7
8
9
10
@GetMapping("/userInfo") //获取用户信息
public Result<User> userInfo(/*@RequestHeader(name="Authorization") String token*/){
//根据用户名查询用户

Map<String,Object> map = JwtUtil.parseToken(token);
String username = (String) map.get("username");
User user = userService.findByUserName(username);

return Result.success(user);
}

8、@RequestBody

自动将前端传来的js对象转换成Java对象

通常是这样的js对象

1
2
3
4
const userInfoData = {
username: "newUsername",
email: "newemail@example.com"
};

1

9、@RequestParam

自动将前端传来的url中的param接收转换成我们需要的对象

通常是这样的URL

1
https://example.com/api?qq=2636939357

1

10、@PathVariable

自动识别路径参数,自动注入

1
2
3
4
5
public Result startOrStop(@PathVariable Integer status,Long id){
log.info("启用禁用员工账号:{},{}",status,id);
employeeService.startOrStop(status,id);//后绪步骤定义
return Result.success();
}

四、JWT登录验证

JWT(JSON Web Token)通常用于身份验证和信息交换。分别是头部(Header)、有效载荷(Payload)和签名(Signature)组成

1、结构分析

  1. 头部

  • 算法类型alg):表示签名所使用的算法,常见的有HS256(HMAC SHA256)、RS256(RSA SHA256)等。

  • 令牌类型typ):通常是JWT,表示这是一个JWT令牌。

示例

1
2
3
4
json{
"alg": "HS256",
"typ": "JWT"
}
  1. 有效载荷

  • 注册声明(Registered Claims):这些是JWT预定义的标准声明,如iss(发行者)、exp(过期时间)、sub(主题)、aud(受众)等。

  • 公共声明(Public Claims):这些是可由用户定义的声明,但要避免与注册声明冲突,通常使用自己命名的标准。

  • 私有声明(Private Claims):这些是自定义声明,用于双方之间交换信息,但没有预定义标准。

示例

1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
  1. 签名

  • 使用头部中指定的算法(alg)和密钥(通常是服务器私钥或共享密钥)对头部和有效载荷进行签名。

  • 签名的生成过程是:将编码后的头部和有效载荷拼接成一个字符串,再通过加密算法生成签名。

假设使用HMAC SHA256算法,签名的生成公式为:

1
2
3
4
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

签名部分确保JWT未被篡改,因为任何微小的修改都会导致签名无效。

一个完整的JWT令牌看起来可能如下:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • 第一部分是经过Base64Url编码的头部:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

  • 第二部分是经过Base64Url编码的有效载荷:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

  • 第三部分是签名:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

2、JWT-生成与验证

  1. 引入依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.4.0</version>
    </dependency>
  2. JWT生成

    1
    2
    3
    4
    5
    6
    7
    8
    9
    private static final String KEY = "itheima";

    //接收业务数据,生成token并返回
    public static String genToken(Map<String, Object> claims) {
    return JWT.create()
    .withClaim("claims", claims) //载荷
    .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12)) //过期时间
    .sign(Algorithm.HMAC256(KEY)); //加密算法与加密秘钥
    }
  3. JWT验证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private static final String KEY = "itheima";

    //接收token,验证token,并返回业务数据
    public static Map<String, Object> parseToken(String token) {
    return JWT.require(Algorithm.HMAC256(KEY))
    .build() //创建验证器
    .verify(token) //验证toker
    .getClaim("claims")//获取负载
    .asMap(); //将负载生成map
    }

    一般来说会将上面的方法整合成一个工具类JwtUtil

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class JwtUtil {

    private static final String KEY = "itheima";

    //接收业务数据,生成token并返回
    public static String genToken(Map<String, Object> claims) {
    return JWT.create()
    .withClaim("claims", claims) //载荷
    .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12)) //过期时间
    .sign(Algorithm.HMAC256(KEY)); //加密算法与加密秘钥
    }

    //接收token,验证token,并返回业务数据
    public static Map<String, Object> parseToken(String token) {
    return JWT.require(Algorithm.HMAC256(KEY))
    .build() //创建验证器
    .verify(token) //验证toker
    .getClaim("claims")//获取负载
    .asMap(); //将负载生成map
    }

    }

3、HandlerInterceptor

配合HandlerInterceptor接口实现自动对客户进行身份验证

这里面还使用到了:

  1. ThreadLocal存储业务数据

  2. 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
31
32
33
34
35
36
37
38
39
40
41
@Component
public class LoginInterceptor implements HandlerInterceptor {

@Autowired
private StringRedisTemplate stringRedisTemplate;

//对请求处理之前进行处理
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//令牌验证
String token = request.getHeader("Authorization");
try{
//从redis中获取相同token
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
String redisToken = operations.get(token);

if(redisToken == null){
//token已经失效了
throw new RuntimeException();
}

Map<String,Object> claims = JwtUtil.parseToken(token);
ThreadLocalUtil.set(claims);
//把业务数据存储到ThreadLocal中

return true;
//放行
}catch (Exception e){
response.setStatus(401);
//不放行
return false;
}
}

//对请求处理之后处理
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//清空ThreadLocal中的数据
ThreadLocalUtil.remove();
}
}

添加拦截器需要配置WebConfig类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//mvc配置类。配置拦截器等信息
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Autowired
LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//登录接口和注册接口不拦截
//添加拦截器
registry.addInterceptor(loginInterceptor)
.excludePathPatterns("/user/login","/user/register"); //放行
}
}

五、ThreadLocal

提供线程访问内都可访问的局部变量,只有在线程内才能获取到对应的值,线程外则不能访问。

  1. 用来存取数据: set()/get()

  2. 使用ThreadLocal存储的数据, 线程安全

  3. 用完记得调用remove方法释放

1、封装的工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BaseContext {

public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

public static void setCurrentId(Long id) {
threadLocal.set(id);
}

public static Long getCurrentId() {
return threadLocal.get();
}

public static void removeCurrentId() {
threadLocal.remove();
}

}

六、文件上传处理

1、前端通常的代码

头像:
1
2
3
4
<form action="/upload" method="post" enctype="multipart/form-data">
头像: <input type="file" name="image"><br>
<input type="submit" value="提交">
</form>

2、后端方法定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
*
* @param file 接收文件
* @return
* @throws Exception
*/
@PostMapping("/upload")
public Result<String> upload(MultipartFile file) throws Exception {
//得到接收到的临时路径
String originalFilename = file.getOriginalFilename();
//雪花算法生成文件名
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//调用阿里云的oss对象存储服务
String url = AliOssUtil.uploadFile(fileName,file.getInputStream());
return Result.success(url);
}

阿里云oss上传代码模版

引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--    阿里云oss依赖坐标-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.17.4</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</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
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
public class AliOssUtil {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
private static String ENDPOINT = "https://oss-cn-hangzhou.aliyuncs.com";
// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
// EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

//填写访问秘钥
private static String ACCESS_KEY_ID="你的ACCESS_KEY_ID";

private static String ACCESS_KEY_SECRET="你的ACCESS_KEY_SECRET";
// 填写Bucket名称,例如examplebucket。
private static String BUCKET_NAME = "你的BucketName";


public static String uploadFile(String objectName, InputStream in) throws Exception {
String url ="";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。

// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID,ACCESS_KEY_SECRET);

try {
// 填写字符串。
String content = "Hello OSS,你好世界";

// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, objectName, in);
// 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
// ObjectMetadata metadata = new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// metadata.setObjectAcl(CannedAccessControlList.Private);
// putObjectRequest.setMetadata(metadata);

// 上传字符串。
PutObjectResult result = ossClient.putObject(putObjectRequest);

//url组成 https://bucket名称.区域节点/objectName
url = "https://"+BUCKET_NAME+"."+ENDPOINT.substring(ENDPOINT.lastIndexOf("/")+1)+"/"+objectName;
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}


return url;
}
}

七、Redis

1、使用Redis进行的登录优化

当我们没有使用Redis的时候,前端直接拿Token令牌向后端交互。一般情况下没问题

但是当我们修改密码后,之前申请的token令牌理应立马失效。 我们可以用Redis来解决这个问题

令牌主动失效机制:当用户修改密码成功后,删除redis中存储的旧令牌

1
2
3
4
5
6
//调用service完成密码更新
userService.updatePwd(newPwd);

//删除redis中存储的token
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.getOperations().delete(token);

八、SpringBoot项目部署

1、打包插件

1
2
3
4
5
6
7
8
9
10
<build>
<plugins>
<!--打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.1.3</version>
</plugin>
</plugins>
</build>
  1. 如何生成jar包?

​ 执行package命令即可

  1. 如何运行jar包?

​ Java –jar jar包位置

  1. Jar包部署对服务器有什么要求?

​ 必须有jre环境

这里不详细展开

2、属性配置方式

  1. 命令行参数

当我们想要用cmd打开一个jar包的时候

1
java -jar big-event-1.0-SNAPSHOT.jar --server.port=9999

可以在后面添加参数

然后可以在主函数的args中读取到这个参数

  1. 配置文件方式

    1
    server.port = 8081

    或者

    1
    2
    server:
    port: 8082
  2. 环境变量方式

    19

  3. 外部文件方式

    20

配置优先级,有上到下依次升高

  1. 项目中resources目录下的application.yml

  2. Jar包所在目录下的application.yml

  3. 操作系统环境变量

  4. 命令行参数

命令行参数 > 环境变量 > JAR包所在目录的配置 > 项目的 application.yml 配置

九、IDEA工具快捷用法

1、Http Client

1
2
3
4
5
6
7
8
POST /content/course/list?pageNo=2&pageSize=1
Content-Type: application/json

{
"auditStatus": "202002",
"courseName": "",
"publishStatus":""
}

十、Mapper

1、创建一个mapper接口

1
2
3
public interface CourseCategoryMapper extends BaseMapper<CourseCategory> {
public List<CourseCategoryTreeDto> selectTreeNodes(String id);
}

2、创建一个同名.xml文件

在同级下,例如CourseCategoryMapper.xml

尖括号指定方法类型 : select delete update insert

namespace指定mapper接口

id指定方法名

parameterType指定传参类型

resultType指定返回值类型

mybatis会自动转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xuecheng.content.mapper.CourseCategoryMapper">

<select id="selectTreeNodes" parameterType="string" resultType="com.xuecheng.content.model.dto.CourseCategoryTreeDto">
with recursive t1 as (
select * from course_category p where id= '1'
union all
select t.* from course_category t inner join t1 on t1.id = t.parentid
)
select * from t1 order by t1.id, t1.orderby

</select>

</mapper>

十一、常用

1、对于业务码可以定义一个字典表专门存储

21

2、解决跨域问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
public class GlobalCorsConfig {

/**
* 允许跨域调用的过滤器
*/
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
//允许白名单域名进行跨域调用
config.addAllowedOrigin("*");
//允许跨越发送cookie
config.setAllowCredentials(true);
//放行全部原始头信息
config.addAllowedHeader("*");
//允许所有请求方法跨域调用
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}