SpringBoot2.0不容错过的新特性 ——WebFlux响应式编程(2)

一、函数式编程

1、函数式编程是一种方法论:能够熟练使用流 API和lambda表达式和流相关思想,就可以说自己会函数式编程了。

2、命令式编程关注 怎么做,函数式编程关注 做什么(我要实现什么样的功能而不用管实现的细节)

二、Lambda表达式:

1、Lambda表达式返回的是一个指定接口的对象实例。接口里只能有一个要实现的方法(要实现的方法并不是指接口里只能有一个方法)。jdk8里引入注解@FunctionalInterface,标明此接口为函数接口。jdk8里还新增了已实现的方法(如图)

2、函数接口:

Function<Integer,String>:定义一个输入是int类型,输出是String的函数。使用函数接口,可以不用定义那么多函数接口,还可以实现链式编程。

链式操作举例

class MyMoney{
    private final int money;
    public MyMoney(int money){
        this.money = money;
    }
    public void getMoney(Function<Integer,String> func){
        System.out.println("我的存款是:"+func.apply(this.money));
    }
}
public class MyMoneyDemo {
    public static void main(String[] args) {
        MyMoney me = new MyMoney(999999);
        Function<Integer, String> function = i -> new DecimalFormat("#,###").format(i);
        me.getMoney(function.andThen(s -> "人民币"+ s));
    }

}

public class FunctionDemo {
    public static void main(String[] args) {
        //断言接口
        IntPredicate result = i -> i>0 ;
        System.out.println(result.test(-9));

        //消费者接口
        Consumer<String> consumer = s -> System.out.println(s);
        consumer.accept("输入的数据");
    }

Lambda的本质是一个匿名函数

s -> System.out.println(s);

函数的参数    函数的执行体

3、Lambda方法引用:

静态方法的引用:

class Dog{
    private String name = "哮天犬";
    public static void bak(Dog dog){
        System.out.println(dog+"叫了");
    }
    @Override
    public String toString() {
        return this.name;
    }
}

public class FunctionDemo {
    public static void main(String[] args) {
        //静态方法
        Consumer<Dog> consumer = Dog::bak;
        Dog dog = new Dog();
        consumer.accept(dog);
    }


}

非静态方法的引用:


class Dog{
    private String name = "哮天犬";
    private int food = 50;
    public static void bak(Dog dog){
        System.out.println(dog+"叫了");
    }
    public int eat(int num){
        System.out.println("吃了"+num +"狗粮");
        this.food -= num;
        return this.food;
    }
    @Override
    public String toString() {
        return this.name;
    }
}

public class FunctionDemo {
    public static void main(String[] args) {
        //静态方法
//        Consumer<Dog> consumer = Dog::bak;
        Dog dog = new Dog();
//        consumer.accept(dog);
        //1.非静态,使用对象实例的方法引用
        Function<Integer,Integer> function = dog::eat;
        System.out.println("还剩下:"+function.apply(2)+"斤");

    }

<Integer,Integer>方法的输入和输出类型一致时

//        Function<Integer,Integer> function = dog::eat;
//        UnaryOperator<Integer> function = dog::eat;//一元函数接口
          IntUnaryOperator function = dog::eat;
          System.out.println("还剩下:"+function.applyAsInt(2)+"斤");

修改非静态的参数:

不报错的原因:jdk默认会把当前实例传入非静态函数,参数名是this,位置是第一个。

所以当我们用类名对非静态方法做方法引用的时候,参数名为两个。

以上两种都可以

  //2.使用类名引用方法
        Dog dog = new Dog();
        BiFunction<Dog,Integer,Integer> biFunction = Dog::eat;
        System.out.println("还剩下:"+biFunction.apply(dog,6)+"斤");

没有参数的构造函数方法的引用: 

Supplier<Dog> dogSupplier = Dog::new;
        System.out.println("创造了一个新的对象:"+dogSupplier.get());

有参数的构造函数方法的引用:

class Dog{
    private String name = "哮天犬";
    private int food = 50;
    public Dog(){

    }
    public Dog(String name){
        this.name = name;
    }

    


 Function<String,Dog> stringDogFunction = Dog::new;
 System.out.println("创造了一个新的对象:"+stringDogFunction.apply("旺旺"));

 

4、

Lambda返回了一个指定接口的对象

 lambda的类型推断:

解决:强转

Jdk1.8之前,内部类要引用外部的变量,外部的变量需要命名为final

传参传的是值而不是引用

原因:传值图解:

传引用图解:

5、级联表达式和柯里化:

级联表达式:有多个箭头的lambda表达式

6、Stream流编程

流的创建:

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        //并行流
        list.stream();
        list.parallelStream();
        //从数组创建
        Arrays.stream(new int[]{2,3,4});
        //创建一个数字流
        IntStream.of(1,2,3);
        IntStream.rangeClosed(1,10);
        //使用random创建一个无限流
        new Random().ints().limit(10);
        Random random = new Random();
        Stream.generate(() -> random.nextInt()).limit(20);
    }

无状态:我当前的操作和其他元素的前后状态无关

有状态:我当前的结果需要依赖其他的元素(例如排序)

返回的都是String

Map:得到某个对象的属性。例如mapToInt(输入一个字符串得到它的长度)

public static void main(String[] args) {
        //打印出每个单词的长度
        String str= "my name is hhdh";
//        Stream.of(str.split(" ")).filter(s -> s.length()>2).map(s -> s.length()).forEach(System.out::println);
        //flatMap a ->B属性(集合)最终得到所有的A元素里面所有的B集合的属性
        //返回的是IntStream/LongStrem 并不是Stream的子类,所以需要自动装箱
        //打印出来的是所有的字符
        Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).forEach(
                i -> System.out.println((char)i.intValue())
        );
        //peek用于debug是个中间操作,foEach是个中间操作
        //my my name name is is hhdh hhdh
        Stream.of(str.split(" ")).peek(System.out::println).forEach(System.out::println);
        //limit的使用
         New Random().ints().filter(s -> s>=1000&&s<=10000).limit(10).forEach(System.out::println);
    }

(char)i.intValue():不加char 打印出的是数字不是字符。i.intValue():Integer需要转换为int

public static void main(String[] args) {
        String str= "my name is hhdh";
        str.chars().parallel().forEach(i -> System.out.print((char)i));
        //有序
        //my name is hhdh
        str.chars().parallel().forEachOrdered(i -> System.out.print((char)i));
        //收集到list
        //[my, name, is, hhdh]
        List<String> list =  Stream.of(str.split(" ")).collect(Collectors.toList());
        System.out.println(list);
        //使用reduce拼接字符串
        //my|name|is|hhdh
        Optional<String> letter = Stream.of(str.split(" ")).reduce((s1, s2) -> s1 +"|" +s2);
        //返回值为空时不报错
        System.out.println(letter.orElse(""));
        //|my|name|is|hhdh
        String reduce = Stream.of(str.split(" ")).reduce("",(s1, s2) -> s1 +"|" +s2);
        System.out.println(reduce);
        //单词的总长度
        Integer length = Stream.of(str.split(" ")).map(s -> s.length()).reduce(0,(s1,s2) -> s1+s2);
        System.out.println(length);
        //长度最长的单词
        //name
        Optional<String> max = Stream.of(str.split(" ")).max((s1,s2) -> s1.length() - s2.length());
        System.out.println(max.get());

        //短路操作
        //-1330528590
        OptionalInt first = new Random().ints().findFirst();
        System.out.println(first.getAsInt());

    }

并行流


public class StreamDemo3 {
    public static void main(String[] args) {
        //一行一个的打印
        IntStream.range(1,100).peek(StreamDemo3::debug).count();
        //一次两行的打印
        IntStream.range(1,100).parallel().peek(StreamDemo3::debug).count();
        //先并行再串行
        //多次调用parallel/sequential,以最后一次为准(截图)
        IntStream.range(1,100).parallel().peek(StreamDemo3::debug2).sequential()
                .peek(StreamDemo3::debug).count();
        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism()","20");
        IntStream.range(1,1000).parallel().peek(StreamDemo3::debug).count();  未成功
        //使用自己的线程池,不适用默认的,防止任务被阻塞
        ForkJoinPool pool = new ForkJoinPool(8);
        pool.submit(() -> IntStream.range(1,100).parallel().peek(StreamDemo3::debug).count());
        pool.shutdown();
        //自己的线程是守护线程的关系,因为main函数主线程退出了,所以守护线程也退出了
        synchronized (pool){
            try {
                pool.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void debug(int i){
        System.out.println(Thread.currentThread().getName()+"debug" + i);
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void debug2(int i){
        System.err.println("debug" + i);
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

收集器:

使用MapUtils引入依赖:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
</dependency>
package com.example.springwebfluxtest;

import com.sun.xml.internal.ws.policy.PolicyMapUtil;
import org.apache.commons.collections.MapUtils;

import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.Collectors;

class Student{
    private String name;
    private int age;
    private String address;

    public Student() {
    }

    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
public class CollectorDemo {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
               new Student("小王",12,"北京"),
                new Student("小红",12,"北京"),
                new Student("小ming",14,"北京")
        );
//        List<Integer> ages = students.stream().map(s -> s.getAge()).collect(Collectors.toList());
        //不会多生成一个类似lambda$0的函数
        List<Integer> ages = students.stream()
                .map(Student::getAge).collect(Collectors.toList());
        //去重复[12, 13, 14]
        Set<Integer> ages2 = students.stream()
                .map(Student::getAge).collect(Collectors.toSet());
        //转换为指定类型的集合
        Set<Integer> ages3 = students.stream()
                .map(Student::getAge).collect(Collectors.toCollection(TreeSet::new));
        System.out.println(ages2);
        //统计信息
        //IntSummaryStatistics{count=3, sum=39, min=12, average=13.000000, max=14}
        IntSummaryStatistics statistics = students.stream()
                .collect(Collectors.summarizingInt(Student::getAge));
        System.out.println(statistics);

        //分块
        //列表分块 =
        //{
        //    false = [Student{name='小ming', age=14, address='北京'}]
        //    true = [Student{name='小王', age=12, address='北京'}, Student{name='小红', age=12, address='北京'}]
        //}
        Map<Boolean,List<Student>> map = students.stream().collect(Collectors.partitioningBy(s -> s.getAge() == 12));
        MapUtils.verbosePrint(System.out,"列表分块",map);
        //分组
        //列表分组 =
        //{
        //    12 = [Student{name='小王', age=12, address='北京'}, Student{name='小红', age=12, address='北京'}]
        //    14 = [Student{name='小ming', age=14, address='北京'}]
        //}
        Map<Integer,List<Student>> integerListMap = students.stream().collect(Collectors.groupingBy(Student::getAge));
        MapUtils.verbosePrint(System.out,"列表分组",integerListMap);
        //列表分组统计 =
        //{
        //    12 = 2
        //    14 = 1
        //}
        Map<Integer,Long> count = students.stream()
                .collect(Collectors.groupingBy(Student::getAge,Collectors.counting()));
        MapUtils.verbosePrint(System.out,"列表分组统计",count);
    }

}

JDK1.9 Reactive Stream

背压

Reactive Stream    调节发布、订阅者直接的关系

同步servlet和异步servlet针对的是后台服务器而言,前台只有同步

同步servlet阻塞的是Tomcat的servlet线程,开启异步Servlet,可以把耗时的任务放在一个独立的线程池里,servlet线程可以快速返回去处理其他的任务。

异步servlet是怎样工作的:

1、开启异步上下文

2、把业务代码放在一个独立的线程池进行

3、调用complete通知结束

@RestController
@Slf4j
public class FluxController {
    @RequestMapping("/1")
    public String hello(){
        log.info("get start");
        String result = createStr();
        log.info("get end");
        return result;
    }

    private String  createStr() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "sone String";
    }

    @RequestMapping("/2")
    public Mono<String> hello2(){
        log.info("get start2");
        Mono<String> result =Mono.fromSupplier(() -> createStr());
        log.info("get end2");
        return result;
    }
}

访问localhost:8080/1

2018-12-26 16:35:38.206  INFO 9252 --- [ctor-http-nio-2] c.e.springwebfluxtest.FluxController     : get start

2018-12-26 16:35:43.207  INFO 9252 --- [ctor-http-nio-2] c.e.springwebfluxtest.FluxController     : get end

访问localhost:8080/2

2018-12-26 16:36:35.237  INFO 9252 --- [ctor-http-nio-2] c.e.springwebfluxtest.FluxController     : get start2

2018-12-26 16:36:35.240  INFO 9252 --- [ctor-http-nio-2] c.e.springwebfluxtest.FluxController     : get end2

@RequestMapping(value = "/3",produces =MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> flux(){
       Flux<String> result = Flux.fromStream(IntStream.range(1,5).mapToObj(i -> {
           try {
               TimeUnit.SECONDS.sleep(1);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           return "flux"+i;
       }));
       return result;
    }

数据像流一样,一个一个输出

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值