0%

一.简介

Bind mounts and named volumes are the two main types of volumes that come with the Docker engine.

Named Volumes Bind Mounts
Host Location Docker chooses You control
Mount Example (using -v) my-volume:/usr/local/data /path/to/data:/usr/local/data
Populates new volume with container contents Yes No
Supports Volume Drivers Yes No
二.示例
1
2
3
4
5
6
7
8
9
创建镜像
docker build -t myupi .
挂载volume //容器内的文件夹不存在,docker会自动创建
docker run -d -p 12000:12000 --name upi -v /datatest/file:/filetest myupi

进入容器
docker exec -it 71d bash


/datatest/file和/filetest两个目录内的文件共享,其中一个改变另一个跟着改变(如果理解挂载的话,这句话就是废话)

三.docker -p
1
-p 8080:80	Bind host’s port 8080 to container’s TCP port 80

参考:https://docs.docker.com/get-started/06_bind_mounts/

一.yml
1
2
3
4
5
spring:
servlet:
multipart:
max-file-size: 2MB
max-request-size: 2MB
二.upload.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title>文件上传页面</title>
</head>
<body>
<h1>文件上传页面</h1>
<form method="post" action="/upload" enctype="multipart/form-data">
选择要上传的文件:<input type="file" name="file"><br>
<hr>
<input type="submit" value="提交">
</form>
</body>
</html>
三.UploadController
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

@Controller
@Slf4j
public class UploadController {

private String path="D:\\IDEA\\filetest\\";

@GetMapping("/")
public String uploadPage() {
return "upload.html";
}

@PostMapping("/upload")
@ResponseBody
public String create(@RequestPart MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
// System.out.println(fileName);
String filePath = path + fileName;

File dest = new File(filePath);
Files.copy(file.getInputStream(), dest.toPath());
return "Upload file success : " + dest.getAbsolutePath();
}

}
四.@RequestPart

@RequestPart类似于@RequestParam

参考:https://www.baeldung.com/sprint-boot-multipart-requests

https://segmentfault.com/a/1190000038864532

https://segmentfault.com/a/1190000038985065?utm_source=sf-similar-article

当length总是2的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。

所以,为什么h& (length-1)运算等价于对length取模?

实现的原理如下:

X % 2^n = X & (2^n - 1)

假设n为3,则2^3 = 8,表示成2进制就是1000。2^3 -1 = 7 ,即0111。

此时X & (2^3 - 1) 就相当于取X的2进制的最后三位数。

位与( & )
1
2
3
4
5
public class Test {
public static void main(String[] args) {
System.out.println(5 & 3);//结果为1
}
}

5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101

3转换为二进制:0000 0000 0000 0000 0000 0000 0000 0011


1转换为二进制:0000 0000 0000 0000 0000 0000 0000 0001

位与:第一个操作数的第n位与第二个操作数的第n位如果都是1,那么结果的第n位也为1,否则为0

参考:https://www.jianshu.com/p/e910ff6a5579

https://blog.csdn.net/xiaochunyong/article/details/7748713

临界区 Critical Section

  • 一个程序运行多个线程本身是没有问题的

  • 问题出在多个线程访问共享资源

    • 多个线程读共享资源其实也没有问题

    • 在多个线程对共享资源读写操作时发生指令交错,就会出现问题

  • 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区

例如,下面代码中的临界区

1
2
3
4
5
6
7
8
9
10
11
static int counter = 0;
static void increment()
// 临界区
{
counter++;
}
static void decrement()
// 临界区
{
counter--;
}

竞态条件 Race Condition

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

为什么需要 join

下面的代码执行,打印 r 是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static int r = 0;
public static void main(String[] args) throws InterruptedException {
test1();
}
private static void test1() throws InterruptedException {
log.debug("开始");
Thread t1 = new Thread(() -> {
log.debug("开始");
sleep(1);
log.debug("结束");
r = 10;
});
t1.start();
log.debug("结果为:{}", r);
log.debug("结束");
}

分析

  • 因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r=10

  • 而主线程一开始就要打印 r 的结果,所以只能打印出 r=0

解决方法

  • 用 join,加在 t1.start() 之后即可

哪个线程对象调用join,就等待哪个线程运行结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static int r = 0;
public static void main(String[] args) throws InterruptedException {
test1();
}
private static void test1() throws InterruptedException {
log.debug("开始");
Thread t1 = new Thread(() -> {
log.debug("开始");
sleep(1);
log.debug("结束");
r = 10;
});
t1.start();
t1.join();
log.debug("结果为:{}", r);
log.debug("结束");
}

t1.join()加在 t1.start() 之后,表示主线程等待t1线程运行结束

对于二叉树来说,如果确定了中序遍历以后,给出任意一种其他的遍历序列,都可以得到唯一的一棵二叉树。 在只给出前序遍历和后序遍历的时候,无法确定唯一二叉树。

Around advice is declared by annotating a method with the @Around annotation. The method should declare Object as its return type, and the first parameter of the method must be of type ProceedingJoinPoint. Within the body of the advice method, you must invoke proceed() on the ProceedingJoinPoint in order for the underlying method to run. Invoking proceed() without arguments will result in the caller’s original arguments being supplied to the underlying method when it is invoked. For advanced use cases, there is an overloaded variant of the proceed() method which accepts an array of arguments (Object[]). The values in the array will be used as the arguments to the underlying method when it is invoked.

The value returned by the around advice is the return value seen by the caller of the method. For example, a simple caching aspect could return a value from a cache if it has one or invoke proceed() (and return that value) if it does not. Note that proceed may be invoked once, many times, or not at all within the body of the around advice. All of these are legal.

If you declare the return type of your around advice method as void, null will always be returned to the caller, effectively ignoring the result of any invocation of proceed(). It is therefore recommended that an around advice method declare a return type of Object. The advice method should typically return the value returned from an invocation of proceed(), even if the underlying method has a void return type. However, the advice may optionally return a cached value, a wrapped value, or some other value depending on the use case.

参考:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop

一、实现方式

有时我们需要对类按照类中的某一个属性(或者多个属性)来对类的对象进行排序,有两种方法可以实现,一种方法是类实现Comparable接口,然后调用Collections.sort(List)方法进行排序,另一种方法是类不实现Comparable接口,而在排序时使用Collections.sort(List, Comparator)方法,并实现其中的Comparator接口。

二、用第二种方法配合Lambda 表达式快速生成排序方式

1
2
3
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
1
2
PriorityQueue<Integer> queMin;
queMin = new PriorityQueue<Integer>((a, b) -> (b - a));

三、关于Lambda表达式

语法

lambda 表达式的语法格式如下:

1
2
3
(parameters) -> expression 

(parameters) ->{ statements; }
以下是lambda表达式的重要特征:
  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
Lambda 表达式实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 不需要参数,返回值为 5  
() -> 5

// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x

// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y

// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y

// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

参考:https://www.cnblogs.com/sench/p/8889435.html

https://www.runoob.com/java/java8-lambda-expressions.html

堆的两个特性

结构性:用数组表示的完全二叉树;

有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)

​ “最大堆(MaxHeap)”,也称“大顶堆”:最大值

​ “最小堆(MinHeap)”,也称“小顶堆”:最小值

在使用Spring Security时,添加了验证码功能。但要对验证码进行验证,需要在过滤器中认证。

一、自定义过滤器类
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
package com.yuanli.campustradingplatform.config;

import com.yuanli.campustradingplatform.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
* @author YuanLi
* @version 1.0
* @date 2022/3/26 10:20
*/
@Component
public class VerificationCodeFilter extends GenericFilterBean {
private String filterUrl="/mylogin";

private String filterMethod="POST";

@Autowired
RedisUtil redisUtil;

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String method = request.getMethod();
String servletPath = request.getServletPath();
if( filterMethod.equals(method) && filterUrl.equals(servletPath) ){
//验证码验证逻辑
String captcha = request.getParameter("captcha");
if( captcha.equals("") || captcha==null){
throw new AuthenticationServiceException("验证码不能为空!");
}
String captchaKey = request.getParameter("captchaKey");
String redisCaptcha = (String) redisUtil.get(captchaKey);
boolean result = captcha.equalsIgnoreCase(redisCaptcha);
if(!result){
throw new AuthenticationServiceException("验证码错误!");
}

}
filterChain.doFilter(servletRequest,servletResponse);
}
}

二、在SecurityConfig中添加配置

1
http.addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class);

参考:https://www.baeldung.com/spring-security-custom-filter

https://www.javadevjournal.com/spring-security/custom-filter-in-spring-security/

http://www.javaboy.org/2020/0303/springsecurity-verifycode.html