day01

在IDEA中Debug

断点处右键可以设置条件,当满足条件时,才会停止到当行。

玩转Stream流

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。

Stream对流的两种操作

  1. 中间操作,每次返回一个新的流,可以有多个。(筛选filter、映射map、排序sorted、去重组合skip—limit)
  2. 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。(遍历foreach、匹配find–match、规约reduce、聚合max–min–count、收集collect)

Stream特性

  1. stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  2. stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
  3. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

Stream与传统遍历对比

几乎所有的集合(如 Collection 接口或 Map 接口等)都支持直接或间接的遍历操作。而当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。

  • 传统遍历方式与流式遍历:
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
import java.util.ArrayList;
import java.util.List;

public class Demo1List {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("小昭");
list.add("殷离");
list.add("张三");
list.add("张三丰");

List<String> listA = new ArrayList<>();
for ( String s : list) {
if (s.startsWith("张"))
listA.add(s);
}

List<String> listB = new ArrayList<>();
for (String s: listA) {
if (s.length() == 3)
listB.add(s);
}

for (String s: listB) {
System.out.println(s);
}

//流式遍历,可以
list.stream()
.filter(name -> name.startsWith("张"))
.filter(name -> name.length() == 3)
.forEach(name -> System.out.println(name));
}
}
  • 循环遍历明显看出很繁杂,而流式遍历步骤简单,一步到位。

Stream的创建

Stream可以通过集合或数组创建。

1. 通过java.util.Collection.stream()方法用集合创建流
1
2
3
4
5
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
2. 通过java.util.Collection.stream()方法用集合创建流
1
2
int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
stream和parallelStream的简单区分

stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如二者在筛选集合中奇数的处理方式不同:

alt text

Stream流的使用

Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。(用以接受Stream筛选后的元素)

遍历/筛选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// import已省略,请自行添加,后面代码亦是

public class StreamTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);

// 遍历输出符合条件的元素
list.stream().filter(x -> x > 6).forEach(System.out::println);
// 匹配第一个
Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
// 匹配任意(适用于并行流)
Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
// 是否包含符合特定条件的元素
boolean anyMatch = list.stream().anyMatch(x -> x > 6);
System.out.println("匹配第一个值:" + findFirst.get());
System.out.println("匹配任意一个值:" + findAny.get());
System.out.println("是否存在大于6的值:" + anyMatch);
}
}

筛选出员工中工资高于8000的员工姓名并形成新的集合,形成新集合依赖collect:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StreamTest {
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
personList.add(new Person("Owen", 9500, 25, "male", "New York"));
personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

List<String> filterList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName).collect(Collectors.toList()); //map在流式处理中将元素转换为另一个形式,比如这里转化为姓名
System.out.print("高于8000的员工姓名:" + filterList);

Optional<Person> max = personList.stream().max(Comparator.comparingInt(Person::getSalary));
System.out.println("员工工资最大值:" + max.get().getSalary());


List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
long count = list.stream().filter(x -> x > 6).count();
System.out.println("list中大于6的元素个数:" + count);

}
}
聚合(max/min/count)
  1. 获取String集合中最长的元素
1
2
3
4
5
6
7
public class StreamTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
Optional<String> max = list.stream().max(Comparator.comparing(String::length));
System.out.println("最长的字符串:" + max.get());
}
}
  1. 获取Integer集合中的最大值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class StreamTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);

// 自然排序
Optional<Integer> max = list.stream().max(Integer::compareTo);
// 自定义排序
Optional<Integer> max2 = list.stream().max(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
System.out.println("自然排序的最大值:" + max.get());
System.out.println("自定义排序的最大值:" + max2.get());
}
}
映射(map、flatMap)

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap(map往往后面跟着的是collect,意味着要收集成新元素):

  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

代码案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class StreamTest {
public static void main(String[] args) {
String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());

List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());

System.out.println("每个元素大写:" + strList);
System.out.println("每个元素+3:" + intListNew);

// 给每位员工增加1000薪资
// 改变原来员工集合的方式
List<Person> personListNew2 = personList.stream().map(person -> {
person.setSalary(person.getSalary() + 10000);
return person;
}).collect(Collectors.toList());

}

}
规约(reduce)

归约,也称缩减,即把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。

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 StreamTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
// 求和方式1
Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
// 求和方式2
Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
// 求和方式3
Integer sum3 = list.stream().reduce(0, Integer::sum);

// 求乘积
Optional<Integer> product = list.stream().reduce((x, y) -> x * y);

// 求最大值方式1
Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
// 求最大值写法2
Integer max2 = list.stream().reduce(1, Integer::max);

System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
System.out.println("list求积:" + product.get());
System.out.println("list求和:" + max.get() + "," + max2);
}

Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(),Integer::sum); //(reduce(0,(sum, p) -> sum += p.getSalary(),表示从0开始收集,后面是一个lambda方法,表示统计薪资集合)
}
收集(Collect)

因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toListtoSettoMap比较常用。

Stream流总结内容,后续还有排序(sorted)等内容

day02

Lambda表达式

比较器的Lambda写法:

1
2
Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1]: o2[0] - o1[0]); //二维数组
Arrays.sort(intervals, (a, b) -> a[0] - b[0]); //二维数组

SpringBoot单元测试

什么是单元测试

当一个测试满足下面任意一点,就不是单元测试

  1. 与数据库/网络/文件系统交流
  2. 不能与其他单元测试同一时间运行
  3. 不能特意为单元测试做特别的事情

微服务中单元测试(开发者测试)

微服务中先从对外接口开始测,这样做可以用尽量少的测试代码,覆盖更多的功能逻辑,测试效率高。其次是我们可以在保证接口不变的情况下,对内部逻辑进行重构,重构效率高(“粗粒度测试”)。

依赖问题的解决

对于分布式微服务系统而言,“粗粒度单元测试”最大挑战在于如何解决周边依赖问题,对于一个典型的应用而言,它可能会依赖数据库、消息中间件、缓存系统等,以及周边的其它服务。

Mock做替身

框架比如Mockito,EasyMock支持去做mock的事情,比如对于常见的数据库依赖,我们可以通过如下的方式去mock(基于Mockito框架):

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
import static org.mockito.Mockito.*;

public class UserServiceTest {

@Test
public void testGetUserById() {
// 创建一个模拟的 UserRepository
UserRepository userRepositoryMock = mock(UserRepository.class);

// 创建一个 UserService 实例,将模拟的 UserRepository 传入
UserService userService = new UserService(userRepositoryMock);

// 设置模拟的 UserRepository 的行为
User expectedUser = new User(1, "John");
expectedUser.setAge(35);
expectedUser.setSex("male");
expectedUser.setEducation("doctor");
when(userRepositoryMock.getUserById(1)).thenReturn(expectedUser);

// 调用 UserService 的方法
User actualUser = userService.getUserById(1);

// 验证模拟的 UserRepository 的方法是否被正确调用
verify(userRepositoryMock).getUserById(1);

// 断言返回的用户是否符合预期
assertEquals(expectedUser, actualUser);
}
}
EmbeddedServer做替身

比如我用到了Redis缓存,那么我可以使用embedded-redis启动一个本地的redis,从而连接到“真实”的redis环境。使用的话只需要加入下面的依赖。在Junit5的Extension帮助下,我们可以很优雅的使用embedded的服务。

1
2
3
4
5
6
<dependency>
<groupId>com.github.codemonstur</groupId>
<artifactId>embedded-redis</artifactId>
<version>1.4.2</version>
<scope>test</scope>
</dependency>

day04

Dynamic与Druid中的多数据源配置与应用

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
import javax.sql.DataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration
public class DataSourceConfig {

@Bean(name = "datasource1")
@ConfigurationProperties(prefix = "spring.datasource.druid.datasource1")
public DataSource dataSource1() {
return new DruidDataSource();
}


@Bean(name = "datasource2")
@ConfigurationProperties(prefix = "spring.datasource.druid.datasource2")
public DataSource dataSource2() {
return new DruidDataSource();
}
}

主数据源配置

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
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = {"com.ais.cdc.mapper"}, sqlSessionFactoryRef = "sqlSessionFactoryDs1")
public class MybatisMasterConfig {

@Autowired
@Qualifier("datasource1")
private DataSource datasource1;

@Bean
@Primary
public SqlSessionFactory sqlSessionFactoryDs1() throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(datasource1);
factoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*.xml")
);
//向Mybatis过滤器链中添加拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
factoryBean.setPlugins(interceptor);
return factoryBean.getObject();
}

@Bean
@Primary
public SqlSessionTemplate sqlSessionTemplateDs1() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactoryDs1());
return template;
}

@Bean
@Primary
public DataSourceTransactionManager transactionManager1() {
return new DataSourceTransactionManager(datasource1);
}
}

从数据源的配置

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
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = {"com.ais.cdc.mapper2"}, sqlSessionFactoryRef = "sqlSessionFactoryDs2")
public class MybatisSlaveConfig {

@Autowired
@Qualifier("datasource2")
private DataSource datasource2;

@Bean
public SqlSessionFactory sqlSessionFactoryDs2() throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(datasource2);
factoryBean.setMapperLocations(
//设置mybatis的xml所在位置
new PathMatchingResourcePatternResolver().getResources("classpath:/mapper2/*.xml")
);
return factoryBean.getObject();
}

@Bean
public SqlSessionTemplate sqlSessionTemplateDs2() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactoryDs2());
return template;
}

@Bean
public DataSourceTransactionManager transactionManager2() {
return new DataSourceTransactionManager(datasource2);
}
}

配置后相关报错问题

IO多路复用机制,select/epoll原理

RabbitMQ