day01
在IDEA中Debug
断点处右键可以设置条件,当满足条件时,才会停止到当行。
玩转Stream流
Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
Stream对流的两种操作
- 中间操作,每次返回一个新的流,可以有多个。(筛选filter、映射map、排序sorted、去重组合skip—limit)
- 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。(遍历foreach、匹配find–match、规约reduce、聚合max–min–count、收集collect)
Stream特性
- stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
- stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
- 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是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如二者在筛选集合中奇数的处理方式不同:
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
|
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()); 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)
- 获取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()); } }
|
- 获取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);
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); Optional<Integer> sum = list.stream().reduce((x, y) -> x + y); Optional<Integer> sum2 = list.stream().reduce(Integer::sum); Integer sum3 = list.stream().reduce(0, Integer::sum); Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y); 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); }
|
收集(Collect)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList
、toSet
和toMap
比较常用。
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单元测试
什么是单元测试
当一个测试满足下面任意一点,就不是单元测试
- 与数据库/网络/文件系统交流
- 不能与其他单元测试同一时间运行
- 不能特意为单元测试做特别的事情
微服务中先从对外接口开始测,这样做可以用尽量少的测试代码,覆盖更多的功能逻辑,测试效率高。其次是我们可以在保证接口不变的情况下,对内部逻辑进行重构,重构效率高(“粗粒度测试”)。
依赖问题的解决
对于分布式微服务系统而言,“粗粒度单元测试”最大挑战在于如何解决周边依赖问题,对于一个典型的应用而言,它可能会依赖数据库、消息中间件、缓存系统等,以及周边的其它服务。
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 userRepositoryMock = mock(UserRepository.class); UserService userService = new UserService(userRepositoryMock); User expectedUser = new User(1, "John"); expectedUser.setAge(35); expectedUser.setSex("male"); expectedUser.setEducation("doctor"); when(userRepositoryMock.getUserById(1)).thenReturn(expectedUser); User actualUser = userService.getUserById(1); 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") ); 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( 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