美文网首页
1.构建一个基础的web项目

1.构建一个基础的web项目

作者: 呆叔么么 | 来源:发表于2019-12-22 16:01 被阅读0次
    课程内容
    业务模块

    使用MockMvc构建MVC测试环境

    package com.imooc.controller;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    /**
     * @Author:LovingLiu
     * @Description:
     * @Date:Created in 2019-12-15
     */
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class UserControllerTest {
        @Autowired
        private WebApplicationContext wac;
        private MockMvc mockMvc;
    
        /**
         * 在所有方法执行之前执行
         * 构造mvc环境
         */
        @Before
        public void setup(){
            mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
        }
    
        @Test
        public void user() throws Exception {
            // 构建http请求
            mockMvc.perform(MockMvcRequestBuilders.get("/user") // 请求地址
                    .param("userName","jiaojiao")
                    .contentType(MediaType.APPLICATION_JSON_UTF8)) // content-type: application/json
                    .andExpect(MockMvcResultMatchers.status().isOk()) // 期待的请求结果状态码 200
                    .andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(3));// 期待的请求结果长度为 3
        }
    }
    

    jsonpath表达式

    Operator Description
    $ The root element to query. This starts all path expressions.
    @ The current node being processed by a filter predicate.
    * Wildcard. Available anywhere a name or numeric are required.
    .. Deep scan. Available anywhere a name is required.
    .<name> Dot-notated child
    ['<name>' (, '<name>')] Bracket-notated child or children
    [<number> (, <number>)] Array index or indexes
    [start:end] Array slice operator
    [?(<expression>)] Filter expression. Expression must evaluate to a boolean value.

    jsonpath方法

    函数可以在路径的末尾调用——函数的输入是路径表达式的输出。
    函数输出由函数本身决定。

    Function Description Output
    min() Provides the min value of an array of numbers Double
    max() Provides the max value of an array of numbers Double
    avg() Provides the average value of an array of numbers Double
    stddev() Provides the standard deviation value of an array of numbers Double
    length() Provides the length of an array Integer
    sum() Provides the sum value of an array of numbers Double

    jsonpath 过滤属性

    筛选器是用于筛选数组的逻辑表达式。一个典型的过滤器应该是[?(@.age > 18)] 其中 @表示当前正在处理的项目。可以使用逻辑运算符&&||创建更复杂的过滤器.字符串文本必须用单引号或双引号括起来 ([?(@.color == 'blue')] or [?(@.color == "blue")]).

    Operator Description
    == left is equal to right (note that 1 is not equal to '1')
    != left is not equal to right
    < left is less than right
    <= left is less or equal to right
    > left is greater than right
    >= left is greater than or equal to right
    =~ left matches regular expression [?(@.name =~ /foo.*?/i)]
    in left exists in right [?(@.size in ['S', 'M'])]
    nin left does not exists in right
    subsetof left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])]
    anyof left has an intersection with right [?(@.sizes anyof ['M', 'L'])]
    noneof left has no intersection with right [?(@.sizes noneof ['M', 'L'])]
    size size of left (array or string) should match right
    empty left (array or string) should be empty

    @JsonView控制在不同的视图下 显示不同的字段

    1.使用接口来声明多个视图
    2.在值对象的get方法上指定视图
    3.在Controller的方法上指定视图
    

    User

    package com.imooc.dto;
    
    import com.fasterxml.jackson.annotation.JsonView;
    
    /**
     * @Author:LovingLiu
     * @Description:
     * @Date:Created in 2019-12-15
     */
    public class User {
        public interface UserSimpleView {};
        public interface UserDetailView extends UserSimpleView {};
    
        private String username;
        private String password;
    
        @JsonView(UserSimpleView.class)
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
        @JsonView(UserDetailView.class)
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    

    UserController

    package com.imooc.controller;
    
    import com.fasterxml.jackson.annotation.JsonView;
    import com.imooc.dto.User;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Author:LovingLiu
     * @Description:
     * @Date:Created in 2019-12-15
     */
    @RestController
    @RequestMapping("/user")
    @Slf4j
    public class UserController {
        
        @GetMapping
        @JsonView(User.UserSimpleView.class)
        public List<User> user(@RequestParam String userName){
            log.info("【userName】{}",userName);
            List<User> userList = new ArrayList<>();
            userList.add(new User());
            userList.add(new User());
            userList.add(new User());
            return userList;
        }
    
        @GetMapping("/{id:\\d+}")
        @JsonView(User.UserDetailView.class)
        public User getInfo(@PathVariable("id") String id){
            log.info("【id】=>{}", id);
            User user = new User();
            user.setUsername("lovingliu");
            user.setPassword("123");
            return user;
        }
    }
    

    RESTful API的校验

    @Valid 被注释的元素是一个对象,需要检查此对象的所有字段值
    @Null 被注释的元素必须为 null
    @NotNull 被注释的元素必须不为 null
    @AssertTrue 被注释的元素必须为 true
    @AssertFalse 被注释的元素必须为 false
    @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    @Size(max, min) 被注释的元素的大小必须在指定的范围内
    @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
    @Past 被注释的元素必须是一个过去的日期
    @Future 被注释的元素必须是一个将来的日期
    @Pattern(value) 被注释的元素必须符合指定的正则表达式

    自定义校验
    1.创建自定义Annotation

    package com.imooc.validator;
    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @Author:LovingLiu
     * @Description: 自定义参数校验逻辑
     * @Date:Created in 2019-12-16
     */
    @Target({ElementType.FIELD,ElementType.METHOD}) // 可以添加在什么类型上
    @Retention(RetentionPolicy.RUNTIME) // 起作用的时机
    @Constraint(validatedBy = MyConstraintValidator.class) // 实现的逻辑(要执行的校验逻辑)
    public @interface MyConstraint {
        /**
         * 自定义校验器必须含有以下三个属性
         */
        String message() default "自定义校验逻辑验证未通过";
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { };
    }
    

    2.创建自定义Annotation所执行的校验逻辑

    package com.imooc.validator;
    
    import com.imooc.service.HelloService;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    /**
     * @Author:LovingLiu
     * @Description: Spring 会将 实现了ConstraintValidator接口的类自动注入到容器中。不用@Component
     * @Date:Created in 2019-12-16
     */
    public class MyConstraintValidator implements ConstraintValidator<MyConstraint,Object> {
        /**
         * 设置可以获取 Spring管理的所有的类
         */
        @Autowired
        private HelloService helloService;
    
        /**
         * 校验器初始化
         * @param constraintAnnotation
         */
        @Override
        public void initialize(MyConstraint constraintAnnotation) {
            System.out.println("my validator init...");
        }
    
        /**
         *
         * @param value 要校验的值
         * @param context 校验的上下文
         * @return
         */
        @Override
        public boolean isValid(Object value, ConstraintValidatorContext context) {
            String result = helloService.sayHello(value.toString());
            System.out.println("isValid:"+result);
            return false;// 是否通过验证
        }
    }
    

    3.在需要校验的地方使用

    package com.imooc.dto;
    
    import com.fasterxml.jackson.annotation.JsonView;
    import com.imooc.validator.MyConstraint;
    
    import javax.validation.constraints.NotBlank;
    import javax.validation.constraints.Past;
    import java.util.Date;
    
    /**
     * @Author:LovingLiu
     * @Description:
     * @Date:Created in 2019-12-15
     */
    public class User {
        public interface UserSimpleView {};
        public interface UserDetailView extends UserSimpleView {};
    
        private String id;
        @MyConstraint // 自定义验证
        private String username;
        @NotBlank
        private String password;
        @Past(message = "生日必须是过去的时间")
        private Date birthday;
    
        @JsonView(UserSimpleView.class)
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
        @JsonView(UserDetailView.class)
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
        @JsonView(UserDetailView.class)
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
        @JsonView(UserSimpleView.class)
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    }
    

    自定义异常处理处理

    当系统访问一个不存在的URL地址时,对于浏览器和APP(其他)访问方式的处理方式是不一致的。


    自定义异常显示页面存放地址

    创建resources/error/404.html也是仅仅对浏览器发生的指定异常状态码请求的处理方式。
    com.imooc.exception.UserException

    package com.imooc.exception;
    
    import lombok.Data;
    
    /**
     * @Author:LovingLiu
     * @Description: 自定义异常
     * @Date:Created in 2019-12-16
     */
    @Data
    public class UserException extends RuntimeException {
    
        private Integer id;
        public UserException(String msg,Integer id){
            super(msg);
            this.id = id;
        }
    }
    

    com.imooc.handle.UserExceptionHandle

    package com.imooc.handle;
    
    import com.imooc.exception.UserException;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @Author:LovingLiu
     * @Description: 统一异常
     * @Date:Created in 2019-12-16
     */
    @ControllerAdvice
    public class UserExceptionHandle {
         @ExceptionHandler(value = UserException.class)
         @ResponseBody
         @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
         public Map<String,Object> handleUserException(UserException e){
             Map<String,Object> result = new HashMap<>();
             result.put("id",e.getId());
             result.put("message",e.getMessage());
             return result;
         }
    }
    

    Filter,Inreceptor,Aspect

    拦截器过滤器切面执行顺序
    Filter
    1.拦截所有请求
    com.imooc.filter.TimeFilter
    package com.imooc.filter;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    /**
     * @Author:LovingLiu
     * @Description: 自定义过滤器
     * @Date:Created in 2019-12-16
     */
    @Component
    @Slf4j
    public class TimeFilter implements Filter {
        /**
         * 指定过滤器逻辑
         * @param request
         * @param response
         * @param chain
         * @throws IOException
         * @throws ServletException
         */
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            log.info("time filter start");
            long start = System.currentTimeMillis();
            chain.doFilter(request,response);
            log.info("filter spend time {}",System.currentTimeMillis() - start);
            log.info("time filter finish");
        }
    
        /**
         * 初始化
         * @param filterConfig
         * @throws ServletException
         */
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            log.info("time filter init");
        }
    
        /**
         * 销毁
         */
        @Override
        public void destroy() {
            log.info("time filter destory");
        }
    }
    

    2.配置拦截指定url
    通常第三方的框架会自带Filter,但是并不会自动@Component,需要我们编写配置文件,注册到Spring中
    com.imooc.filter.TimeFilter2

    package com.imooc.filter;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    /**
     * @Author:LovingLiu
     * @Description: 自定义过滤器(不自动@Component)
     * @Date:Created in 2019-12-16
     */
    @Slf4j
    public class TimeFilter2 implements Filter {
        /**
         * 指定过滤器逻辑
         * @param request
         * @param response
         * @param chain
         * @throws IOException
         * @throws ServletException
         */
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            log.info("time filter2 start");
            long start = System.currentTimeMillis();
            chain.doFilter(request,response);
            log.info("filter2 spend time {}",System.currentTimeMillis() - start);
            log.info("time filter2 finish");
        }
    
        /**
         * 初始化
         * @param filterConfig
         * @throws ServletException
         */
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            log.info("time filter2 init");
        }
    
        /**
         * 销毁
         */
        @Override
        public void destroy() {
            log.info("time filter2 destory");
        }
    }
    

    编写配置类,将Filter注入到Spring中生效
    com.imooc.config.WebCongfig

    package com.imooc.config;
    
    import com.imooc.filter.TimeFilter2;
    import com.imooc.interceptor.TimeInterceptor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Author:LovingLiu
     * @Description: 不继承Filter接口 实现Filter拦截器(可适用于第三方Filter)
     * @Date:Created in 2019-12-16
     */
    @Configuration
    public class WebCongfig {
        /**
         * Filter 设置
         * @return
         */
        @Bean
        public FilterRegistrationBean timeFilter2() {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            TimeFilter2 timeFilter2 = new TimeFilter2();
            registrationBean.setFilter(timeFilter2);
            List<String> urls = new ArrayList<>();
            urls.add("/*"); // 指定拦截的url列表
            registrationBean.setUrlPatterns(urls);
            return registrationBean;
        }
    }
    

    Inteceptor
    1、拦截器执行顺序是按照Spring配置文件中定义的顺序而定的。

    2、会先按照顺序执行所有拦截器的preHandle方法,一直遇到return false为止,比如第二个preHandle方法是return false,则第三个以及以后所有拦截器都不会执行。若都是return true,则按顺序加载完preHandle方法。

    3、然后执行主方法(自己的controller接口),若中间抛出异常,则跟return false效果一致,不会继续执行postHandle,只会倒序执行afterCompletion方法。

    4、在主方法执行完业务逻辑(页面还未渲染数据)时,按倒序执行postHandle方法。若第三个拦截器的preHandle方法return false,则会执行第二个和第一个的postHandle方法和afterCompletionpostHandle都执行完才会执行这个,也就是页面渲染完数据后,执行after进行清理工作)方法。(postHandleafterCompletion都是倒序执行)

    1.创建拦截器 com.imooc.interceptor.TimeInterceptor

    package com.imooc.interceptor;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Date;
    
    /**
     * @Author:LovingLiu
     * @Description: 时间拦截器 (Interceptor解决的是 无法Spring管理 因为符合的是J2EE的规范, 无法知道控制器信息)
     * @Date:Created in 2019-12-16
     */
    @Component
    @Slf4j
    public class TimeInterceptor implements HandlerInterceptor {
        /**
         * 控制器(Controller)方法调用之前会被调用
         * @param request
         * @param response
         * @param handler
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            log.info("preHandle");
            log.info("methodClassName=>{}",((HandlerMethod) handler).getBean().getClass().getName());
            log.info("methodName=>{}",((HandlerMethod) handler).getMethod().getName());
            request.setAttribute("startTime", new Date().getTime());
            return true;
        }
    
        /**
         * 控制器(Controller)方法调用成功之后会被调用
         * @param request
         * @param response
         * @param handler
         * @param modelAndView
         * @throws Exception
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            log.info("postHandle");
            Long start = (Long) request.getAttribute("startTime");
            log.info("time interceptor 耗时:{}", new Date().getTime() - start);
        }
    
        /**
         * 控制器(Controller)方法调用之后会被调用(无论成功或者失败)
         * 注意:@ControllerAdvice声明的统一异常处理是在afterCompletion 之前的。所以发生的异常如果被@ControllerAdvice声明的异常处理类处理,无法捕获。ex 为null
         * @param request
         * @param response
         * @param handler
         * @param ex
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            log.info("afterCompletion");
            Long start = (Long) request.getAttribute("startTime");
            log.info("afterCompletion 耗时:{}", start);
        }
    }
    

    2.注入到Spring中

    package com.imooc.config;
    
    import com.imooc.filter.TimeFilter2;
    import com.imooc.interceptor.TimeInterceptor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Configuration
    public class WebCongfig extends WebMvcConfigurationSupport {
        @Autowired
        private TimeInterceptor timeInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(timeInterceptor);
        }
    }
    

    Aspect
    创建切点和切面 com.imooc.aspect.TimeAspect

    package com.imooc.aspect;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    /**
     * @Author:LovingLiu
     * @Description: 解决Inteceptor无法拿到请求参数(User user)user无法拿到
     * @Date:Created in 2019-12-16
     */
    @Aspect
    @Component
    @Slf4j
    public class TimeAspect {
        @Pointcut("execution(public * com.imooc.controller.UserController.* (..))")
        public void timePointcut() {}
    
        @Around("timePointcut()")
        public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable{
            log.info("time aspect start");
            Object[] args = pjp.getArgs();// 调用被拦击的控制器的方法时 传递的参数
            for (Object obj: args) {
                log.info("arg : {}", obj);
            }
            long start = new Date().getTime();
            Object result = pjp.proceed();// 调用被拦击的控制器的方法
            log.info("result: {}",result);
            log.info("Aspect 耗时: {}",(new Date().getTime() - start));
            log.info("time aspect end");
            return result;
        }
    }
    

    Filter,Interceptor,Aspect 它们之间的差别
    过滤器(Filter) :可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息。

    拦截器(Interceptor) :可以拿到你请求的控制器和方法,却拿不到请求方法的参数。

    切片 (Aspect) : 可以拿到方法的参数,但是却拿不到http请求和响应的对象。

    com.imooc.filter.TimeFilter2             : time filter2 start
    com.imooc.filter.TimeFilter              : time filter start
    com.imooc.interceptor.TimeInterceptor    : preHandle
    com.imooc.interceptor.TimeInterceptor    : methodClassName=>com.imooc.controller.UserController$$EnhancerBySpringCGLIB$$95854c15
    com.imooc.interceptor.TimeInterceptor    : methodName=>getInfo
    com.imooc.aspect.TimeAspect              : time aspect start
    com.imooc.aspect.TimeAspect              : arg : 1
    com.imooc.controller.UserController      : 【id】=>1
    com.imooc.aspect.TimeAspect              : result: com.imooc.dto.User@5af5ec7
    com.imooc.aspect.TimeAspect              : Aspect 耗时: 4
    com.imooc.aspect.TimeAspect              : time aspect end
    com.imooc.interceptor.TimeInterceptor    : postHandle
    com.imooc.interceptor.TimeInterceptor    : time interceptor 耗时:72
    com.imooc.interceptor.TimeInterceptor    : afterCompletion
    com.imooc.interceptor.TimeInterceptor    : afterCompletion 耗时:1576511855244
    com.imooc.filter.TimeFilter              : filter spend time 90
    com.imooc.filter.TimeFilter              : time filter finish
    com.imooc.filter.TimeFilter2             : filter2 spend time 90
    com.imooc.filter.TimeFilter2             : time filter2 finish
    

    6.RESTFul异步改造 提高系统吞吐量

    1.传统同步

    package com.imooc.async;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.RandomStringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.context.request.async.DeferredResult;
    
    import java.util.concurrent.Callable;
    
    /**
     * @Author:LovingLiu
     * @Description: 异步处理请求
     * @Date:Created in 2019-12-17
     */
    @RestController
    @RequestMapping("/order")
    @Slf4j
    public class AsyncController {
        @GetMapping
        public String order() throws Exception{
            log.info("主线程开始");
            Thread.sleep(1000);
            log.info("主线程结束");
            return "success";
        }
    }
    
    同步处理Rest请求
    2.Callable 接口实现异步处理
    异步处理REST请求
    com.imooc.async.AsyncController
    package com.imooc.async;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.RandomStringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.context.request.async.DeferredResult;
    
    import java.util.concurrent.Callable;
    
    /**
     * @Author:LovingLiu
     * @Description: 异步处理请求
     * @Date:Created in 2019-12-17
     */
    @RestController
    @RequestMapping("/order")
    @Slf4j
    public class AsyncController {
        @GetMapping("/orderAsync")
        public Callable<String> orderAsync() throws Exception{
            log.info("主线程开始");
            Callable<String> result = new Callable<String>() {
                @Override
                public String call() throws Exception {
                    log.info("副线程开始");
                    Thread.sleep(1000);// 模拟下单的业务逻辑
                    log.info("副线程返回");
                    return "success";
                }
            };
            log.info("主线程结束");
            return result;
        }
    }
    

    Runnable使用的缺点,必须使用祝线程才能调起一个副线程


    Runnable异步线程

    3.DeferredResult 接口实现异步处理

    diffentResult 应用场景
    实例映射
    com.imooc.async.AsyncController
    package com.imooc.async;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.RandomStringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.context.request.async.DeferredResult;
    
    import java.util.concurrent.Callable;
    
    /**
     * @Author:LovingLiu
     * @Description: 异步处理请求
     * @Date:Created in 2019-12-17
     */
    @RestController
    @RequestMapping("/order")
    @Slf4j
    public class AsyncController {
    
        @Autowired
        private MockQueue mockQueue;
        @Autowired
        private DeferredResultHolder deferredResultHolder;
    
        @GetMapping("/orderDeferred")
        public DeferredResult <String> orderDeferred() throws Exception{
            log.info("主线程开始");
            String orderNo = RandomStringUtils.randomNumeric(8); // 生成订单号
            mockQueue.setPlaceOrder(orderNo); // 放入消息队列
            DeferredResult<String> result = new DeferredResult<>();
            deferredResultHolder.getMap().put(orderNo,result);
            log.info("主线程结束");
            return result;
        }
    }
    

    com.imooc.async.MockQueue

    package com.imooc.async;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author:LovingLiu
     * @Description: 模拟消息队列
     * @Date:Created in 2019-12-17
     */
    @Component
    @Slf4j
    public class MockQueue {
        private String placeOrder; // 下单的消息
        private String completeOrder; // 订单完成的消息
    
        public String getPlaceOrder() {
            return placeOrder;
        }
    
        public void setPlaceOrder(String placeOrder){
            new Thread(()-> {
                log.info("接到下单请求, {}", placeOrder);
                try {
                    Thread.sleep(1000);
                }catch (Exception e){
                    e.printStackTrace();
                }
    
                this.completeOrder = placeOrder;
                log.info("下单请求处理完毕, {}", placeOrder);
            }).start();
    
        }
    
        public String getCompleteOrder() {
            return completeOrder;
        }
    
        public void setCompleteOrder(String completeOrder) {
            this.completeOrder = completeOrder;
        }
    }
    

    com.imooc.async.QueueListener

    package com.imooc.async;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author:LovingLiu
     * @Description: ContextRefreshedEvent spring容器初始化完成的事件
     * @Date:Created in 2019-12-17
     */
    @Component
    @Slf4j
    public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
        @Autowired
        private MockQueue mockQueue;
        @Autowired
        private DeferredResultHolder deferredResultHolder;
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            new Thread(() -> {
                while (true){
                    if(StringUtils.isNotBlank(mockQueue.getCompleteOrder())){
                        String orderNo = mockQueue.getCompleteOrder();
                        log.info("返回订单处理结果:{}",orderNo);
                        deferredResultHolder.getMap().get(orderNo).setResult("place order success");
                        mockQueue.setCompleteOrder(null);
                    }else {
                        try {
                            Thread.sleep(100);
                        }catch (Exception e){
                            e.printStackTrace();
                        }
    
                    }
                }
            }).start();
    
        }
    }
    

    com.imooc.async.DeferredResultHolder

    package com.imooc.async;
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.async.DeferredResult;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @Author:LovingLiu
     * @Description: 订单处理结果 Map<key, info>   key: 订单号 info:订单处理信息
     * @Date:Created in 2019-12-17
     */
    @Component
    public class DeferredResultHolder {
        private Map<String, DeferredResult<String>> map = new HashMap<>();
    
        public Map<String, DeferredResult<String>> getMap() {
            return map;
        }
    
        public void setMap(Map<String, DeferredResult<String>> map) {
            this.map = map;
        }
    }
    
    [nio-8001-exec-4] com.imooc.async.AsyncController          : 主线程开始
    [nio-8001-exec-4] com.imooc.async.AsyncController          : 主线程结束
    [      Thread-38] com.imooc.async.MockQueue                : 接到下单请求, 38677847
    [      Thread-38] com.imooc.async.MockQueue                : 下单请求处理完毕, 38677847
    [      Thread-22] com.imooc.async.QueueListener            : 返回订单处理结果:38677847
    

    相关文章

      网友评论

          本文标题:1.构建一个基础的web项目

          本文链接:https://www.haomeiwen.com/subject/patlnctx.html