任务调度框架

学习quartz任务调度框架

介绍

Quartz 是什么?一文带你入坑 - 知乎 (zhihu.com)

其他框架 —— 3千字带你搞懂XXL-JOB任务调度平台 (baidu.com)

  • xxl job (大众点评 许雪里)
  • elasticjob (依托zookeeper)

STEP

  1. 导入maven依赖
  2. 创建自定义Job类 实现Job 在execute中进行任务编写
  3. 创建任务调度工程工厂
  4. 创建定时器(startNow() or startAt(Date date))
  5. schedule.start()
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
package com.jcDemo.quartz;

import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
*@author:gao_quansui
*@user:ASUS
*@date:2022/9/29- 14:44
*@projectName:jc_demo
*/
@Slf4j
public class TestJob implements Job {
private static intindex= 1;

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
String data = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
log.info("第{}次执行 Timed Task, current time :{}",index++, data);
}
}

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
package com.jcDemo.controller.quartz;

import com.jcDemo.quartz.TestJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
*@author:gao_quansui
*@user:ASUS
*@date:2022/9/29- 14:47
*@projectName:jc_demo
*/
@Slf4j
@RestController
public class QuartzController {

@RequestMapping("/quartz/test")
public void test() throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 定义任务调度实例, 并与TestJob绑定
JobDetail job = JobBuilder.newJob(TestJob.class)
.withIdentity("testJob", "testJobGroup")
.build();
// 定义触发器, 会马上执行一次, 接着5秒执行一次, 共十次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("testTrigger", "testTriggerGroup")
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(10, 1))
.build();
// 使用触发器调度任务的执行
scheduler.scheduleJob(job, trigger);
// 开启任务
scheduler.start();
}
}

Untitled

JPA & Java

JAP

  1. @Entity

  2. @Table

  3. @Id

  4. @Query

  5. Sort sort = new Sort(Sort.Direction.DESC, “id”); —→ findAll(sort)

  6. Pageable pageable=PageRequest.of(0,5); 第一页 一页5条

  7. extends JpaRepository<T, ID>

  8. List findByUsername(String username)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    interface PersonRepository extends Repository<User, Long> {
    // and 的查询关系
    List<User> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
    // 包含 distinct 去重,or 的 sql 语法
    List<User> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
    // 根据 lastname 字段查询忽略大小写
    List<User> findByLastnameIgnoreCase(String lastname);
    // 根据 lastname 和 firstname 查询 equal 并且忽略大小写
    List<User> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
    // 对查询结果根据 lastname 排序,正序
    List<User> findByLastnameOrderByFirstnameAsc(String lastname);
    // 对查询结果根据 lastname 排序,倒序
    List<User> findByLastnameOrderByFirstnameDesc(String lastname);
    }

![Untitled](D:\hexo warehouse\myblog\source_posts\Untitled-16690181602452.png)

link: Spring Data JPA - Reference Documentation

(16条消息) SpringDataJpa的使用 – 条件查询、排序查询、分页查询_十⑧的博客-CSDN博客_jpa 条件查询

stream

  1. list.stream().map(People::getAge)
  2. filter(i -> i > 5)
  3. count()
  4. collect(Collectors.toList()
  5. People::getAge

tree

  1. code parent_code (queryProcessTree)

    1. 找父节点
    2. 遍历寻找子节点
    3. 递归
    4. 封装数据返回
  2. queryLazyProcessTree 懒加载去除孩子节点数据(估计前端点击展开后继续往下查询)

    1. 找父节点
    2. 找所有ChildNode
    3. isleaf = ChildNode.getParentCode.equals(ParentsCode).count==0 ? false : true
    4. 封装返回
  3. recursive sql

1
2
3
4
5
6
with recursive temp(id,username,pid,pname) AS (
SELECT k.id,k.username,k.pid,k.username from t_parent_kid k WHERE k.username='B'
UNION ALL
SELECT k.id,k.username,t.id,t.username from temp t,t_parent_kid k WHERE t.id=k.pid
)
SELECT * from temp

![Untitled 1](D:\hexo warehouse\myblog\source_posts\Untitled 1-16690181860235.png)

![Untitled 2](D:\hexo warehouse\myblog\source_posts\Untitled 2-16690181910378.png)

union all / union

uniall 整合两张表 上下连接,去重

uniall all 整合两张表 上下连接,列出全部

学习并发

学习并发

  1. 创建线程的方式

    • 继承Thread类创建

      • 已经继承的类无法使用

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        public class ExtendThread extends Thread {

        }

        @Test
        void test1() {
        ExtendThread extendThread = new ExtendThread();
        log.info("threadId:{}", extendThread.getId());
        log.info("threadName:{}", extendThread.getName());
        log.info("threadState:{}", extendThread.getState());
        log.info("threadPriority:{}", extendThread.getPriority());
        }
    • 实现Runnable接口

      • 可以使用匿名对象来进行实现,减少代码

        1
        2
        3
        4
        5
        6
        7
        @Test
        void test4(){
        Thread thread = new Thread(()->{
        System.out.println("lambda+匿名对象类");
        },"threadName");
        thread.start();
        }
    • 利用Callable和FutureTask创建

      • 异步?
    • 通过线程池创建

      • 减少线程创建、销毁时间

        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
        private static ExecutorServicepool= Executors.newFixedThreadPool(3);

        static class DemoThread implements Runnable{
        @SneakyThrows
        @Override
        public void run() {
        for (int i = 0; i < 10; i++) {
        log.info("execute times of runnable thread:{}",i);
        }
        }
        }

        static class ReturnableTask implements Callable<Long>{
        public Long call() throws Exception{
        long startTime = System.currentTimeMillis();
        for (int i = 0; i <10; i++) {
        log.info("execute times of Callable Thread:{}",i);
        }
        long used = System.currentTimeMillis() - startTime;
        return used;
        }
        }

        @Test
        public void test5() throws ExecutionException, InterruptedException {
        pool.execute(new DemoThread());
        // 无返回值

        Future future =pool.submit(new ReturnableTask());
        Long result = (Long) future.get();
        log.info("异步的结果为:{}",result);
        // 有返回值
        }
  2. 线程池

    1. 创建线程池

      1. 固定数量线程池

        1. ExecutorService pool = Executors.newFixedThreadPool(3);
      2. 缓存线程池

        1. ExecutorService pool = Executors.newCachedThreadPool();
      3. 定时执行线程池

        1. ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
      4. 线程池标准创建方式

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        public class ThreadPoolExecutor{
        // 核心线程数,即使线程空闲(Idle),也不会回收;
        int corePoolSize;

        // 线程数的上限;
        int maximumPoolSize;

        // 线程最大空闲(Idle)时长
        long keepAliveTime;

        //单位?
        TimeUnit unit;

        // 任务的排队队列
        BlockingQueue<Runnable> workQueue;

        // 新线程的产生方式
        ThreadFactory threadFactory;

        // 拒绝策略
        RejectedExecutionHandler handler;
        }
    2. 向线程池提交的方式

      1. execute
        1. 只能接收Runnable类型的参数
        2. 没有返回值
        3. 不利于异常捕获
      2. submit
        1. 能接受Runnable 和 Callable
        2. 有返回值
        3. 可以通过返回的Future(异步执行实例)对象来进行异常捕获
    3. 线程工厂

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    public void test9() {
    ExecutorService my_pool = Executors.newFixedThreadPool(2, new My_thread_factory());

    for (int i = 0; i < 5; i++) {
    my_pool.execute(new Task());
    }
    pool.shutdown();
    }
  3. 线程安全

    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
    package com.example.jc_demo.ThreadTest;

    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.Test;

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    /**
    *@AUTHOR:gao_quansui
    *@USER:ASUS
    *@DATE:2022/10/13 - 15:43
    *@PROJECT_NAME:jc_demo
    */

    @Slf4j
    public class ThreadSecurity {

    private static intcount= 0;

    class increaseVariable implements Runnable {
    @Override
    public void run() {
    plus();
    }
    }

    // synchronized 关键字设置同步方法 为粗粒度 整个方法都保护为一个线程执行
    // 相反 synchronized 代码块为细粒度 只保证代码块中的被一个线程执行
    public synchronized void plus() {
    count++;
    }

    @Test
    public void test1() throws InterruptedException {
    ExecutorService pool = Executors.newFixedThreadPool(30);
    for (int i = 0; i < 10000; i++) {
    pool.submit(new increaseVariable());
    }

    Thread.sleep(500);

    pool.shutdown();

    log.info(String.valueOf(count));
    }

    }

学习rabbitMQ

学习rabbitMQ

介绍:

(12条消息) windows环境下安装RabbitMQ(超详细)_luckySnow-julyo的博客-CSDN博客_windows安装rabbitmq

其他框架

Kafka、RabbitMQ、RocketMQ 全方位对比 - 龘人上天 - 博客园 (cnblogs.com)

整合步骤:

  1. 下载rabbitMQ,解压
  2. maven依赖
  3. 添加config类(生产者消费者应该不同项目)
  4. 启动rabbitMQ Rabbitmq的启动和停止 - .未央 - 博客园 (cnblogs.com)
  5. 访问 http://localhost:15672 usernname: guest password: guest

其他

1
2
3
4
5
6
7
8
9
<!--rabbitmq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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
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
66
67
68
69
70
71
72
73
74
package com.jcDemo.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* @author: gao_quansui
* @user:ASUS
* @date:2022/9/27 - 14:15
* @projectName:jc_demo
*/
@Configuration
public class DirectRabbitMQConfig {
// 创建队列
@Bean
public Queue TestDirectQueue() {
return new Queue("DirectQueue", true);
}

@Bean
public Queue TopicQueue() {
return new Queue("TopicQueue", true);
}

@Bean Queue FanoutQueue1(){
return new Queue("Fanout.Queue1");
}

@Bean Queue FanoutQueue2(){
return new Queue("Fanout.Queue2");
}

// 创建交换机
@Bean
DirectExchange DirectExchange() {
return new DirectExchange("DirectExchange", true, false);
}

@Bean
TopicExchange TopicExchange(){
return new TopicExchange("TopicExchange",true,false);
}

@Bean
FanoutExchange FanoutExchange(){
return new FanoutExchange("FanoutExchange",true,false);
}

// 建立连接绑定
// 直连为一个连接,即一对一
@Bean
Binding bindingDirect() {
return BindingBuilder.bind(TestDirectQueue()).to(DirectExchange()).with("DirectRoutingKey");
}

// 队列可以绑定多个生产者
@Bean
Binding bindingTopic(){
return BindingBuilder.bind(TopicQueue()).to(TopicExchange()).with("TopicRoutingKey.#");
// 匹配多个路由,使得消费者可以收到多个生产者的消息
}

// 交换机可以绑定多个队列(广播模式) (没有路由key)
@Bean
Binding bindingFanout1(){
return BindingBuilder.bind(FanoutQueue1()).to(FanoutExchange());
}

@Bean
Binding bindingFanout2(){
return BindingBuilder.bind(FanoutQueue2()).to(FanoutExchange());
}
}
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package com.jcDemo.controller.mq;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
* @author: gao_quansui
* @user:ASUS
* @date:2022/9/27 - 14:18
* @projectName:jc_demo
*/
@RestController
public class MQTest {

//使用RabbitTemplate,这提供了接收/发送等等方法
@Autowired
RabbitTemplate rabbitTemplate;

@GetMapping("/sendDirectMessage")
public String sendDirectMessage() {
String messageId = String.valueOf(UUID.randomUUID());
String messageData = "direct producer message, hello!";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String, Object> map = new HashMap<>();
map.put("messageId", messageId);
map.put("messageData", messageData);
map.put("createTime", createTime);
//将消息携带绑定键值
rabbitTemplate.convertAndSend("DirectExchange", "DirectRoutingKey", map);
return "ok";
}

@GetMapping("/sendTopicMessage1")
public String sendTopicMessage1() {
String messageId = String.valueOf(UUID.randomUUID());
String messageData = "topic producer1 message, hello!";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String, Object> map = new HashMap<>();
map.put("messageId", messageId);
map.put("messageData", messageData);
map.put("createTime", createTime);
//将消息携带绑定键值
rabbitTemplate.convertAndSend("TopicExchange", "TopicRoutingKey.test1", map);
return "ok";
}

@GetMapping("/sendTopicMessage2")
public String sendTopicMessage2() {
String messageId = String.valueOf(UUID.randomUUID());
String messageData = "topic producer2 message, hello!";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String, Object> map = new HashMap<>();
map.put("messageId", messageId);
map.put("messageData", messageData);
map.put("createTime", createTime);
//将消息携带绑定键值
rabbitTemplate.convertAndSend("TopicExchange", "TopicRoutingKey.test2", map);
return "ok";
}

@GetMapping("/sendFanoutMessage")
public String sendFanoutMessage() {
String messageId = String.valueOf(UUID.randomUUID());
String messageData = "fanout producer message, hello!";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String, Object> map = new HashMap<>();
map.put("messageId", messageId);
map.put("messageData", messageData);
map.put("createTime", createTime);
rabbitTemplate.convertAndSend("FanoutExchange","",map);
return "ok";
}
}

消费者-Direct

1
2
3
4
5
6
7
8
9
10
@Slf4j
@Component
@RabbitListener(queues = "DirectQueue")
public class ConsumerDirect {

@RabbitHandler
public void process(Map testMessage) {
log.info("DirectQueue的消费者收到消息{}",testMessage.toString());
}
}

消费者-Topic

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
package com.rabbit_consumer.mq;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.annotation.RabbitListeners;
import org.springframework.stereotype.Component;

import javax.naming.Binding;
import java.util.Map;

/**
* @author: gao_quansui
* @user:ASUS
* @date:2022/10/9 - 9:59
* @projectName:rabbit_consumer
*/

@Slf4j
@Component
@RabbitListener(queues = "TopicQueue")
// 该队列对应的生产者有两个, 能够收到两个生产者的消息,不同于直连的一一对应关系
public class ConsumerTopic {

@RabbitHandler
public void process(Map map){
log.info("TopicQueue received:{}",map.toString());
}
}

消费者-Fanout

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
package com.rabbit_consumer.mq;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
* @author: gao_quansui
* @user:ASUS
* @date:2022/10/9 - 13:45
* @projectName:rabbit_consumer
*/

@Slf4j
@Component
public class ConsumerFanout {

@RabbitListener(queues = "Fanout.Queue1")
// @RabbitHandler
public void process1(Map map) {
log.info("Consumer1 receive from FanoutQueue:{}", map);
}

@RabbitListener(queues = "Fanout.Queue2")
// @RabbitHandler
public void process(Map map){
log.info("Consumer2 receive from FanoutQueue:{}",map);
}
}

总结

  1. 三种模式

    1. Direct 直连 一一对应
    2. Topic 主题连接 利用*,# 进行匹配 一个交换机对应多个生产者
      • )只能向后匹配一个单词 即 a. → a.a(可以) a.a.a(不可以)
      • (#)只能向后匹配多个单词 即 a.* → a.a(可以) a.a.a(可以)
    3. Fanout 广播模式 一个交换机分发到多个队列,队列对应到相应的消费者
  2. 疑问:

    1. 能否实现一个生产者,一个交换机,一个队列,分发多个消费者?

Hutool

Hutool工具类学习

工具类,大杂烩

入门和安装 (hutool.cn)

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package com.example.jc_demo;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Month;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.*;
import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
public class UtilsTest {

@Test
void Convert() {
int a = 1;
String str_a = Convert.toStr(a);
log.info(String.valueOf(str_a instanceof String));

log.info("=========================================");

String[] b = {"1", "2"};
Integer[] integers = Convert.toIntArray(b);
for (int i = 0; i < integers.length; i++) {
log.info(String.valueOf(integers[i] instanceof Integer));
}

log.info("=========================================");

String c = "2022-10-10";
log.info(String.valueOf(Convert.toDate(c) instanceof Date));

log.info("=========================================");

Object[] d = {"1", 2, "牛", "jc"};
List<?> list = Convert.convert(List.class, d);
list.forEach(e -> {
log.info(e.toString() + ":" + e.getClass().toString());
});
log.info(list.getClass().toString());

log.info("=========================================");

String e = "123456789";
log.info(Convert.toSBC(e));
log.info(Convert.toDBC(Convert.toSBC(e)));

log.info("=========================================");

String f = "STRING";
log.info(Convert.toHex(f, CharsetUtil.CHARSET_UTF_8));
log.info(Convert.hexToStr(Convert.toHex(f, CharsetUtil.CHARSET_UTF_8), CharsetUtil.CHARSET_UTF_8));

log.info("=========================================");

String g = "字符串";
String result = Convert.convertCharset(g, CharsetUtil.UTF_8, CharsetUtil.ISO_8859_1);
String raw = Convert.convertCharset(result, CharsetUtil.ISO_8859_1, "UTF-8");
log.info(result);
log.info(raw);

log.info("=========================================");

long l = 23466543;
log.info(String.valueOf(Convert.convertTime(l, TimeUnit.MILLISECONDS, TimeUnit.MINUTES)));

log.info("=========================================");

double h = 1231.452;
log.info(Convert.digitToChinese(h));
log.info(Convert.numberToWord(h));
log.info(Convert.numberToSimple(h));
log.info(Convert.numberToChinese(12341., false));
log.info(Convert.numberToChinese(1234.1, true));
log.info(String.valueOf(Convert.chineseToNumber("一万二千三百四十一")));

log.info("=========================================");
log.info("=========================================");
}

@Test
void My_Date() {
log.info(String.valueOf(Month.of(Calendar.JANUARY).getLastDay(false)));
log.info(String.valueOf(Month.of(Calendar.JANUARY).getLastDay(true)));
log.info(DateUtil.date().toString());
log.info(DateUtil.date().toDateStr());
log.info(DateUtil.date(System.currentTimeMillis()).toString());
log.info(DateUtil.now().toString());
log.info(DateUtil.today().toString());
log.info(DateUtil.parse("2022-10-10", "yyyy-MM-dd").getClass().toString());
String date = "2022-10-10";
Date date1 = DateUtil.parse(date);

log.info(DateUtil.format(date1, "yyyy/MM/dd"));
log.info(DateUtil.formatDate(date1));
log.info(DateUtil.formatDateTime(date1));
log.info(DateUtil.formatTime(date1));

log.info(String.valueOf(DateUtil.year(date1)));
log.info(String.valueOf(DateUtil.month(date1)));

}

@Test
void File_utils() {
File[] ls = FileUtil.ls("C:\\Users");
for (int i = 0; i < ls.length; i++) {
log.info(ls[i].getName());
}
}

@Test
void StrUtils() {
log.info(StrUtil.removeSuffix("a.jpg", ".jpg"));
log.info(StrUtil.removePrefix("a.jpg", "a."));
String str = "123abc";
log.info(StrUtil.sub(str, 2, 4));
}

@Test
void IDCard() {
String id1 = "110101199003076413";
log.info(String.valueOf(IdcardUtil.isValidCard(id1)));

log.info(String.valueOf(IdcardUtil.getAgeByIdCard(id1)));
log.info(String.valueOf(IdcardUtil.getBirthByIdCard(id1)));
log.info(String.valueOf(IdcardUtil.getProvinceByIdCard(id1)));
log.info(String.valueOf(IdcardUtil.getProvinceCodeByIdCard(id1)));

}

@Test
void securityUtils() {
log.info(DesensitizedUtil.idCardNum("110101199003076413", 4, 4));
log.info(DesensitizedUtil.mobilePhone("15157547799"));
log.info(DesensitizedUtil.password("15157547799"));
}

@Test
void CreditCode() {
log.info(String.valueOf(CreditCodeUtil.isCreditCode("91310110666007217T")));
log.info(CreditCodeUtil.randomCreditCode());
}

@Test
void outUtils() {
Console.log("还能console 真不错");
Console.log("还能{} 真不错", "format");
Console.error("还能{} 真不错", "error");
}
}

Guava相关操作

Guava相关操作

Description

(12条消息) Guava的基础功能与集合_鲲鹏飞九万里的博客-CSDN博客_guava

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package com.example.jc_demo;

import cn.hutool.core.date.DateUtil;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.util.*;

@Slf4j
public class GuavaTest {
@Data
@AllArgsConstructor
@NoArgsConstructor
class User {
String name;
}

@Test
public void my_Optional() {
Optional<Integer> op = Optional.ofNullable(new Integer(123));
log.info(op.get().toString());
}

@Test
public void returnUser() {
User u = new User();
u = null;
// 如果不用ofNullable会有空指针异常 用ofNullable能够在get()的时候报出空元素异常
Optional<User> op = Optional.ofNullable(u);
User u1 = op.get();
log.info(u1.toString());
}

@Test
public void orElse() {
// ???防止空指针 备用值??? IFNULL???
Optional<String> op = Optional.of("aaa");
log.info(String.valueOf(op.isPresent()));
log.info(op.orElse(null));
}

@Test
public void isPresent() {
Optional<String> op = Optional.of("2322");
log.info(String.valueOf(op.isPresent()));

Optional<String> op1 = Optional.ofNullable(null);
log.info(String.valueOf(op1.isPresent()));
}

//

@Test
public void my_Preconditions() {
int i = -1;
int j = 10;
String k = null;
// Preconditions.checkArgument(i > j, "%s is not bigger than %s", i, j);
// Preconditions.checkNotNull(k,"k is null");
// Preconditions.checkArgument("1".equals("2"),"1 is not equals 2");

// int[] arr = new int[10];
// Preconditions.checkElementIndex(20,arr.length);
}

@Test
public void myImmutableSet() {
/**
* 三种方式创建 只读数组
* 1.of
* 2.builder
* 3.copy
*
* 注意:不可放入null
*/
ImmutableSet<String> immutableSet = ImmutableSet.of("a", "b", "c");
immutableSet.forEach(e->{
log.info(e);
});
// java.lang.UnsupportedOperationException 不允许操作数组, 只读
// immutableSet.add("d");

ImmutableSet<String> immutableSet1 = ImmutableSet.<String>builder()
.add("a")
.add("b")
.add("c")
.build();

immutableSet1.forEach(e->{
log.info(e);
});
// java.lang.UnsupportedOperationException 不允许操作数组, 只读
// immutableSet1.add("d");

// ImmutableSet<String> immutableSet3 = ImmutableSet.copyOf(arrayList);

}

@Data
@AllArgsConstructor
@NoArgsConstructor
private class Device {
Date CreatTime;
Date UpdateTime;
}

@Test
public void my_Order() {
Ordering ordering = new Ordering<String>() {
// order by length
@Override
public int compare(String s, String t1) {
return s.length() > t1.length() ? new Integer(1) : new Integer(-1);
}
};

ArrayList<String> list = new ArrayList<>();
list.add("sadewf");
list.add("sadewfsda");
list.add("sadewfsdaasdfeao");
list.add("aadewfsdaasdfea");
list.add("zoad");
list.add("zoadnas");
list.add("doadn");
list.add("doadnsadasda");
list.add("zoadnasodnaosndaosd");

//ascii ?
Collections.sort(list);
My_ForEach(list);

Collections.sort(list, ordering);
My_ForEach(list);

Collections.reverse(list);
My_ForEach(list);

}

@Test
public void order2() {
// 链式调用 从右往左调用,先orderBy XXX 将Null放最前或最后 对剩下的自然排序
Ordering<Device> ordering1 = Ordering.natural().nullsFirst().onResultOf((Device device) -> device.CreatTime);
Ordering<Device> ordering2 = Ordering.natural().nullsLast().onResultOf((Device device) -> device.CreatTime);
List<Device> list1 = new ArrayList<>();
list1.add(new Device(DateUtil.parse("2022-1-1", "yyyy-MM-dd"), null));
list1.add(new Device(null, null));
list1.add(new Device(DateUtil.parse("2022-1-8", "yyyy-MM-dd"), null));
list1.add(new Device(DateUtil.parse("2021-1-7", "yyyy-MM-dd"), null));
list1.add(new Device(DateUtil.parse("2022-5-1", "yyyy-MM-dd"), null));
list1.add(new Device(DateUtil.parse("2022-1-7", "yyyy-MM-dd"), null));
list1.add(new Device(DateUtil.parse("2022-3-1", "yyyy-MM-dd"), null));
list1.add(new Device(DateUtil.parse("2022-6-1", "yyyy-MM-dd"), null));

Collections.sort(list1, ordering2);

My_ForEach(list1);
}

@Test
public void isOrder() {
List<String> list = new ArrayList<>(Arrays.asList("a", "asd", "b", "asdve"));

// 返回降序前两个
List<String> list1 = Ordering.natural().greatestOf(list, 2);
// 返回升序后两个
List<String> list2 = Ordering.natural().leastOf(list, 2);

My_ForEach(list1);
My_ForEach(list2);
}

public void My_ForEach(List list) {
if (list.size() > 0) {
list.forEach(e -> {
log.info(e.toString());
});
} else {
log.error("空集合");
}
log.info("=======================================================");
}
}

SpringBoot

SpringBoot

分组校验

  1. 实体类中创建接口(为空,用于连接实体类以及接口)
  2. 在字段上对不同接口进行分别验证
  3. Controller中对参数开启指定接口验证
1
public Result updateDevice(@RequestBody @Validated(value = Device.UpdateDevice.class) Device device) throws ParseException {
1
2
3
4
5
6
7
8
9
public interface InsertDevice {
}

public interface UpdateDevice {
}

@NotNull(message = "设备ID不能为空", groups = {UpdateDevice.class})
@ApiModelProperty("设备ID")
private Integer id;

实体类使用引用类型

由于java中的基本类型会有默认值,例如当某个类中存在private int age;字段时,创建这个类时,age会有默认值0.当使用age属性时,它总会有值。因此,在某些情况下,便无法使age为null.并且在动态SQL的部分,如果使用age != null 进行判断,结果总会为true,会导致很多隐藏的问题。

所以,在实体类中不要使用基本类型。基本类型包括 byte\short\int\long\float\double\char\boolean.

结论:在mybatis中,不要使用基本类型,要使用引用类型。

统一处理权限返回值

1
2
3
4
5
6
7
8
9
10
11
@ControllerAdvice
public class UnauthorizedAdvice {

@ResponseBody
@ExceptionHandler(AuthorizationException.class)
public Result AuthorizationException(Exception ex) {
// 登录了 但是操作权限没有
return new Result(ResultCode.UNAUTHENTICATED_OPERATION);
}

}

@postConstruct

  • 注解使用在方法上,表示该方法在实例化当前Bean后立刻执行该方法,再去实例化其他Bean
  • 可以有多个@PostConstruct方法

做初始化工作

ResponseEntity.ok(execute)

1
2
3
4
5
6
7
@GetMapping("/lua")
public ResponseEntity lua() {
List<String> keys = Arrays.asList("testLua", "hello lua");
Boolean execute = stringRedisTemplate.execute(redisScript, keys, "100","200");
assert execute != null;
return ResponseEntity.ok(execute);
}

返回ok

ClassPathResource

1
2
3
4
5
6
7
@Bean
public DefaultRedisScript<Boolean> redisScript() {
DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("\\script\\firstLua.lua")));
redisScript.setResultType(Boolean.class);
return redisScript;
}

ClassPathResource直接在resources包下,路径打后面的就可以

shiro结合ehcache实现缓存

Description

为解决每次调用接口都执行shiro授权操作,影响效率,因此引入缓存概念

STEP

  1. 导入依赖
  2. 配置ehcache-shieo.xml文件
  3. ShiroConfig类中添加相关Bean
    1. EhCacheManager
    2. DefaultWebSecurityManager(设置缓存管理器,即a)
    3. UserRealm中启用验证,名字为xml文件中的
  4. 启动类开启缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 开启 cache 缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<!-- ehcache 缓存 -->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.10.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache-core -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">

<!--
缓存对象存放路径
java.io.tmpdir:默认的临时文件存放路径。
user.home:用户的主目录。
user.dir:用户的当前工作目录,即当前程序所对应的工作路径。
其它通过命令行指定的系统属性,如“java –DdiskStore.path=D:\\abc ……”。
-->
<diskStore path="java.io.tmpdir"/>

<!--
name:缓存名称。
maxElementsOnDisk:硬盘最大缓存个数。0表示不限制
maxEntriesLocalHeap:指定允许在内存中存放元素的最大数量,0表示不限制。
maxBytesLocalDisk:指定当前缓存能够使用的硬盘的最大字节数,其值可以是数字加单位,单位可以是K、M或者G,不区分大小写,
如:30G。当在CacheManager级别指定了该属性后,Cache级别也可以用百分比来表示,
如:60%,表示最多使用CacheManager级别指定硬盘容量的60%。该属性也可以在运行期指定。当指定了该属性后会隐式的使当前Cache的overflowToDisk为true。
maxEntriesInCache:指定缓存中允许存放元素的最大数量。这个属性也可以在运行期动态修改。但是这个属性只对Terracotta分布式缓存有用。
maxBytesLocalHeap:指定当前缓存能够使用的堆内存的最大字节数,其值的设置规则跟maxBytesLocalDisk是一样的。
maxBytesLocalOffHeap:指定当前Cache允许使用的非堆内存的最大字节数。当指定了该属性后,会使当前Cache的overflowToOffHeap的值变为true,
如果我们需要关闭overflowToOffHeap,那么我们需要显示的指定overflowToOffHeap的值为false。
overflowToDisk:boolean类型,默认为false。当内存里面的缓存已经达到预设的上限时是否允许将按驱除策略驱除的元素保存在硬盘上,默认是LRU(最近最少使用)。
当指定为false的时候表示缓存信息不会保存到磁盘上,只会保存在内存中。
该属性现在已经废弃,推荐使用cache元素的子元素persistence来代替,如:<persistence strategy=”localTempSwap”/>。
diskSpoolBufferSizeMB:当往磁盘上写入缓存信息时缓冲区的大小,单位是MB,默认是30。
overflowToOffHeap:boolean类型,默认为false。表示是否允许Cache使用非堆内存进行存储,非堆内存是不受Java GC影响的。该属性只对企业版Ehcache有用。
copyOnRead:当指定该属性为true时,我们在从Cache中读数据时取到的是Cache中对应元素的一个copy副本,而不是对应的一个引用。默认为false。
copyOnWrite:当指定该属性为true时,我们在往Cache中写入数据时用的是原对象的一个copy副本,而不是对应的一个引用。默认为false。
timeToIdleSeconds:单位是秒,表示一个元素所允许闲置的最大时间,也就是说一个元素在不被请求的情况下允许在缓存中待的最大时间。默认是0,表示不限制。
timeToLiveSeconds:单位是秒,表示无论一个元素闲置与否,其允许在Cache中存在的最大时间。默认是0,表示不限制。
eternal:boolean类型,表示是否永恒,默认为false。如果设为true,将忽略timeToIdleSeconds和timeToLiveSeconds,Cache内的元素永远都不会过期,也就不会因为元素的过期而被清除了。
diskExpiryThreadIntervalSeconds :单位是秒,表示多久检查元素是否过期的线程多久运行一次,默认是120秒。
clearOnFlush:boolean类型。表示在调用Cache的flush方法时是否要清空MemoryStore。默认为true。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
maxElementsInMemory:缓存最大数目
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
memoryStoreEvictionPolicy:
Ehcache的三种清空策略;
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>

<!-- 授权缓存 -->
<cache name="authorizationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>

<!-- 认证缓存 -->
<cache name="authenticationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>

</ehcache>
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package com.jc_demo.shiro;

import com.jc_demo.entity.entities.User;
import com.jc_demo.interceptor.MyFormAuthenticationFilter;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* @author: gao_quansui
* @user:ASUS
* @date:2022/9/21 - 10:57
* @projectName:jc_demo
*/

@Configuration
public class ShiroConfig {

@Bean
public UserRealm userRealm() {
UserRealm ur= new UserRealm();
// 告诉realm,使用credentialsMatcher加密算法类来验证密文
// ur.setCredentialsMatcher(hashedCredentialsMatcher());
/* 开启支持缓存,需要配置如下几个参数 */
ur.setCachingEnabled(true);
// 启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
ur.setAuthenticationCachingEnabled(true);
// 缓存AuthenticationInfo信息的缓存名称 在 ehcache-shiro.xml 中有对应缓存的配置
ur.setAuthenticationCacheName("authenticationCache");
// 启用授权缓存,即缓存AuthorizationInfo信息,默认false
ur.setAuthorizationCachingEnabled(true);
// 缓存AuthorizationInfo 信息的缓存名称 在 ehcache-shiro.xml 中有对应缓存的配置
ur.setAuthorizationCacheName("authorizationCache");

return ur;
}

@Bean(name = "filterShiroFilterRegistrationBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {

// 1.创建过滤工厂
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

Map<String, Filter> filters = new LinkedHashMap<>();
filters.put("MyFormAuthenticationFilter", new MyFormAuthenticationFilter());
bean.setFilters(filters);
// 2.设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);

// 3.配置未授权跳转页面
// bean.setLoginUrl("/test");

// 4.设置filter
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/api/user/login", "anon");

// filterMap.put("/api/user/role/**", "authc");

filterMap.put("/api/**", "MyFormAuthenticationFilter");

bean.setFilterChainDefinitionMap(filterMap);

return bean;
}

@Bean(name = "SecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

// 将 CookieRememberMeManager 注入到 SecurityManager 中,否则不会生效
// securityManager.setRememberMeManager(rememberMeManager());
// 将 sessionManager 注入到 SecurityManager 中,否则不会生效
// securityManager.setSessionManager(sessionManager());
// 将 EhCacheManager 注入到 SecurityManager 中,否则不会生效
securityManager.setCacheManager(ehCacheManager());
securityManager.setRealm(userRealm);
return securityManager;
}

// 开启shiro注解
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}

// 开启aop注解支持
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}

// shiro 缓存
@Bean
public EhCacheManager ehCacheManager(){
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:\\shiro\\ehcache-shiro.xml");
return cacheManager;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.jc_demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import springfox.documentation.oas.annotations.EnableOpenApi;

@EnableCaching
// 开启缓存 shiro权限认证
@EnableOpenApi
@SpringBootApplication
@MapperScan("com.jc_demo.mapper")
public class JcDemoApplication {

public static void main(String[] args) {
SpringApplication.run(JcDemoApplication.class, args);
}

}