谷粒商城篇章6 ---- P193-P210 ---- 异步&线程池&商品详情【分布式高级篇三】

目录

[1. 异步](#1. 异步)

[1.1 初始化线程的 4 种方式](#1.1 初始化线程的 4 种方式)

[1.1.1 继承 Thread](#1.1.1 继承 Thread)

[1.1.2 实现 Runnable 接口](#1.1.2 实现 Runnable 接口)

[1.1.3 实现 Callable 接口 + FutureTask](#1.1.3 实现 Callable 接口 + FutureTask)

[1.1.4 线程池](#1.1.4 线程池)

[1.1.5 以上 4 种方式总结:](#1.1.5 以上 4 种方式总结:)

[1.2 线程池详解](#1.2 线程池详解)

[1.2.1 初始化线程池的 2 种方式](#1.2.1 初始化线程池的 2 种方式)

[1.2.1.1 使用 Executors 创建](#1.2.1.1 使用 Executors 创建)

[1.2.1.2 使用 ThreadPoolExecutor 创建](#1.2.1.2 使用 ThreadPoolExecutor 创建)

[1.2.1.2.1 ThreadPoolExecutor 线程池的七大参数](#1.2.1.2.1 ThreadPoolExecutor 线程池的七大参数)

[1.2.1.2.2 运行流程](#1.2.1.2.2 运行流程)

[1.2.1.2.3 线程池面试题](#1.2.1.2.3 线程池面试题)

[1.2.2 常见的 4 种线程池](#1.2.2 常见的 4 种线程池)

[1.2.3 使用线程池的好处](#1.2.3 使用线程池的好处)

[1.3 CompletableFuture异步编排](#1.3 CompletableFuture异步编排)

[1.3.1 启动异步任务](#1.3.1 启动异步任务)

[1.3.1.1 runAsync没有返回值](#1.3.1.1 runAsync没有返回值)

[1.3.1.2 supplyAsync有返回值](#1.3.1.2 supplyAsync有返回值)

[1.3.2 完成回调与异常感知](#1.3.2 完成回调与异常感知)

[1.3.2.1 完成回调 whenComplete](#1.3.2.1 完成回调 whenComplete)

[1.3.2.2 异常感知 exceptionally](#1.3.2.2 异常感知 exceptionally)

[1.3.3 handle 最终处理](#1.3.3 handle 最终处理)

[1.3.4 线程串行化](#1.3.4 线程串行化)

[1.3.4.1 thenRunAsync](#1.3.4.1 thenRunAsync)

[1.3.4.2 thenAcceptAsync](#1.3.4.2 thenAcceptAsync)

[1.3.4.3 thenApplyAsync](#1.3.4.3 thenApplyAsync)

[1.3.5 两个任务组合 - 都要完成](#1.3.5 两个任务组合 - 都要完成)

[1.3.5.1 runAfterBothAsync](#1.3.5.1 runAfterBothAsync)

[1.3.5.2 thenAcceptBothAsync](#1.3.5.2 thenAcceptBothAsync)

[1.3.5.3 thenCombineAsync](#1.3.5.3 thenCombineAsync)

[1.3.6 两个任务组合 - 一个完成](#1.3.6 两个任务组合 - 一个完成)

[1.3.6.1 runAfterEitherAsync](#1.3.6.1 runAfterEitherAsync)

[1.3.6.2 acceptEitherAsync](#1.3.6.2 acceptEitherAsync)

[1.3.6.3 applyToEitherAsync](#1.3.6.3 applyToEitherAsync)

[1.3.7 多任务组合](#1.3.7 多任务组合)

[1.3.7.1 allOf](#1.3.7.1 allOf)

[1.3.7.2 anyOf](#1.3.7.2 anyOf)

[2. 商品详情](#2. 商品详情)

[2.1 环境搭建](#2.1 环境搭建)

[2.1.1 SwitchHosts增加配置](#2.1.1 SwitchHosts增加配置)

[2.1.2 nginx配置](#2.1.2 nginx配置)

[2.1.3 配置网关转发](#2.1.3 配置网关转发)

[2.1.4 商品详情页面搭建](#2.1.4 商品详情页面搭建)

[2.1.5 模型抽取](#2.1.5 模型抽取)

[2.1.6 商品详情的业务代码](#2.1.6 商品详情的业务代码)

[2.1.6.1 controller层](#2.1.6.1 controller层)

[2.1.6.2 service层](#2.1.6.2 service层)

[2.1.6.3 获取sku的图片信息](#2.1.6.3 获取sku的图片信息)

[2.1.6.4 获取spu的规格参数](#2.1.6.4 获取spu的规格参数)

[2.1.6.5 获取spu的销售属性组合](#2.1.6.5 获取spu的销售属性组合)

[2.1.7 详情页渲染【P207-P209】](#2.1.7 详情页渲染【P207-P209】)

[2.1.8 异步编排优化](#2.1.8 异步编排优化)

[2.1.8.1 自定义线程池](#2.1.8.1 自定义线程池)

[2.1.8.1.1 引入依赖](#2.1.8.1.1 引入依赖)

[2.1.8.1.2 线程池属性配置类](#2.1.8.1.2 线程池属性配置类)

[2.1.8.1.3 配置文件中配置](#2.1.8.1.3 配置文件中配置)

[2.1.8.1.4 自定义线程池](#2.1.8.1.4 自定义线程池)

[2.1.8.2 异步编排优化商品详情业务](#2.1.8.2 异步编排优化商品详情业务)

[2.1.8.2.1 controller层](#2.1.8.2.1 controller层)

[2.1.8.2.2 service层](#2.1.8.2.2 service层)


1. 异步

1.1 初始化线程的 4 种方式

1.1.1 继承 Thread

java 复制代码
public class ThreadTest {
    public static void main(String[] args) {
        System.out.println("main...start...");
        // 1. 继承Thread类
        Thread01 thread01 = new Thread01();
        thread01.start(); // 启动线程
        System.out.println("main...end...");
    }

    public static class Thread01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }
}

测试结果:

1.1.2 实现 Runnable 接口

java 复制代码
public class ThreadTest {
    
    public static void main(String[] args) {
        System.out.println("main...start...");
         // 1. 继承Thread类
        //Thread01 thread01 = new Thread01();
        //thread01.start(); // 启动线程

        // 2. 实现Runnable接口
        Runnable01 runnable01 = new Runnable01();
        new Thread(runnable01).start();

        System.out.println("main...end...");
    }

    public static class Thread01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }

    public static class Runnable01 implements Runnable{

        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }
}

1.1.3 实现 Callable 接口 + FutureTask

FutureTask的get()方法,阻塞式等待整个线程的执行完成,获取线程执行返回结果。

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        // 1. 继承Thread类
        //Thread01 thread01 = new Thread01();
        //thread01.start(); // 启动线程

        // 2. 实现Runnable接口
        //Runnable01 runnable01 = new Runnable01();
        //new Thread(runnable01).start();

        // 3. 实现Callable接口+FutureTask (可以拿到返回结果,可以处理异常)
        Callable01 callable01 = new Callable01();
        FutureTask<Integer> futureTask = new FutureTask<>(callable01);
        new Thread(futureTask).start();
        // 阻塞式等待整个线程执行完成,获取线程执行返回结果
        Integer integer = futureTask.get();

        System.out.println("main...end..."+integer );
    }

    public static class Thread01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }

    public static class Runnable01 implements Runnable{

        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }

    public static class Callable01 implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
            return i;
        }
    }
}

注意:

Future可以获取异步执行结果;

FutureTask继承了Runnable接口。

1.1.4 线程池

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        // 1. 继承Thread类
        //Thread01 thread01 = new Thread01();
        //thread01.start(); // 启动线程

        // 2. 实现Runnable接口
        //Runnable01 runnable01 = new Runnable01();
        //new Thread(runnable01).start();

        // 3. 实现Callable接口+FutureTask (可以拿到返回结果,可以处理异常)
        //Callable01 callable01 = new Callable01();
        //FutureTask<Integer> futureTask = new FutureTask<>(callable01);
        //new Thread(futureTask).start();
         阻塞式等待整个线程执行完成,获取线程执行返回结果
        //Integer integer = futureTask.get();

        // 在以后的业务代码里面,以上三种启动线程的方式都不用,因为频繁的创建和销毁线程耗费系统资源。【将所有的多线程异步任务都交给线程池执行】
        //new Thread(()-> System.out.println("hello")).start();

        // 4. 线程池  给线程池直接提交业务

        // 当前系统中池只有一两个(例如:核心业务/非核心业务),每个异步任务,提交给线程池让它自己去执行就行
        executorService.execute(new Runnable01());
        System.out.println("main...end...");
    }

    public static class Thread01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }

    public static class Runnable01 implements Runnable{

        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }

    public static class Callable01 implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
            return i;
        }
    }
}

1.1.5 以上 4 种方式总结:

java 复制代码
区别:
1、2不能得到返回值;3可以获取返回值
1、2、3都不能控制资源
4可以控制资源,性能稳定

注意:

  1. 在以后的业务代码里面,以上三种启动线程的方式都不用,因为频繁的创建和销毁线程耗费系统资源,可能导致服务器资源耗尽。【将所有的多线程异步任务都交给线程池执行】

  2. 当前系统中池只有一两个(例如:核心业务/非核心业务),每个异步任务,提交给线程池让它自己去执行就行

1.2 线程池详解

1.2.1 初始化线程池的 2 种方式

1.2.1.1 使用 Executors 创建

java 复制代码
ExecutorService executorService = Executors.newFixedThreadPool(10);

线程池执行任务的两种方式:

executorService.execute(new Runnable01());

Future<?> submit = executorService.submit(new Runnable01());

区别:

execute:参数只能是Runnable,没有返回值。

submit:参数可以是Runnable、Callable,返回值是Future。

1.2.1.2 使用 ThreadPoolExecutor 创建

java 复制代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
            200,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
1.2.1.2.1 ThreadPoolExecutor 线程池的七大参数
  • corePoolSize:核心线程数【一直存在除非(allowCoreThreadTimeOut)】,线程池创建好后就准备就绪,等待任务。
  • maximumPoolSize:最大线程数,控制资源。
  • keepAliveTime:存活时间。如果当前的线程数大于核心线程数,只要线程空闲大于指定的keepAliveTime,就会释放空闲的线程【maximumPoolSize-corePoolSize】。
  • unit:存活时间的时间单位。
  • workQueue:阻塞队列。如果当前对线程的需求超过核心线程数,就会将多余的任务放到阻塞队列。如果有空闲的线程,就会去队列里取出新的任务继续执行。
  • threadFactory:创建线程的工厂。
  • handler:拒绝策略。提交给线程池的任务量超过最大线程池大小+队列长度,按照我们指定的拒绝策略拒绝执行任务。
    • ThreadPoolExecutor.AbortPolicy:抛出 RejectedExecutionException来拒绝新任务的处理。
    • ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务。
    • ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃掉。
    • ThreadPoolExecutor.DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。
1.2.1.2.2 运行流程

1、线程池创建,准备好core数量的核心线程,准备接受任务。

2、core 满了, 就将再进来的任务放入阻塞队列中。 空闲的 core 就会自己去阻塞队列获取任务执行。

3、阻塞队列满了, 就直接开新线程执行, 最大只能开到 max 指定的数量

4、如果线程数开到了 max 的数量, 还有新任务进来, 就会使用 RejectedExecutionHandler拒绝任务。

5、max 都执行好了。 Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自 动销毁。 最终保持到 core 大小 。

1.2.1.2.3 线程池面试题

问题:一个线程池 core 7; max 20 , queue: 50, 100 并发进来怎么分配的;

答案:先有 7 个能直接得到执行, 接下来 50 个进入队列排队, 在多开 13 个继续执行。 现在 70 个被安排上了。 剩下 30 个默认拒绝策略。

1.2.2 常见的 4 种线程池

  • newCachedThreadPool:创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程。
  • newFixedThreadPool:创建一个定长线程池, 可控制线程最大并发数, 超出的线程会在队列中等待。
  • newScheduledThreadPool:创建一个定长线程池, 支持定时及周期性任务执行。
  • newSingleThreadExecutor:创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

1.2.3 使用线程池的好处

  • 降低资源的消耗:通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗。
  • 提高响应速度:因为线程池中的线程数没有超过线程池的最大上限时, 有的线程处于等待分配任务的状态, 当任务来时无需创建新的线程就能执行。
  • 提高线程的可管理性:线程池会根据当前系统特点对池内的线程进行优化处理, 减少创建和销毁线程带来的系统开销。 无限的创建和销毁线程不仅消耗系统资源, 还降低系统的稳定性, 使用线程池进行统一分配。

1.3 CompletableFuture异步编排

在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture, 提供了非常强大的Future 的扩展功能, 可以帮助我们简化异步编程的复杂性, 提供了函数式编程的能力, 可以通过回调的方式处理计算结果, 并且提供了转换和组合 CompletableFuture 的方法。CompletableFuture 类实现了 Future 接口, 所以你还是可以像以前一样通过`get`方法阻塞或者轮询的方式获得结果, 但是这种方式不推荐使用。

CompletableFuture 和 FutureTask 同属于 Future 接口的实现类, 都可以获取线程的执行结果。

1.3.1 启动异步任务

CompletableFuture 提供了四个静态方法来创建一个异步操作。

java 复制代码
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)

注意:

(1)runAsync都是没有返回结果的,supplyAsync都是可以获取返回结果的;

(2)可以传入自定义的线程池,否则就用默认的线程池;

(3)Async代表异步方法。

1.3.1.1 runAsync没有返回值

java 复制代码
public static void main(String[] args) {
     System.out.println("main...start...");
     CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
          System.out.println("当前线程:" + Thread.currentThread().getId());
          int i = 10 / 2;
          System.out.println("运行结果:" + i);
     }, executorService);
     
     System.out.println("main...end...");
}

测试结果:

1.3.1.2 supplyAsync有返回值

java 复制代码
public static void main(String[] args) throws ExecutionException, InterruptedException {
    System.out.println("main...start...");
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("当前线程:" + Thread.currentThread().getId());
        int i = 10 / 2;
        System.out.println("运行结果:" + i);
        return i;
    }, executorService);
    Integer integer = future.get();
    System.out.println("main...end..."+integer);
}

测试结果:

1.3.2 完成回调与异常感知

java 复制代码
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)

public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)

whenComplete可以处理正常和异常的结果, exceptionally 处理异常情况。

whenComplete 和 whenCompleteAsync 的区别:

  • whenComplete: 是执行当前任务的线程执行继续执行 whenComplete 的任务。
  • whenCompleteAsync: 是执行把 whenCompleteAsync 这个任务继续提交给线程池
    来进行执行。

注意:方法不以 Async 结尾, 意味着 Action 使用相同的线程执行, 而 Async 可能会使用其他线程执行(如果是使用相同的线程池, 也可能会被同一个线程选中执行)

1.3.2.1 完成回调 whenComplete

  • 无异常情况
java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        //CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        //    System.out.println("当前线程:" + Thread.currentThread().getId());
        //    int i = 10 / 2;
        //    System.out.println("运行结果:" + i);
        //}, executorService);
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 1;
            System.out.println("运行结果:" + i);
            return i;
        }, executorService).whenComplete((result,exception)->{
            // 可以感知到异常,但无法修改返回数据
            System.out.println("异步任务完成了...返回值为:"+result+";异常为:"+exception);
        });
        //Integer integer = future.get();
        System.out.println("main...end...");
    }
}
  • 有异常情况
java 复制代码
将 int i = 10/1; 改为 int i = 10/0;

1.3.2.2 异常感知 exceptionally

  • 无异常情况(不会进入exceptionally)
java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        //CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        //    System.out.println("当前线程:" + Thread.currentThread().getId());
        //    int i = 10 / 2;
        //    System.out.println("运行结果:" + i);
        //}, executorService);
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }, executorService).whenComplete((result,exception)->{
            // 可以感知到异常,但无法修改返回数据
            System.out.println("异步任务完成了...返回值为:"+result+";异常为:"+exception);
        }).exceptionally(throwable -> {
            // 可以感知异常,同时返回默认值
            return 10;
        });
        Integer integer = future.get();
        System.out.println("main...end..."+integer);
    }
}
  • 有异常情况
java 复制代码
将 int i = 10/2; 改为 int i = 10/0;

1.3.3 handle 最终处理

一般用handle,因为whencomplete如果异常不能给定默认返回结果,需要再调用exceptionally,而handle可以。

handle方法作用:获得前一任务的返回值【handleAsync可以是异步执行的】,也可以处理上一任务的异常,调用exceptionally修改前一任务的返回值【只有在异常情况时给一个默认返回值】而handle方法可以简化操作。

  • 无异常情况
java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        //CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        //    System.out.println("当前线程:" + Thread.currentThread().getId());
        //    int i = 10 / 2;
        //    System.out.println("运行结果:" + i);
        //}, executorService);
        /**
         * 方法执行完成后的感知
         */
        //CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        //    System.out.println("当前线程:" + Thread.currentThread().getId());
        //    int i = 10 / 2;
        //    System.out.println("运行结果:" + i);
        //    return i;
        //}, executorService).whenComplete((result,exception)->{
        //    // 可以感知到异常,但无法修改返回数据
        //    System.out.println("异步任务完成了...返回值为:"+result+";异常为:"+exception);
        //}).exceptionally(throwable -> {
        //    // 可以感知异常,同时返回默认值
        //    return 10;
        //});
        /**
         *  方法执行完成后的处理
         */
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 4;
            System.out.println("运行结果:" + i);
            return i;
        }).handle((res, thr) -> {
            if (res != null) {
                return res * 2;
            }
            if (thr != null) {
                return 10;
            }
            return 0;
        });
        // R apply(T t, U u);
        Integer integer = future.get();
        System.out.println("main...end..."+integer);
    }
}
  • 有异常情况
java 复制代码
将 int i = 10/4; 改为 int i = 10/0;

1.3.4 线程串行化

java 复制代码
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)

public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)

thenApply 方法: 当一个线程依赖另一个线程时, 获取上一个任务返回的结果, 并返回当前

任务的返回值。
thenAccept 方法: 消费处理结果。 接收任务的处理结果, 并消费处理, 无返回结果。
thenRun 方法: 只要上面的任务执行完成, 就开始执行 thenRun, 只是处理完任务后, 执行
thenRun 的后续操作

带有 Async 默认是异步执行的。 同之前。

以上都要前置任务成功完成。
Function<? super T,? extends U>
T : 上一个任务返回结果的类型。
U: 当前任务的返回值类型 。

1.3.4.1 thenRunAsync

java 复制代码
public static void main(String[] args) throws ExecutionException, InterruptedException {
    System.out.println("main...start...");
    CompletableFuture.supplyAsync(() -> {
        System.out.println("当前线程:" + Thread.currentThread().getId());
        int i = 10 / 4;
        System.out.println("运行结果:" + i);
        return i;
    }).thenRunAsync(()->{
        // 不接收上一步结果,无返回值
        System.out.println("任务2启动了...");
    },executorService);
    System.out.println("main...end...");
}

执行结果:

1.3.4.2 thenAcceptAsync

java 复制代码
public static void main(String[] args) throws ExecutionException, InterruptedException {
    System.out.println("main...start...");
    CompletableFuture.supplyAsync(() -> {
        System.out.println("当前线程:" + Thread.currentThread().getId());
        int i = 10 / 4;
        System.out.println("运行结果:" + i);
        return i;
    }).thenAcceptAsync(res -> {
        // thenRunAsync:不接收上一步结果,无返回值
        // thenAcceptAsync:接收上一步结果,无返回值  void accept(T t);
        System.out.println("任务2启动了..." + res);
    }, executorService);
    System.out.println("main...end...");
}

执行结果:

1.3.4.3 thenApplyAsync

java 复制代码
public static void main(String[] args) throws ExecutionException, InterruptedException {
    System.out.println("main...start...");
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("当前线程:" + Thread.currentThread().getId());
        int i = 10 / 4;
        System.out.println("运行结果:" + i);
        return i;
    }).thenApplyAsync(res -> {
        // thenRunAsync:不接收上一步结果,无返回值
        // thenAcceptAsync:接收上一步结果,无返回值  void accept(T t);
        // thenApplyAsync:接收上一步结果,有返回值  R apply(T t);
        System.out.println("任务2启动了..." + res);
        return "Hello" + res;
    }, executorService);
    System.out.println("main...end..."+future.get());
}

执行结果:

1.3.5 两个任务组合 - 都要完成

java 复制代码
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn, Executor executor)

public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T, ? super U> action, Executor executor)

public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,
Executor executor)

两个任务必须都完成, 触发该任务。
thenCombine : 组合两个 future, 获取两个 future 的返回结果, 并返回当前任务的返回值
thenAcceptBoth : 组合两个 future, 获取两个 future 任务的返回结果, 然后处理任务, 没有

返回值。
runAfterBoth : 组合两个 future, 不需要获取 future 的结果, 只需两个 future 处理完任务后,

处理该任务。

1.3.5.1 runAfterBothAsync

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        /**
         * 两个任务组合-都要完成
         */
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程:"+Thread.currentThread().getId());
            int i = 10/4;
            System.out.println("任务1结束。。。");
            return i;
        }, executorService);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程:"+Thread.currentThread().getId());
            System.out.println("任务2结束。。。");
            return "Hello";
        }, executorService);
        future01.runAfterBothAsync(future02,()->{
            System.out.println("任务3开始");
        },executorService);
        System.out.println("main...end...");
    }
}

执行结果:

1.3.5.2 thenAcceptBothAsync

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        /**
         * 两个任务组合-都要完成
         */
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程:"+Thread.currentThread().getId());
            int i = 10/4;
            System.out.println("任务1结束。。。");
            return i;
        }, executorService);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程:"+Thread.currentThread().getId());
            System.out.println("任务2结束。。。");
            return "Hello";
        }, executorService);
        //future01.runAfterBothAsync(future02,()->{
        //    System.out.println("任务3开始");
        //},executorService);
        future01.thenAcceptBothAsync(future02,(f1,f2)->{
            //void accept(T t, U u);
            System.out.println("任务3开始。。。之前的结果:"+f1+"->"+f2);
        },executorService);
        System.out.println("main...end...");
    }
}

执行结果:

1.3.5.3 thenCombineAsync

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        /**
         * 两个任务组合-都要完成
         */
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程:"+Thread.currentThread().getId());
            int i = 10/4;
            System.out.println("任务1结束。。。");
            return i;
        }, executorService);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程:"+Thread.currentThread().getId());
            System.out.println("任务2结束。。。");
            return "Hello";
        }, executorService);
        //future01.runAfterBothAsync(future02,()->{
        //    System.out.println("任务3开始");
        //},executorService);
        //future01.thenAcceptBothAsync(future02,(f1,f2)->{
        //    //void accept(T t, U u);
        //    System.out.println("任务3开始。。。之前的结果:"+f1+"->"+f2);
        //},executorService);
        CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
            return f1 + "->" + f2 + "->相柳"; // R apply(T t, U u);
        }, executorService);
        System.out.println("main...end..."+future.get());
    }
}

执行结果:

1.3.6 两个任务组合 - 一个完成

java 复制代码
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn)
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)

public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor)

public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor)

当两个任务中, 任意一个 future 任务完成的时候, 执行任务。
applyToEither : 两个任务有一个执行完成, 获取它的返回值, 处理任务并有新的返回值。
acceptEither : 两个任务有一个执行完成, 获取它的返回值, 处理任务, 没有新的返回值。
runAfterEither : 两个任务有一个执行完成, 不需要获取 future 的结果, 处理任务, 也没有返

回值。

1.3.6.1 runAfterEitherAsync

线程1执行完成,线程2睡了3秒,达成runAfterEitherAsync执行条件

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程:"+Thread.currentThread().getId());
            int i = 10/4;
            System.out.println("任务1结束。。。");
            return i;
        }, executorService);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程:"+Thread.currentThread().getId());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2结束。。。");
            return "Hello";
        }, executorService);
  
        /**
         * 两个任务,只要有一个完成,我们就执行任务3
         */
        future01.runAfterEitherAsync(future02,()->{
            System.out.println("任务3开始。。。之前的结果:");
        },executorService);
        System.out.println("main...end...");
    }
}

执行结果:

1.3.6.2 acceptEitherAsync

两个任务的返回值类型要一致,这里全改为Object

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程:"+Thread.currentThread().getId());
            int i = 10/4;
            System.out.println("任务1结束。。。");
            return i;
        }, executorService);
        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程:"+Thread.currentThread().getId());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2结束。。。");
            return "Hello";
        }, executorService);
        /**
         * 两个任务,只要有一个完成,我们就执行任务3
         */
        //future01.runAfterEitherAsync(future02,()->{
        //    System.out.println("任务3开始。。。之前的结果:");
        //},executorService);
        future01.acceptEitherAsync(future02,res->{
            System.out.println("任务3开始。。。之前的结果:"+res);
        },executorService);
        System.out.println("main...end...");
    }
}

执行结果:

1.3.6.3 applyToEitherAsync

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程:"+Thread.currentThread().getId());
            int i = 10/4;
            System.out.println("任务1结束。。。");
            return i;
        }, executorService);
        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程:"+Thread.currentThread().getId());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2结束。。。");
            return "Hello";
        }, executorService);
        /**
         * 两个任务,只要有一个完成,我们就执行任务3
         */
        //future01.runAfterEitherAsync(future02,()->{
        //    System.out.println("任务3开始。。。之前的结果:");
        //},executorService);
        //future01.acceptEitherAsync(future02,res->{
        //    System.out.println("任务3开始。。。之前的结果:"+res);
        //},executorService);
        CompletableFuture<String> future = future01.applyToEitherAsync(future02, res -> {
            System.out.println("任务3开始。。。之前的结果:"+res);
            return res.toString() + "九头蛇";
        }, executorService);
        System.out.println("main...end..."+future.get());
    }
}

执行结果:

1.3.7 多任务组合

java 复制代码
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

allOf : 等待所有任务完成
anyOf: 只要有一个任务完成

1.3.7.1 allOf

让查询商品介绍睡3秒钟,便于观察。

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息");
            return "hello.jpg";
        }, executorService);
        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的属性");
            return "白色+256G";
        }, executorService);
        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询商品介绍");
            return "小米";
        }, executorService);

        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
        allOf.get();// 等待所有任务完成

        System.out.println("main...end..."+futureImg.get()+"->"+futureAttr.get()+">"+futureDesc.get());
    }
}

执行结果:

1.3.7.2 anyOf

java 复制代码
public class ThreadTest {
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start...");
        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息");
            return "hello.jpg";
        }, executorService);
        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的属性");
            return "白色+256G";
        }, executorService);
        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询商品介绍");
            return "小米";
        }, executorService);

        //CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
        //allOf.get();// 等待所有任务完成
        //System.out.println("main...end..."+futureImg.get()+"->"+futureAttr.get()+">"+futureDesc.get());
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
        System.out.println("main...end..."+anyOf.get());
    }
}

执行结果:

2. 商品详情

2.1 环境搭建

2.1.1 SwitchHosts增加配置

添加商品详情的域名与ip映射:172.1.11.10 item.gulimall.com

2.1.2 nginx配置

配置域名 item.gulimall.com,这里之前应该配置过,如下:

server_name gulimall.com *.gulimall.com;

bash 复制代码
# 打开nginx配置文件
vi /root/docker/nginx/conf/conf.d/gulimall.conf

2.1.3 配置网关转发

将商品详情域名(item.gulimall.com)访问转发的商品服务。

XML 复制代码
- id: gulimall_host_route
  uri: lb://gulimall-product
  predicates:
    # 由以下的主机域名访问转发到商品服务
    - Host=gulimall.com,item.gulimall.com

2.1.4 商品详情页面搭建

java 复制代码
1. 将shangpinxiangqing.html复制到gulimall-product/src/main/resources/templates,并改名为item.html;
2. 将静态资源放到nginx的/root/docker/nginx/html/static/item/路径下;
3. 将item.html页面的href和src以/static/item/开头。
java 复制代码
4. 在gulimall-product/src/main/java/com/wen/gulimall/product/web/下编写ItemController.java;
5. 启动网关服务、商品服务、搜索服务,进行测试:
    1)在搜索服务点击某个商品发现无法跳转至商品详情服务,修改搜索服务list.html,如下:
        通过右键商品审查元素,发现需要修改class="da"的图片跳转路径,如下:
<p class="da">
    <a th:href="|http://item.gulimall.com/${product.skuId}.html|" >
        <img th:src="${product.skuImg}" class="dim">
    </a>
</p>
java 复制代码
@Controller
public class ItemController {

    @GetMapping("/{skuId}.html")
    public String item(@PathVariable Long skuId){
        System.out.println("准备查询:"+skuId+"的详情!");
        return "item";
    }
}

2.1.5 模型抽取

封装页面需要的详情数据模型,如下:

java 复制代码
@Data
public class SkuItemVo {
    // 获取sku的基本信息 pms_sku_info
    private SkuInfoEntity info;

    // 获取sku的图片信息 pms_sku_images
    private List<SkuImagesEntity> images;

    // 获取spu的销售属性组合
    private List<SkuItemSaleAttrVo> saleAttr;

    // 获取spu的介绍
    private SpuInfoDescEntity desc;

    // 获取spu的规格参数信息
    private List<SpuItemAttrGroupVo> groupAttrs;

}
java 复制代码
@ToString
@Data
public class SpuItemAttrGroupVo {
    private String groupName;
    private List<Attr> attrs;
}
java 复制代码
// 之前代码中存在
@Data
public class Attr {
    private Long attrId;
    private String attrName;
    private String attrValue;
}
java 复制代码
@ToString
@Data
public class SkuItemSaleAttrVo {
    private Long attrId;
    private String attrName;
    private List<AttrValueAndWithSkuIdVo> attrValues;
}
java 复制代码
@Data
public class AttrValueAndWithSkuIdVo {
    // 属性值
    private String attrValue;
    // 属性编号,属性名,属性值对应的所有skuId
    private String skuIds;
}

2.1.6 商品详情的业务代码

2.1.6.1 controller层

java 复制代码
@Controller
public class ItemController {
    @Resource
    private SkuInfoService skuInfoService;

    @GetMapping("/{skuId}.html")
    public String item(@PathVariable Long skuId, Model model){
        // 获取sku的基本信息 pms_sku_info
        // 获取sku的图片信息 pms_sku_images
        // 获取spu的销售属性组合
        // 获取spu的介绍
        // 获取spu的规格参数信息
        SkuItemVo vo = skuInfoService.item(skuId);
        model.addAttribute("item",vo);
        return "item";
    }
}

2.1.6.2 service层

java 复制代码
public interface SkuInfoService extends IService<SkuInfoEntity> {

    ...

    SkuItemVo item(Long skuId);
}
java 复制代码
@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {

    @Resource
    private SkuImagesService skuImagesService;
    @Resource
    private SpuInfoDescService spuInfoDescService;
    @Resource
    private AttrGroupService attrGroupService;
    @Resource
    private SkuSaleAttrValueService skuSaleAttrValueService;

    ...


    @Override
    public SkuItemVo item(Long skuId) {
        SkuItemVo skuItemVo = new SkuItemVo();
        // 获取sku的基本信息 pms_sku_info
        SkuInfoEntity info = getById(skuId);
        skuItemVo.setInfo(info);
        Long spuId = info.getSpuId();
        Long catalogId = info.getCatalogId();

        // 获取sku的图片信息 pms_sku_images
        List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
        skuItemVo.setImages(images);
        // 获取spu的销售属性组合
        List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(spuId);
        skuItemVo.setSaleAttr(saleAttrVos);
        // 获取spu的介绍
        SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
        skuItemVo.setDesc(spuInfoDescEntity);

        // 获取spu的规格参数信息
        List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(spuId,catalogId);
        skuItemVo.setGroupAttrs(attrGroupVos);
        return skuItemVo;
    }

}

2.1.6.3 获取sku的图片信息

java 复制代码
public interface SkuImagesService extends IService<SkuImagesEntity> {

    ...

    List<SkuImagesEntity> getImagesBySkuId(Long skuId);
}
java 复制代码
@Service("skuImagesService")
public class SkuImagesServiceImpl extends ServiceImpl<SkuImagesDao, SkuImagesEntity> implements SkuImagesService {

    ...

    @Override
    public List<SkuImagesEntity> getImagesBySkuId(Long skuId) {
        List<SkuImagesEntity> imagesEntities = this.baseMapper.selectList(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));
        return imagesEntities;
    }

}

2.1.6.4 获取spu的规格参数

java 复制代码
public interface AttrGroupService extends IService<AttrGroupEntity> {

    ...

    List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId);
}
java 复制代码
@Service("attrGroupService")
public class AttrGroupServiceImpl extends ServiceImpl<AttrGroupDao, AttrGroupEntity> implements AttrGroupService {

   ...

    @Override
    public List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId) {
        List<SpuItemAttrGroupVo> vos = this.baseMapper.getAttrGroupWithAttrsBySpuId(spuId,catalogId);
        return vos;
    }
}
java 复制代码
@Mapper
public interface AttrGroupDao extends BaseMapper<AttrGroupEntity> {

    List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(@Param("spuId") Long spuId, @Param("catalogId") Long catalogId);

}
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.wen.gulimall.product.dao.AttrGroupDao">

    <resultMap id="spuItemAttrGroupVo" type="com.wen.gulimall.product.vo.SpuItemAttrGroupVo">
        <result property="groupName" column="groupName"/>
        <collection property="attrs" ofType="com.wen.gulimall.product.vo.Attr">
            <result property="attrName" column="attrName"/>
            <result property="attrValue" column="attrValue"/>
        </collection>
    </resultMap>
    <select id="getAttrGroupWithAttrsBySpuId" resultMap="spuItemAttrGroupVo">
        SELECT
            pav.spu_id,
            ag.attr_group_id,
            ag.attr_group_name groupName,
            attr.attr_name attrName,
            pav.attr_value attrValue
        FROM pms_attr_group ag LEFT JOIN pms_attr_attrgroup_relation aar ON (ag.attr_group_id=aar.attr_group_id)
        LEFT JOIN pms_attr attr ON (aar.attr_id=attr.attr_id)
        LEFT JOIN pms_product_attr_value pav ON (attr.attr_id = pav.attr_id)
        WHERE ag.catelog_id = #{catalogId} AND pav.spu_id = #{spuId}
    </select>

</mapper>

2.1.6.5 获取spu的销售属性组合

java 复制代码
public interface SkuSaleAttrValueService extends IService<SkuSaleAttrValueEntity> {

    ...

    List<SkuItemSaleAttrVo> getSaleAttrsBySpuId(Long spuId);
}
java 复制代码
@Service("skuSaleAttrValueService")
public class SkuSaleAttrValueServiceImpl extends ServiceImpl<SkuSaleAttrValueDao, SkuSaleAttrValueEntity> implements SkuSaleAttrValueService {

    ...

    @Override
    public List<SkuItemSaleAttrVo> getSaleAttrsBySpuId(Long spuId) {
        List<SkuItemSaleAttrVo> vos = this.baseMapper.getSaleAttrsBySpuId(spuId);
        return vos;
    }

}
java 复制代码
@Mapper
public interface SkuSaleAttrValueDao extends BaseMapper<SkuSaleAttrValueEntity> {

    List<SkuItemSaleAttrVo> getSaleAttrsBySpuId(@Param("spuId") Long spuId);
}
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.wen.gulimall.product.dao.SkuSaleAttrValueDao">

     <resultMap id="skuItemSaleAttrVo" type="com.wen.gulimall.product.vo.SkuItemSaleAttrVo">
        <result property="attrId" column="attr_id"/>
        <result property="attrName" column="attr_name"/>
        <collection property="attrValues" ofType="com.wen.gulimall.product.vo.AttrValueAndWithSkuIdVo">
            <result property="attrValue" column="attr_value"/>
            <result property="skuIds" column="sku_ids"/>
        </collection>
    </resultMap>
    <select id="getSaleAttrsBySpuId" resultMap="skuItemSaleAttrVo">
      SELECT
        ssav.attr_id,
        ssav.attr_name,
        ssav.attr_value,
        GROUP_CONCAT(DISTINCT info.sku_id) sku_ids
      FROM pms_sku_info info LEFT JOIN pms_sku_sale_attr_value ssav ON info.sku_id=ssav.sku_id
      WHERE info.spu_id=#{spuId}
      GROUP BY ssav.attr_id,ssav.attr_name,ssav.attr_value
    </select>
</mapper>

2.1.7 详情页渲染【P207-P209】

html 复制代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

	<head>
		<meta charset="UTF-8">
		<title></title>
		<link rel="stylesheet" type="text/css" href="/static/item/scss/shop.css" />
		<link rel="stylesheet" type="text/css" href="/static/item/scss/jd.css"/>
		<link rel="stylesheet" href="/static/item/scss/header.css" />
		<link rel="stylesheet" type="text/css" href="/static/item/bootstrap/css/bootstrap.css"/>
	</head>
	<body>
		<div id="max">
		<header>
			<!--品牌官方网站-->
					<div class="min">
						<ul class="header_ul_left">
							<li class="glyphicon glyphicon-home"> <a href="/static/item/shouye.html" class="aa">京东首页</a></li>
							<li class="glyphicon glyphicon-map-marker"> <a href="/static/item/javascript:;">北京</a>
								<ol id="beijing">
									<li style="background: red;"><a href="javascript:;" style="color: white;">北京</a></li>
									<li><a href="javascript:;">上海</a></li>
									<li><a href="javascript:;">天津</a></li>
									<li><a href="javascript:;">重庆</a></li>
									<li><a href="javascript:;">河北</a></li>
									<li><a href="javascript:;">山西</a></li>
									<li><a href="javascript:;">河南</a></li>
									<li><a href="javascript:;">辽宁</a></li>
									<li><a href="javascript:;">吉林</a></li>
									<li><a href="javascript:;">黑龙江</a></li>
									<li><a href="javascript:;">内蒙古</a></li>
									<li><a href="javascript:;">江苏</a></li>
									<li><a href="javascript:;">山东</a></li>
									<li><a href="javascript:;">安徽</a></li>
									<li><a href="javascript:;">浙江</a></li>
									<li><a href="javascript:;">福建</a></li>
									<li><a href="javascript:;">湖北</a></li>
									<li><a href="javascript:;">湖南</a></li>
									<li><a href="javascript:;">广东</a></li>
									<li><a href="javascript:;">广西</a></li>
									<li><a href="javascript:;">江西</a></li>
									<li><a href="javascript:;">四川</a></li>
									<li><a href="javascript:;">海南</a></li>
									<li><a href="javascript:;">贵州</a></li>
									<li><a href="javascript:;">云南</a></li>
									<li><a href="javascript:;">西藏</a></li>
									<li><a href="javascript:;">陕西</a></li>
									<li><a href="javascript:;">甘肃</a></li>
									<li><a href="javascript:;">青海</a></li>
									<li><a href="javascript:;">宁夏</a></li>
									<li><a href="javascript:;">新疆</a></li>
									<li><a href="javascript:;">港澳</a></li>
									<li><a href="javascript:;">台湾</a></li>
									<li><a href="javascript:;">钓鱼岛</a></li>
									<li><a href="javascript:;">海外</a></li>
								</ol>
							</li>
						</ul>
						<ul class="header_ul_right">
							<li style="border: 0;"><a href="../登录页面\index.html" class="aa">你好,请登录</a></li>
							<li><a href="../注册页面\index.html" style="color: red;">免费注册</a> |</li>
							<li><a href="javascript:;" class="aa">我的订单</a> |</li>
							<li class="jingdong"><a href="javascript:;">我的京东</a><span class="glyphicon glyphicon-menu-down">|</span>
								<ol class="jingdong_ol">
									<li><a href="javascript:;">待处理订单</a></li>
									<li><a href="javascript:;">消息</a></li>
									<li><a href="javascript:;">返修退换货</a></li>
									<li><a href="javascript:;">我的回答</a></li>
									<li><a href="javascript:;">降价商品</a></li>
									<li><a href="javascript:;">我的关注</a></li>
									<li style="width: 100%;height: 1px;background: lavender;margin-top: 15px;"></li>
									<li style="margin-top: 0;"><a href="javascript:;">我的京豆</a></li>
									<li style="margin-top: 0;"><a href="javascript:;">我的优惠券</a></li>
									<li style="margin-bottom: 10px;"><a href="javascript:;">我的白条</a></li>

								</ol>
							</li>

							<li><a href="javascript:;" class="aa">京东会员</a> |</li>
							<li><a href="javascript:;" class="aa">企业采购</a> |</li>
							<li class="fuwu"><a href="javascript:;">客户服务</a><span class="glyphicon glyphicon-menu-down"></span> |
								<ol class="fuwu_ol">
									<h6>客户</h6>
									<li><a href="javascript:;">帮助中心</a></li>
									<li><a href="javascript:;">售后服务</a></li>
									<li><a href="javascript:;">在线客服</a></li>
									<li><a href="javascript:;">意见建议</a></li>
									<li><a href="javascript:;">电话客服</a></li>
									<li><a href="javascript:;">客服邮箱</a></li>
									<li style="margin-bottom: -5px;"><a href="javascript:;">金融咨询</a></li>
									<li style="margin-bottom: -5px;"><a href="javascript:;">售全球客服</a></li>
									<h6 style="border-top: 1px dashed darkgray;height: 30px;line-height: 30px;">商户</h6>
									<li style="margin-top: -5px;"><a href="javascript:;">合作招商</a></li>
									<li style="margin-top: -5px;"><a href="javascript:;">学习中心</a></li>
									<li><a href="javascript:;">商家后台</a></li>
									<li><a href="javascript:;">京麦工作台</a></li>
									<li><a href="javascript:;">商家帮助</a></li>
									<li><a href="javascript:;">规则平台</a></li>
								</ol>
							</li>
							<li class="daohang"><a href="javascript:;">网站导航</a><span class="glyphicon glyphicon-menu-down"></span> |
								<ol class="daohang_ol">
									<li style="width: 34%;">
										<h5>特色主题</h5>
										<p>
											<a href="javascript:;">京东试用</a>
											<a href="javascript:;">京东金融</a>
											<a href="javascript:;">全球售</a>
											<a href="javascript:;">国际站</a>
										</p>
										<p>
											<a href="javascript:;">京东会员</a>
											<a href="javascript:;">京东预售</a>
											<a href="javascript:;">买什么</a>
											<a href="javascript:;">俄语站</a>
										</p>
										<p>
											<a href="javascript:;">装机大师</a>
											<a href="javascript:;">0元评测</a>
											<a href="javascript:;">定期送</a>
											<a href="javascript:;">港澳售</a>
										</p>
										<p>
											<a href="javascript:;">优惠券</a>
											<a href="javascript:;">秒杀</a>
											<a href="javascript:;">闪购</a>
											<a href="javascript:;">印尼站</a>
										</p>
										<p>
											<a href="javascript:;">京东金融科技</a>
											<a href="javascript:;">In货推荐</a>
											<a href="javascript:;">陪伴计划</a>
											<a href="javascript:;">出海招商</a>
										</p>
									</li>
									<li>
										<h5>行业频道</h5>
										<p>
											<a href="javascript:;" class="aa_2">手机</a>
											<a href="javascript:;" class="aa_2">智能数码</a>
											<a href="javascript:;" class="aa_2">玩3C</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">电脑办公</a>
											<a href="javascript:;" class="aa_2">家用电器</a>
											<a href="javascript:;" class="aa_2">京东智能</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">服装城</a>
											<a href="javascript:;" class="aa_2">美妆馆</a>
											<a href="javascript:;" class="aa_2">家装城</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">母婴</a>
											<a href="javascript:;" class="aa_2">食品</a>
											<a href="javascript:;" class="aa_2">运动户外</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">农资频道</a>
											<a href="javascript:;" class="aa_2">整车</a>
											<a href="javascript:;" class="aa_2">图书</a>
										</p>
									</li>
									<li>
										<h5>生活服务</h5>
										<p>
											<a href="javascript:;" class="aa_2">京东众筹</a>
											<a href="javascript:;" class="aa_2">白条</a>
											<a href="javascript:;" class="aa_2">京东金融APP</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">京东小金库</a>
											<a href="javascript:;" class="aa_2">理财</a>
											<a href="javascript:;" class="aa_2">智能家电</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">话费</a>
											<a href="javascript:;" class="aa_2">水电煤</a>
											<a href="javascript:;" class="aa_2">彩票</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">旅行</a>
											<a href="javascript:;" class="aa_2">机票酒店</a>
											<a href="javascript:;" class="aa_2">电影票</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">京东到家</a>
											<a href="javascript:;" class="aa_2">京东众测</a>
											<a href="javascript:;" class="aa_2">游戏</a>
										</p>
									</li>
									<li style="border: 0;">
										<h5>更多精选</h5>
										<p>
											<a href="javascript:;" class="aa_2">合作招商</a>
											<a href="javascript:;" class="aa_2">京东通信</a>
											<a href="javascript:;" class="aa_2">京东E卡</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">企业采购</a>
											<a href="javascript:;" class="aa_2">服务市场</a>
											<a href="javascript:;" class="aa_2">办公生活馆</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">乡村招募</a>
											<a href="javascript:;" class="aa_2">校园加盟</a>
											<a href="javascript:;" class="aa_2">京友邦</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">京东社区</a>
											<a href="javascript:;" class="aa_2">智能社区</a>
											<a href="javascript:;" class="aa_2">游戏社区</a>
										</p>
										<p>
											<a href="javascript:;" class="aa_2">知识产权维权</a>
											<a href="javascript:;" class="aa_2"></a>
											<a href="javascript:;" class="aa_2"></a>
										</p>
									</li>
								</ol>
							</li>
							<li class="sjjd" style="border: 0;"><a href="javascript:;" class="aa">手机京东</a>
								<div class="er">
									<div class="er_1">
										<div class="er_1_1">
											<h6><a href="#">手机京东</a></h6>
											<p>新人专享大礼包</p>

										</div>
									</div>
									<div class="er_1">
										<div class="er_1_1">
											<h6><a href="#">关注京东微信</a></h6>
											<p>微信扫一扫关注新粉最高180元惊喜礼包</p>
										</div>
									</div>
									<!--我的理财-->
									<div class="er_1" style="border: 0;">
										<img src="/static/item/img/5874a555Ne8192324.jpg"/>
										<div class="er_1_1">
											<h6><a href="#">京东金融客户端</a></h6>
											<p>新人专享大礼包</p>
											<div><a href="#"><img src="/static/item/img/11.png"/></a><a href="#"><img src="/static/item/img/22.png"/></a></div>
										</div>
									</div>
								</div>
							</li>
						</ul>
					</div>
				</header>
				<nav>
				<div class="nav_min">
					<div class="nav_top">
						<div class="nav_top_one"><a href="#"><img src="/static/item/img/111.png"/></a></div>
						<div class="nav_top_two"><input type="text"/><button>搜索</button></div>
						<div class="nav_top_three"><a href="../JD_Shop/One_JDshop.html">我的购物车</a><span class="glyphicon glyphicon-shopping-cart"></span>
							<div class="nav_top_three_1">
								<img src="/static/item/img/44.png"/>购物车还没有商品,赶紧选购吧!
							</div>
						</div>
					</div>
					<div class="nav_down">
						<ul class="nav_down_ul">
							<li class="nav_down_ul_1" style="width: 24%;float: left;"><a href="javascript:;">全部商品分类</a>

							</li>
							<li class="ul_li"><a href="javascript:;">服装城</a></li>
							<li class="ul_li"><a href="javascript:;">美妆馆</a></li>
							<li class="ul_li"><a href="javascript:;">超市</a></li>
							<li class="ul_li" style="border-right: 1px solid lavender;"><a href="javascript:;">生鲜</a></li>
							<li class="ul_li"><a href="javascript:;">全球购</a></li>
							<li class="ul_li"><a href="javascript:;">闪购</a></li>
							<li class="ul_li" style="border-right: 1px solid lavender;"><a href="javascript:;">拍卖</a></li>
							<li class="ul_li"><a href="javascript:;">金融</a></li>
						</ul>
					</div>
				</div>
			</nav>

				</div>


			<div class="crumb-wrap">
			<div class="w">
				<div class="crumb">
					<div class="crumb-item">
						<a href="">手机</a>
					</div>
					<div class="crumb-item sep">></div>
					<div class="crumb-item">
						<a href="">手机通讯</a>
					</div>
					<div class="crumb-item sep">></div>
					<div class="crumb-item">
						<a href="">手机</a>
					</div>
					<div class="crumb-item sep">></div>
					<div class="crumb-item">
						<div class="crumb-item-one">
							华为 (HUAWEI)
							<img src="/static/item/img/4a79b87a68623d4e8a73aff3e25fa99b.png" alt="" class="img" />
							<div class="crumb-item-two ">
								<div class="crumb-item-con clear">
									<ul class="con-ul">
										<li>
											<img src="/static/item/img/5825a5a6Nde8ecb75.jpg" alt="" />
										</li>
										<li>
											<p>
												荣耀8青春版 全网通标配 3GB+32GB 幻海蓝
											</p>
											<p>
												¥1099.00
											</p>
										</li>
									</ul>
									<ul class="con-ul">
										<li>
											<img src="/static/item/img/5919637aN271a1301.jpg" alt="" />
										</li>
										<li>
											<p>
												荣耀8青春版 全网通标配 3GB+32GB 幻海蓝
											</p>
											<p>
												¥1099.00
											</p>
										</li>
									</ul>
									<ul class="con-ul">
										<li>
											<img src="/static/item/img/599a806bN9d829c1c.jpg" alt="" />
										</li>
										<li>
											<p>
												荣耀8青春版 全网通标配 3GB+32GB 幻海蓝
											</p>
											<p>
												¥1099.00
											</p>
										</li>
									</ul>
								</div>
								<div class="crumb-item-cons clear">
									<ul>
										<li>华为(huawei)</li>
										<li>小米(xiaomi)</li>
										<li>APPle</li>
										<li>魅族(meizu)</li>
										<li>锤子</li>

									</ul>
									<ul>
										<li>三星</li>
										<li>vivo</li>
										<li>飞利浦</li>
										<li>360</li>
										<li>更多>></li>

									</ul>
								</div>
							</div>

						</div>

					</div>
					<div class="crumb-item sep">></div>
					<div class="crumb-item">
						华为Mate 10
					</div>
				</div>

				<div class="contact">
					<ul class="contact-ul">
						<li>
							<a href="#">
								华为京东自营官方旗舰店
							</a>

							<span class="contact-sp">
								<span class="contact-sp1">
								JD
							</span>
							<span class="contact-sp2">
								自营
							    </span>
							</span>
						</li>
						<li>
							<a href="#">
								<img src="/static/item/img/f5831b9848b32440b381bcd30a3d96c7.png" alt="" /> 联系供应商
							</a>
						</li>
						<li>
							<a href="#">
								<img src="/static/item/img/81a6326edc82d343a5a8860a6970f93b.png" alt="" /> JIMI
							</a>
						</li>
						<li>
							<a href="#">
								<img src="/static/item/img/a400e3d61d5645459f769b00d9f431e7.png" alt="" /> 关注店铺
							</a>
						</li>
					</ul>
					<div class="contact-one">
						<ul>
							<li>客服</li>
							<li><img src="/static/item/img/f5831b9848b32440b381bcd30a3d96c7.png" alt="" />留言</li>
							<li><img src="/static/item/img/81a6326edc82d343a5a8860a6970f93b.png" alt="" />JIMI智能</li>
							<li>
								<img src="/static/item/img/1466134037230.jpg" class="contact-img" />
								<p>手机下单</p>
							</li>

						</ul>
						<div class="contact-two">
							<span><img src="/static/item/img/a400e3d61d5645459f769b00d9f431e7.png" alt="" />进店逛逛</span>
							<span><img src="/static/item/img/a400e3d61d5645459f769b00d9f431e7.png" alt="" />关注店铺</span>
						</div>
					</div>
				</div>

			</div>
		</div>
<div class="Shop">
		<div class="box">
			<div class="box-one ">
				<div class="boxx">

					<div class="imgbox">
						<div class="probox">
							<img class="img1" alt="" th:src="${item.info.skuDefaultImg}">
							<div class="hoverbox"></div>
						</div>
						<div class="showbox">
							<img class="img1" alt="" th:src="${item.info.skuDefaultImg}">
						</div>
					</div>

					<div class="box-lh">

						<div class="box-lh-one">
							<ul>
								<li th:each="img:${item.images}" th:if="${!#strings.isEmpty(img.imgUrl)}"><img th:src="${img.imgUrl}" /></li>
							</ul>
						</div>
						<div id="left">
							< </div>
								<div id="right">
									>
								</div>

						</div>

						<div class="boxx-one">
							<ul>
								<li>
									<span>
										<img src="/static/item/img/b769782fe4ecca40913ad375a71cb92d.png" alt="" />关注
									</span>
									<span>
										<img src="/static/item/img/9224fcea62bfff479a6712ba3a6b47cc.png" alt="" />
										对比
									</span>
								</li>
								<li>

								</li>
							</ul>
						</div>

					</div>
					<div class="box-two">
						<div class="box-name" th:text="${item.info.skuTitle}">
							华为 HUAWEI Mate 10 6GB+128GB 亮黑色 移动联通电信4G手机 双卡双待
						</div>
						<div class="box-hide" th:text="${item.info.skuSubtitle}">预订用户预计11月30日左右陆续发货!麒麟970芯片!AI智能拍照!
							<a href=""><u></u></a>
						</div>
						<div class="box-yuyue">
							<div class="yuyue-one">
								<img src="/static/item/img/7270ffc3baecdd448958f9f5e69cf60f.png" alt="" /> 预约抢购
							</div>
							<div class="yuyue-two">
								<ul>
									<li>
										<img src="/static/item/img/f64963b63d6e5849977ddd6afddc1db5.png" />
										<span>190103</span> 人预约
									</li>
									<li>
										<img src="/static/item/img/36860afb69afa241beeb33ae86678093.png" /> 预约剩余
										<span id="timer">

									</span>
									</li>
								</ul>
							</div>
						</div>
						<div class="box-summary clear">
							<ul>
								<li>京东价</li>
								<li>
									<span>¥</span>
									<span th:text="${#numbers.formatDecimal(item.info.price,3,2)}">4499.00</span>
								</li>
								<li>
									预约享资格
								</li>
								<li>
									<a href="">
										预约说明
									</a>
								</li>
							</ul>
						</div>
						<div class="box-wrap clear">
							<div class="box-wrap-one clear">
								<ul>
									<li>增值业务</li>
									<li><img src="/static/item/img/90a6fa41d0d46b4fb0ff6907ca17c478.png" /></li>
									<li><img src="/static/item/img/2e19336b961586468ef36dc9f7199d4f.png" /></li>
									<li><img src="/static/item/img/1f80c3d6fabfd3418e54b005312c00b5.png" /></li>
								</ul>
							</div>
						</div>

						<div class="box-stock">
							<ul class="box-ul">
								<li>配送至</li>
								<li class="box-stock-li">
									<div class="box-stock-one">
										北京朝阳区管庄
										<img src="/static/item/img/4a79b87a68623d4e8a73aff3e25fa99b.png" alt="" class="img" />
									</div>
									<div class="box-stock-two">
										<dl>
											<dt>
												<a>选择新地址</a>
												<img src="/static/item/img/4a79b87a68623d4e8a73aff3e25fa99b.png" alt="" class="box-stock-two-img"/>
											</dt>
											<dd>
												<div class="box-stock-dd">
													<div class="box-stock-top">
														<div class="box-stock-div">北京</div>
														<div class="box-stock-div">朝阳区</div>
														<div class="box-stock-div">管庄</div>
													</div>
													<div class="box-stock-fot">
														<div class="box-stock-con" style="display: block;">
															<ul>
																<li>北京</li>
																<li>上海</li>
																<li>天津</li>
																<li>重庆</li>
															</ul>
														</div>
														<div class="box-stock-con">
															<ul>
																<li>朝阳区</li>
																<li>海淀区</li>
																<li>东城区</li>
																<li>西城区</li>
															</ul>
														</div>
														<div class="box-stock-con">
															<ul>
																<li>4环到5环之内</li>
																<li>管庄</li>
																<li>北苑</li>
															</ul>
														</div>

													</div>
												</div>
											</dd>
										</dl>

									</div>

								</li>
								<li>
									<span th:text="${item.hasStock?'有货':'无货'}">无货</span>, 此商品暂时售完
								</li>
							</ul>
						</div>
						<div class="box-supply">
							<ul class="box-ul">
								<li></li>
								<li>
									由<span>京东</span> 发货,并提供售后服务
								</li>
							</ul>
						</div>
						<div class="box-attr-3">
							<div class="box-attr clear" th:each="attr:${item.saleAttr}">
								<dl>
									<dt>[[${attr.attrName}]]</dt>
									<dd th:each="vals:${attr.attrValues}">
										<a class="sku_attr_value"
										   th:attr="skus=${vals.skuIds},class=${#lists.contains(#strings.listSplit(vals.skuIds,','), item.info.skuId.toString())?'sku_attr_value checked':'sku_attr_value'}">
											[[${vals.attrValue}]]
<!--											<img src="/static/item/img/59ddfcb1Nc3edb8f1.jpg" /> 摩卡金-->
										</a>
									</dd>
								</dl>
							</div>
						</div>

						<div class="box-btns clear">
							<div class="box-btns-one">
								<input type="text" name="" id="" value="1" />
								<div class="box-btns-one1">

									<div>
										<button id="jia">
									+
									</button>
									</div>
									<div>
										<button id="jian">
										-
									</button>
									</div>

								</div>
							</div>
							<div class="box-btns-two">
								<a href="../商品分类\index.html">
									立即预约
								</a>
							</div>
							<div class="box-btns-three">
								<img src="/static/item/img/e4ed3606843f664591ff1f68f7fda12d.png" alt="" /> 分享
							</div>
						</div>

						<div class="box-footer-zo">
							<div class="box-footer clear">
								<dl>
									<dt>本地活动</dt>
									<dd>
										<a href="">
											·1元500MB激活到账30元 >>
										</a>
									</dd>
								</dl>
							</div>

							<div class="box-footer">
								<dl>
									<dt>温馨提示</dt>
									<dd>·本商品不能使用 东券 京券</dd>
									<dd>·请完成预约后及时抢购!</dd>
								</dl>
							</div>
						</div>
					</div>

				</div>
			</div>
			<!--欲约抢购流程-->
			<div class="qianggoulioucheng">
				<div class="lioucheng">
					<h3>欲约抢购流程</h3>
				</div>
				<!--抢购步骤-->
				<ul class="qianggoubuzhao">
					<li>
						<img src="/static/item/img/shop_03.png" />
						<dl class="buzhou">
							<dt>1.等待预约</dt>
							<dl>预约即将开始</dl>
						</dl>
					</li>
					<li>
						<img src="/static/item/img/shop_04.png" />
						<dl class="buzhou">
							<dt>2.预约中</dt>
							<dl>2017-11-15 10:35 2017-11-15 23:59</dl>
						</dl>
					</li>
					<li>
						<img src="/static/item/img/shop_05.png" />
						<dl class="buzhou">
							<dt>3.等待抢购</dt>
							<dl>抢购即将开始</dl>
						</dl>
					</li>
					<li>
						<img src="/static/item/img/shop_06.png" />
						<dl class="buzhou">
							<dt>4.抢购中</dt>
							<dl></dl>
						</dl>
					</li>
				</ul>
			</div>

			<div class="ShopXiangqing">
				<div class="allLeft">
					<!--火热预约-->
					<div class="huoreyuyue">
						<h3>火热预约</h3>
					</div>
					<div class="dangeshopxingqing">
						<ul class="shopdange">
							<li>
								<a href="##"><img src="/static/item/img/5a0afeddNb34732af.jpg" /></a>
								<p>
									<a href="##">OPPO R11s Plus 双卡双待全面屏拍照手机香槟色 全网通(6G RAM+64G ROM)标配</a>
								</p>
								<p><strong class="J-p-20015341974">¥3699.00</strong></p>
							</li>
							<li>
								<a href="##"><img src="/static/item/img/5a12873eN41754123.jpg" /></a>
								<p>
									<a target="_blank" title="詹姆士(GEMRY) R19plus全网通4G 智能手机 双卡双待 6+128GB 鳄鱼纹雅致版(新品预约)" href="//item.jd.com/20348283521.html">詹姆士(GEMRY) R19plus全网通4G 智能手机 双卡双待 6+128GB 鳄鱼纹雅致版(新品预约)</a>
								</p>
								<p><strong class="J-p-20348283521">¥13999.00</strong></p>
							</li>
							<li>
								<a href="##"><img src="/static/item/img/59ec0131Nf239df75.jpg" /></a>
								<p>
									<a target="_blank" title="斐纳(TOMEFON) 德国家用无线无绳手持立式充电吸尘器 静音大吸力吸尘器TF-X60" href="//item.jd.com/16683419775.html">斐纳(TOMEFON) 德国家用无线无绳手持立式充电吸尘器 静音大吸力吸尘器TF-X60</a>
								</p>
								<p><strong class="J-p-16683419775">¥1599.00</strong></p>
							</li>
							<li>
								<a href="##"><img src="/static/item/img/59015444N27317512.jpg" /></a>
								<p>
									<a target="_blank" title="斐纳(TOMEFON) 扫地机器人德国智能导航规划全自动超薄扫地机器人吸尘器TF-D60 香槟金" href="//item.jd.com/12187770381.html">斐纳(TOMEFON) 扫地机器人德国智能导航规划全自动超薄扫地机器人吸尘器TF-D60 香槟金</a>
								</p>
								<p><strong class="J-p-12187770381">¥2599.00</strong></p>
							</li>
						</ul>
					</div>
					<!--看了又看-->
					<div class="huoreyuyue">
						<h3>看了又看</h3>
					</div>
					<div class="dangeshopxingqing">
						<ul class="shopdange">
							<li>
								<a href="##"><img src="/static/item/img/59e55e01N369f98f2.jpg" /></a>
								<p>
									<a target="_blank" title="华为(HUAWEI) 华为 Mate10 4G手机  双卡双待 亮黑色 全网通(6GB RAM+128GB ROM)" href="//item.jd.com/17931625443.html">华为(HUAWEI) 华为 Mate10 4G手机 双卡双待 亮黑色 全网通(6GB RAM+128GB ROM)</a>
									<p><strong class="J-p-17931625443">¥4766.00</strong></p>
							</li>
							<li>
								<a href="##"><img src="/static/item/img/584fcc3eNdb0ab94c.jpg" /></a>
								<p>
									<a target="_blank" title="华为 Mate 9 Pro 6GB+128GB版 琥珀金 移动联通电信4G手机 双卡双待" href="//item.jd.com/3749093.html">华为 Mate 9 Pro 6GB+128GB版 琥珀金 移动联通电信4G手机 双卡双待</a>
								</p>
								<p><strong class="J-p-3749093">¥4899.00</strong></p>
							</li>
							<li>
								<!--shopjieshao-->
								<a href="##"><img src="/static/item/img/59eb0df9Nd66d7585.jpg" /></a>
								<p>
									<a target="_blank" title="华为(HUAWEI) 华为 Mate10 手机 亮黑色 全网通(4+64G)标准版" href="//item.jd.com/12306211773.html">华为(HUAWEI) 华为 Mate10 手机 亮黑色 全网通(4+64G)标准版</a>
								</p>
								<p><strong class="J-p-12306211773">¥4088.00</strong></p>
							</li>
							<li>
								<a href="##"><img src="/static/item/img/5a002ba3N126c2f73.jpg" /></a>
								<p>
									<a target="_blank" title="斐纳(TOMEFON) 扫地机器人德国智能导航规划全自动超薄扫地机器人吸尘器TF-D60 香槟金" href="//item.jd.com/12187770381.html">斐纳(TOMEFON) 扫地机器人德国智能导航规划全自动超薄扫地机器人吸尘器TF-D60 香槟金</a>
								</p>
								<p><strong class="J-p-12187770381">¥2599.00</strong></p>
							</li>
						</ul>
						<img src="/static/item/img/5a084a1aNa1aa0a71.jpg" />
					</div>
				</div>
				<!--商品介绍-->
				<div class="allquanbushop">
					<ul class="shopjieshao">
						<li class="jieshoa" style="background: #e4393c;">
							<a href="##" style="color: white;">商品介绍</a>
						</li>
						<li class="baozhuang">
							<a href="##">规格与包装</a>
						</li>
						<li class="baozhang">
							<a href="##">售后保障</a>
						</li>
						<li class="pingjia">
							<a href="##">商品评价(4万+)</a>
						</li>
						<li class="shuoming">
							<a href="##">预约说明</a>
						</li>

					</ul>
					<button class="Lijiyuyuessss">
							立即预约
						</button>
					<ul class="shopjieshaos posi" style="display: none;">
						<li class="jieshoa" style="background: #e4393c;">
							<a href="#li1" style="color: white;">商品介绍</a>
						</li>
						<li class="baozhuang">
							<a href="#li2">规格与包装</a>
						</li>
						<li class="baozhang">
							<a href="#li3">售后保障</a>
						</li>
						<li class="pingjia">
							<a href="#li4">商品评价(4万+)</a>
						</li>
						<li class="shuoming">
							<a href="#li5">预约说明</a>
						</li>
					</ul>

					<!--商品详情-->
					<div class="huawei">
						<ul class="xuanxiangka">
							<li class="jieshoa actives" id="li1">
								<div class="shanpinsssss">
<!--									<p>-->
<!--										<a href="#">品牌:华为(HUAWEI)</a>-->
<!--									</p>-->
<!--									<table>-->
<!--										<tr>-->
<!--											<td>-->
<!--												<a href="##">商品名称:华为Mate 10</a>-->
<!--											</td>-->
<!--											<td>-->
<!--												<a href="##">商品毛重:0.58kg</a>-->
<!--											</td>-->
<!--											<td>-->
<!--												<a href="##">商品编号:5544038</a>-->
<!--											</td>-->
<!--											<td>-->
<!--												<a href="##">商品产地:中国大陆</a>-->
<!--											</td>-->
<!--										</tr>-->
<!--										<tr>-->
<!--											<td>-->
<!--												<a href="##">系统:安卓(Android)</a>-->
<!--											</td>-->
<!--											<td>-->
<!--												<a href="##">前置摄像头像素:800万-1599万</a>-->
<!--											</td>-->
<!--											<td>-->
<!--												<a href="##">后置摄像头像素:2000万及以上,1200万-1999万</a>-->
<!--											</td>-->
<!--											<td>-->
<!--												<a href="##">机身内存:128GB</a>-->
<!--											</td>-->
<!--										</tr>-->
<!--										<tr>-->
<!--											<td colspan="4">-->
<!--												<a href="##">全面屏,双卡双待,指纹识别,Type-C,VoLTE,2K屏,拍照神器,支持NFC,商务手机,安全手机,分辨率10</a>-->
<!--											</td>-->
<!--										</tr>-->
<!--									</table>-->
									<img class="xiaoguo" th:src="${desc}" th:each="desc:${#strings.listSplit(item.desc.decript,',')}" />

								</div>
							</li>
							<li class="baozhuang actives" id="li2">
								<div class="guiGebox">
									<div class="guiGe" th:each="group:${item.groupAttrs}">
										<h3 th:text="${group.groupName}">主体</h3>
										<dl>
											<div th:each="attr:${group.attrs}">
												<dt th:text="${attr.attrName}">品牌</dt>
												<dd th:text="${attr.attrValue}">华为(HUAWEI)</dd>
											</div>
										</dl>
									</div>
								</div>
							</li>
							<!--包装-->
							<li class="baozhang actives" id="li3">
								<div class="oBox">
		<div class="shuoHou">
			<div class="baoZhang">
				<h2>售后保障</h2>
			</div>
		</div>
		<!--厂家服务-->
		<div class="changJia">
			<div class="lianBao">
				<span class="oImg">
					<img src="/static/item/img/2017.jpg" alt="" />
					<h3>厂家服务</h3>
				</span>
				<div class="wenZi">
					本产品全国联保,享受三包服务,质保期为:一年保<br />
					如因质量问题或故障,凭厂商维修中心或特约维修点的质量检测证明,享受7日内退货,15日内换货,15日以上在保质期内享受免费保修等安保服务!<br />
					(注:如厂家在商品介绍中有售后保障的说明,则此商品按照厂家说明执行售后保障服务。)您可以查询本品牌在各地售后服务中心的练习方式<a href="#">请点击这儿查询...</a><br /><br />
				</div>
			</div>
			<div class="lianBao oCn">
				<span class="oImg">
					<img src="/static/item/img/2017.jpg" alt="" />
					<h3>京东承诺</h3>
				</span>
				<div class="wenZi">
					本产品全国联保,享受三包服务,质保期为:一年保<br />
					如因质量问题或故障,凭厂商维修中心或特约维修点的质量检测证明,享受7日内退货,15日内换货,15日以上在保质期内享受免费保修等安保服务!<br />
					(注:如厂家在商品介绍中有售后保障的说明,则此商品按照厂家说明执行售后保障服务。)您可以查询本品牌在各地售后服务中心的练习方式<a href="#">请点击这儿查询...</a><br /><br /><br />
				</div>
			</div>

			<div class="lianBao ">
				<span class="oImg">
					<img src="/static/item/img/2017.jpg" alt="" />
					<h3>正品行货</h3>
				</span>
				<div class="wenZi hangHuo">
					京东商城向您保证所售商品均为正品行货,京东自营商品开具机打发票或电子发票。
				</div>
			</div>
			<div class="lianBao quanGuo">
				<span class="oImg">
					<img src="/static/item/img/2017-1.jpg" alt="" />
					<h3>全国联保</h3>
				</span>
				<div class="wenZi">
					凭质保证书及京东商城发票,可享受全国联保服务(奢侈品、钟表除外;奢侈品、钟表由京东联系保修,享受法定三包售后服务),与您亲临商场选购的商品享受相同的质量保证。京东商城还为您提供具有竞争力的商品价格和运费政策,请您放心购买! <br />

注:因厂家会在没有任何提前通知的情况下更改产品包装、产地或者一些附件,本司不能确保客户收到的货物与商城图片、产地、附件说明完全一致。只能确保为原厂正货!并且保证与当时市场上同样主流新品一致。若本商城没有及时更新,请大家谅解!
				</div>
			</div>
				<!--权利声明-->
			<div class="quanLi">
				<h4>权利声明:</h4>
				<div class="jingDong">
					京东上的所有商品信息、客户评价、商品咨询、网友讨论等内容,是京东重要的经营资源,未经许可,禁止非法转载使用。<br />
	<span class="oZhu">注</span>:本站商品信息均来自于合作方,其真实性、准确性和合法性由信息拥有者(合作方)负责。本站不提供任何保证,并不承担任何法律责任。
				</div>
			</div>
			<div class="quanLi jiaGe">
				<h4>价格说明:</h4>
				<div class="jingDong">
					<span class="oZhu">京东价</span>:京东价为商品的销售价,是您最终决定是否购买商品的依据。<br />
					<span class="oZhu">划线价</span>:商品展示的划横线价格为参考价,该价格可能是品牌专柜标价、商品吊牌价或由品牌供应商提供的正品零售价(如厂商指导价、建议零售价等)或该商品在京东平台上曾经展示过的销售价;由于地区、时间的差异性和市场行情波动,品牌专柜标价、商品吊牌价等可能会与您购物时展示的不一致,该价格仅供您参考。<br />
					<span class="oZhu">折扣</span>:如无特殊说明,折扣指销售商在原价、或划线价(如品牌专柜标价、商品吊牌价、厂商指导价、厂商建议零售价)等某一价格基础上计算出的优惠比例或优惠金额;如有疑问,您可在购买前联系销售商进行咨询。<br />
					<span class="oZhu">异常问题</span>:商品促销信息以商品详情页"促销"栏中的信息为准;商品的具体售价以订单结算页价格为准;如您发现活动商品售价或促销信息有异常,建议购买前先联系销售商咨询。
				</div>
			</div>
		</div>


	</div>
							</li>
							<!--评价-->
							<li class="PINgjia actives" id="li4">
								<div class="h3">
									<h3>商品评价</h3>
								</div>
								<div class="nav">
									<div class="left">
										<p class="haoping">好评度</p>
										<p><span>100%</span></p>
									</div>
									<div class="right">
										<ul>
											<li>
												<a href="##">就是快(424)</a>
											</li>
											<li>
												<a href="##">物流很快(254) </a>
											</li>
											<li>
												<a href="##">货真价实(168)</a>
											</li>
											<li>
												<a href="##">有档次(158)</a>
											</li>
											<li>
												<a href="##">国产品牌(133)</a>
											</li>
											<li>
												<a href="##">外形美观(103)</a>
											</li>
											<li>
												<a href="##">很给力(75)</a>
											</li>
											<li>
												<a href="##">反应灵敏(68)</a>
											</li>
											<li>
												<a href="##">性价比高(60)</a>
											</li>
											<li>
												<a href="##">价格优惠(50)</a>
											</li>
											<li>
												<a href="##">功能齐全(49)</a>
											</li>
											<li style="background: gainsboro;">
												<a href="##">速度太慢(5)</a>
										</ul>
									</div>
								</div>
								<!--全部评价-->
								<div class="allpingjia">
									<ul>
										<li><a href="##">全部评价(4.2万)</a></li>
										<li><a href="##">晒图(500)</a></li>
										<li><a href="##">追拼(200+)</a></li>
										<li><a href="##">好评(4.1万)</a></li>
										<li><a href="##">中评(100+)</a></li>
										<li><a href="##">差评(100+)</a></li>
										<li><a href="##"><input type="checkbox"/>只看当前商品价格</a></li>
										<li class="imga" style="float: right;"><a href="##">推荐排序 <img src="/static/item/img/animaite.png"/> </a>
										</li>
									</ul>
								</div>
								</li>
								<li class="shuoming actives" id="li5"></li>
						</ul>
					</div>
				</div>
			</div>
		</div>
		<div class="headera">
			<div class="Logo-tu">
				<span><img src="/static/item/img/service_items_1.png"/></span>
				<span><img src="/static/item/img/service_items_2.png"/></span>
				<span><img src="/static/item/img/service_items_3.png"/></span>
				<span><img src="/static/item/img/service_items_4.png"/></span>
			</div>
			<div class="table">
				<dl>
					<dt><a href="##">购物指南</a></dt>
					<dd>
						<a href="##">购物流程</a>
					</dd>
					<dd>
						<a href="##">会员介绍</a>
					</dd>
					<dd>
						<a href="##">生活旅行/团购</a>
					</dd>
					<dd>
						<a href="##">常见问题</a>
					</dd>
					<dd>
						<a href="##">大家电</a>
					</dd>
					<dd>
						<a href="##">练习客服</a>
					</dd>
				</dl>
				<dl>
					<dt><a href="##">配送方式</a></dt>
					<dd>
						<a href="##">上门自提</a>
					</dd>
					<dd>
						<a href="##">211限时达</a>
					</dd>
					<dd>
						<a href="##">配送服务查询</a>
					</dd>
					<dd>
						<a href="##"></a>
					</dd>
					<dd>
						<a href="##">海外配送</a>
					</dd>
					<dd></dd>
				</dl>
				<dl>
					<dt><a href="##">支付方式</a></dt>
					<dd>
						<a href="##">货到付款</a>
					</dd>
					<dd>
						<a href="##">在线支付</a>
					</dd>
					<dd>
						<a href="##">分期付款</a>
					</dd>
					<dd>
						<a href="##">邮局汇款</a>
					</dd>
					<dd>
						<a href="##">公司转账</a>
					</dd>
					<dd></dd>
				</dl>
				<dl>
					<dt><a href="##">售后服务</a></dt>
					<dd>
						<a href="##">售后政策</a>
					</dd>
					<dd>
						<a href="##">价格保护</a>
					</dd>
					<dd>
						<a href="##">退款说明</a>
					</dd>
					<dd>
						<a href="##">返修/退换货</a>
					</dd>
					<dd>
						<a href="##">取消订单</a>
					</dd>
					<dd></dd>
				</dl>
				<dl class="dls">
					<dt><a href="##">特色服务</a></dt>
					<dd>
						<a href="##">夺宝岛</a>
					</dd>
					<dd>
						<a href="##">DIY装机</a>
					</dd>
					<dd>
						<a href="##">延保服务</a>
					</dd>
					<dd>
						<a href="##">京东E卡</a>
					</dd>
					<dd>
						<a href="##">京东通信</a>
					</dd>
					<dd>
						<a href="##">京东JD+</a>
					</dd>
				</dl>
			</div>
			<!--关于我们-->
			<div class="guanyuwomen">
				<ul>
					<li>
						<a href="##">关于我们</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">联系我们</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">联系客服</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">合作招商</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">商家帮助</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">营销中心</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">手机京东</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">友情链接</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">销售联盟</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">京东社区</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">风险检测</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">隐私政策</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">京东公益</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">English Site</a>
					</li>
					<li>|</li>
					<li>
						<a href="##">Mdeila $ IR</a>
					</li>
				</ul>
			</div>
			<!--jieshoa-->
			<p class="p1"><img src="/static/item/img/56a0a994Nf1b662dc.png" />
				<a href="##"> 京公网安备 11000002000088号</a>|
				<a href="##"> 京ICP证070359号</a>|
				<a href="##"> 互联网药品信息服务资格证编号(京)-经营性-2014-0008 </a>|
				<a href="##">新出发京零 字第大120007号</a>
			</p>
			<p class="p1">
				<a href="##"> 互联网出版许可证编号新出网证(京)字150号</a>|
				<a href="##"> 出版物经营许可证</a>|
				<a href="##"> 网络文化经营许可证京网文 </a>|
				<a href="##">[2014]2148-348号 </a>|
				<a href="##"> 违法和不良信息举报电话 </a>|
				<a href="##">:4006561155 </a>
			</p>
			<p class="p1">
				<a href="##"> Copyright © 2004-2017 京东JD.com 版权所有</a>|
				<a href="##"> 消费者维权热线:4006067733 经营证照</a>|
			</p>
			<p class="p1">
				<a href="##"> 京东旗下网站:京东支付</a>|
				<a href="##"> 京东云</a>
			</p>
			<p class="p3">
				<img src="/static/item/img/54b8871eNa9a7067e.png" />
				<img src="/static/item/img/54b8872dNe37a9860.png" />
				<img src="/static/item/img/54b8875fNad1e0c4c.png" />
				<img src="/static/item/img/5698dc03N23f2e3b8.jpg" />
				<img src="/static/item/img/5698dc16Nb2ab99df.jpg" />
				<img src="/static/item/img/56a89b8fNfbaade9a.jpg" />
			</p>
		</div>
		<div class="Fixedbian">
			<ul>
				<li class="li1"><a class="aaa" href="##">顶部</a></li>
			</ul>
		</div>
		<div class="gouwuchexiaguo">
			<img src="/static/item/img/44.png" />
			<span>购物车还没有商品,赶紧选购吧!</span>
		</div>
	</body>

	<script src="/static/item/js/jquery1.9.js" type="text/javascript" charset="utf-8"></script>
	<script src="/static/item/js/js.js" type="text/javascript" charset="utf-8"></script>
	<script>
		$(".sku_attr_value").click(function (){
			// 1. 点击的元素先添加上自定义的属性。为了识别我们是刚才被点击的
			var skus = new Array();
			$(this).addClass("clicked");
			var curr = $(this).attr("skus").split(",");
			skus.push(curr);
			// 2. 去掉同一行所有的checked
			$(this).parent().parent().find(".sku_attr_value").removeClass("checked");
			// 3. 找到其他被选中的属性
			$("a[class='sku_attr_value checked']").each(function (){
				skus.push($(this).attr("skus").split(","));
			});
			// 4. 使用filter取出他们的交集,得到skuId
			var filterEle = skus[0];
			for(var i=1 ;i<skus.length ;i++){
				filterEle = $(filterEle).filter(skus[i]);
			}
			console.log(filterEle[0])
			location.href = "http://item.gulimall.com/"+filterEle[0]+".html";
		});
		$(function (){
			$(".sku_attr_value").parent().css({"border":"solid 1px #CCC"});
			$("a[class='sku_attr_value checked']").parent().css({"border":"solid 1px red"})
		})
	</script>

</html>

2.1.8 异步编排优化

所有的操作针对于商品服务gulimall-product。

2.1.8.1 自定义线程池

2.1.8.1.1 引入依赖

spring-boot-configuration-processor作用:你自己创建的配置类生成元数据信息,这样就能在你自己的配置文件中显示出来非常的方便。【可以不加】

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

引入以上依赖,配置文件中会显示:

2.1.8.1.2 线程池属性配置类

gulimall-product/src/main/java/com/wen/gulimall/product/config/ThreadPoolConfigProperties.java

java 复制代码
@Data
@Component
@ConfigurationProperties(prefix = "gulimall.thread")
public class ThreadPoolConfigProperties {
    private Integer coreSize;
    private Integer maxSize;
    private Integer keepAliveTime;
}
2.1.8.1.3 配置文件中配置
XML 复制代码
gulimall:
  thread:
    core-size: 20
    max-size: 200
    keep-alive-time: 10
2.1.8.1.4 自定义线程池

gulimall-product/src/main/java/com/wen/gulimall/product/config/MyThreadConfig.java

java 复制代码
/**
 * @description 线程池配置
 *  注意:线程池属性类ThreadPoolConfigProperties已经使用了@Component放入容器中,
 *       不需要在使用@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
 */
//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties poolProperties){
        return new ThreadPoolExecutor(poolProperties.getCoreSize(),
                poolProperties.getMaxSize(),
                poolProperties.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

    }
}

2.1.8.2 异步编排优化商品详情业务

2.1.8.2.1 controller层
java 复制代码
@Controller
public class ItemController {
    @Resource
    private SkuInfoService skuInfoService;

    @GetMapping("/{skuId}.html")
    public String item(@PathVariable Long skuId, Model model){
        // 获取sku的基本信息 pms_sku_info
        // 获取sku的图片信息 pms_sku_images
        // 获取spu的销售属性组合
        // 获取spu的介绍
        // 获取spu的规格参数信息
        SkuItemVo vo = null;
        try {
            vo = skuInfoService.item(skuId);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        model.addAttribute("item",vo);
        return "item";
    }
}
2.1.8.2.2 service层
java 复制代码
@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {

    @Resource
    private SkuImagesService skuImagesService;
    @Resource
    private SpuInfoDescService spuInfoDescService;
    @Resource
    private AttrGroupService attrGroupService;
    @Resource
    private SkuSaleAttrValueService skuSaleAttrValueService;
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

   ...

    @Override
    public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
        SkuItemVo skuItemVo = new SkuItemVo();

        // 1.获取sku的基本信息 pms_sku_info
        CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
            SkuInfoEntity info = getById(skuId);
            skuItemVo.setInfo(info);
            return info;
        }, threadPoolExecutor);

        CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
            // 3.获取spu的销售属性组合
            List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
            skuItemVo.setSaleAttr(saleAttrVos);
        }, threadPoolExecutor);

        CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync(res -> {
            // 4.获取spu的介绍
            SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
            skuItemVo.setDesc(spuInfoDescEntity);
        }, threadPoolExecutor);

        CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync(res -> {
            // 5.获取spu的规格参数信息
            List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
            skuItemVo.setGroupAttrs(attrGroupVos);
        }, threadPoolExecutor);

        CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
            // 2.获取sku的图片信息 pms_sku_images
            List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
            skuItemVo.setImages(images);
        }, threadPoolExecutor);

        // 等待所有任务都完成,不用写infoFuture,因为saleAttrFuture/descFuture/baseAttrFuture他们依赖infoFuture完成的结果
        CompletableFuture.anyOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();

        return skuItemVo;
    }

}
相关推荐
陌上花开࿈26 分钟前
调用第三方接口
java
Aileen_0v034 分钟前
【玩转OCR | 腾讯云智能结构化OCR在图像增强与发票识别中的应用实践】
android·java·人工智能·云计算·ocr·腾讯云·玩转腾讯云ocr
西猫雷婶2 小时前
python学opencv|读取图像(十九)使用cv2.rectangle()绘制矩形
开发语言·python·opencv
桂月二二2 小时前
Java与容器化:如何使用Docker和Kubernetes优化Java应用的部署
java·docker·kubernetes
liuxin334455663 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
码农W3 小时前
QT--静态插件、动态插件
开发语言·qt
ke_wu3 小时前
结构型设计模式
开发语言·设计模式·组合模式·简单工厂模式·工厂方法模式·抽象工厂模式·装饰器模式
小马爱打代码3 小时前
设计模式详解(建造者模式)
java·设计模式·建造者模式
code04号3 小时前
python脚本:批量提取excel数据
开发语言·python·excel
小王爱吃月亮糖3 小时前
C++的23种设计模式
开发语言·c++·qt·算法·设计模式·ecmascript