目录
[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.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;
}
}