SpringMVC进阶-01

SpringMVC进阶-01

1.拦截器

public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 拦截器 拦截没有登录的用户
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        if (request.getSession().getAttribute("user") == null) {
            response.sendRedirect("/admin");
            return false;
        }
        return true;
    }
}

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/admin/**")
                .excludePathPatterns("/admin")
                .excludePathPatterns("/admin/login");
    }
}

2.异常处理

@Slf4j
@ControllerAdvice
public class ControllerExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ModelAndView handlerException(HttpServletRequest request, Exception e) throws Exception {
        log.error("request url {}, error info {}", request.getRequestURL(), e);

        // 如果捕获到的异常上有ResponseStatus注解,说明是页面找不到的异常,这里直接抛出,
        // 交给springboot去处理,来返回error/404.html。
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }

        ModelAndView mv = new ModelAndView();
        mv.addObject("url", request.getRequestURL());
        mv.addObject("exception", e);
        mv.setViewName("error/error.html");
        return mv;
    }
}

3.方法访问日志记录

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com.my.blog.controller.*.*(..))")
    public void log() {}

    @Before(value = "log()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        String url = request.getRequestURL().toString();
        String ip = request.getRemoteAddr();
        // 方法名 = 类全名 + 方法名
        String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        RequestInfo requestInfo = new RequestInfo(url, ip, methodName, args);
    }

    @After(value = "log()")
    public void doAfter() {
    }

    @AfterReturning(value = "log()", returning = "obj")
    public void afterReturning(Object obj) {
        log.info("response result {}", obj);
    }

    @ToString
    @AllArgsConstructor
    static class RequestInfo {
        private final String url;
        private final String ip;
        private final String methodName;
        /**
         * 方法参数
         */
        private final Object[] args;
    }
}

4.Controller参数接受

@Slf4j
@RestController
public class UserController {

    @Resource
    private UserService userService;

    /**
     * localhost:8080/user06
     * [{"id":1,"name":"tom"}, {"id":2, "name":"bob"}]
     * json数组的封装
     * @param users
     * @return
     */
    @GetMapping(value = "/user06")
    public List<User> getUser06(@RequestBody List<User> users) {
        // user [User(id=1, name=tom, age=null), User(id=2, name=bob, age=null)]
        log.info("user {}", users);
        return users;
    }

    /**
     * localhost:8080/user04
     * {"id":1}
     *
     * 会将{"id":1}看做是一个整体,然后转换为User对象
     * @param user
     * @return
     */
    @GetMapping(value = "/user05")
    public User getUser05(@RequestBody User user) {
        // user User(id=1, name=null, age=null)
        log.info("user {}", user);
        return user;
    }

    /**
     * 接受json数据
     * localhost:8080/user04
     * {"id":"1"}
     * 会将{"id":"1"}看做是一个整体赋给id
     * @param id
     * @return
     */
    @GetMapping(value = "/user04")
    public User getUser04(@RequestBody String id) {
        // id {"id":"1"}
        log.info("id {}", id);
        return userService.getUser(Integer.parseInt(id));
    }

    /**
     * localhost:8080/user03
     * body form-data参数 id=1,name=tom
     * @param user
     * @return
     */
    @GetMapping("/user03")
    public User getUser03(User user) {
        log.info("user {}", user);
        return user;
    }

    /**
     * localhost:8080/user02
     * body form-data参数 id=1,2,3
     * 同样需要使用@RequestParam注解,否则报错
     * @param ids
     * @return
     */
    @GetMapping("/user02")
    public List<User> getUser02(@RequestParam List<Integer> ids) {
        log.info("list is {}", ids);
        List<User> users = new ArrayList<>();
        ids.forEach(v -> users.add(userService.getUser(v)));

        return users;
    }


    /**
     * localhost:8080/user01
     * body form-data参数 id=1
     * @param id
     * @return
     */
    @GetMapping("/user01")
    public User getUser01(Integer id) {
        log.info("user id {}", id);
        return userService.getUser(id);
    }

    /**
     * localhost:8080/get/user?id=1&name=tom&age=100
     * @param user
     * @return
     */
    @GetMapping("/get/user")
    public User getUser(User user) {
        log.info("user {}", user);
        return user;
    }


    /**
     * localhost:8080/users?ids=1,2,3
     * 可以接受输出参数,但是必须要使用 @RequestParam注解,
     * 否则报错 No primary or default constructor found for interface java.util.List]
     * @param ids
     * @return
     */
    @GetMapping("/users")
    public List<User> getUsers(@RequestParam(name = "ids") List<Integer> ids) {
        log.info("list is {}", ids);
        List<User> users = new ArrayList<>();
        ids.forEach(v -> users.add(userService.getUser(v)));

        return users;
    }

    /**
     * localhost:8080/user?id=1&username=tom.
     * id必须传入,否则页面出现404,服务器端出现警告。
     * Required request parameter 'id' for method parameter type Integer is not present
     * @param id
     * @param name
     * @return
     */
    @GetMapping("/user")
    public User getUser(@RequestParam(name = "id", required = true) Integer id,
                        @RequestParam(name = "username", required = false) String name) {
        log.info("user id {}, name {}", id, name);
        return userService.getUser(id);
    }

    /**
     * localhost:8080/user/1
     * @param id
     * @return
     */
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable("id") Integer id) {
        log.info("user id {}", id);
        return userService.getUser(id);
    }
}

5.耗时请求异步处理

  1. SpringMVC实现耗时请求异步处理,同时客户端和服务端保持连接不断开。
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TestInterceptor01())
                .addPathPatterns("/test/01")
                .order(-100);
    }
}

@Slf4j
public class TestInterceptor01 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        log.info("TestInterceptor01 preHandle {}", Thread.currentThread().getName());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        log.info("TestInterceptor01 postHandle {}", Thread.currentThread().getName());
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("TestInterceptor01 afterCompletion {}", Thread.currentThread().getName());
    }
}

@Slf4j
@RestController
public class TestController {

    @GetMapping("/test/01")
    public Callable<String> test01() {
        log.info("test 01 {}", Thread.currentThread().getName());

        return () -> {

            log.info("异步处理任务开始 {}", Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(5);
            log.info("异步处理任务结束 {}", Thread.currentThread().getName());

            return "异步处理任务完成";
        };
    }
}
  1. 异步任务的执行流程。1)现有SpringMVC线程执行Interceptor.preHandler()和Controller中的方法。2)然由开启的线程异步执行耗时操作。3)耗时操作执行完成,在由SpringMVC线程重新执行Interceptor.preHandler() -> postHandler() -> afterCompletion()。
TestInterceptor01 preHandle http-nio-8080-exec-9
test 01 http-nio-8080-exec-9
异步处理任务开始 task-3
步处理任务结束 task-3
TestInterceptor01 preHandle http-nio-8080-exec-7
TestInterceptor01 postHandle http-nio-8080-exec-7
TestInterceptor01 afterCompletion http-nio-8080-exec-7
  1. 耗时操作有线程池去执行。
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TestInterceptor01())
                .addPathPatterns("/test/01")
                .order(-100);
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数
        threadPoolTaskExecutor.setCorePoolSize(5);
        // 允许核心线程数执行超时
        threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
        // 最大线程数
        threadPoolTaskExecutor.setMaxPoolSize(5);
        // 配置队列大小
        threadPoolTaskExecutor.setQueueCapacity(50);
        // 配置线程池前缀
        threadPoolTaskExecutor.setThreadNamePrefix("async-");
        threadPoolTaskExecutor.initialize();

        configurer.setTaskExecutor(threadPoolTaskExecutor);
    }
}

6.AsyncHandlerInterceptor

  1. AsyncHandlerInterceptor是对HandlerInterceptor接口在异步场景下的一个补充。在异步场景中HandlerInterceptor接口中的postHandle()、afterCompletion()方法只会在异步线程执行完成才去执行。
  2. AsyncHandlerInterceptor提供afterConcurrentHandlingStarted()方法,在异步线程执行之前调用,可以用来释放开启异步线程的线程的资源,如线程的MDC、当前线程缓存的用户数据。
  3. AsyncHandlerInterceptor代码实例。
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TestInterceptor02())
                .addPathPatterns("/test/02")
                .order(1);
    }
}

@Slf4j
public class TestInterceptor02 implements AsyncHandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        log.info("TestInterceptor02 preHandle {}", Thread.currentThread().getName());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        log.info("TestInterceptor02 postHandle {}", Thread.currentThread().getName());
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        log.info("TestInterceptor02 afterCompletion {}", Thread.currentThread().getName());
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        log.info("TestInterceptor02 afterConcurrentHandlingStarted {}", Thread.currentThread().getName());
    }
}

@Slf4j
@RestController
public class TestController {

    @GetMapping("/test/02")
    public Callable<String> test02() {
        log.info("test 02 {}", Thread.currentThread().getName());

        return () -> {
            log.info("异步处理任务开始 {}", Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(5);

            log.info("异步处理任务结束 {}", Thread.currentThread().getName());
            return "异步处理任务完成";
        };
    }
}
  1. AsyncHandlerInterceptor执行流程。
TestInterceptor02 preHandle http-nio-8080-exec-9
test 02 http-nio-8080-exec-9
# 可以在afterConcurrentHandlingStarted()中
# 释放http-nio-8080-exec-9线程ThreadLocal中缓存的数据。
TestInterceptor02 afterConcurrentHandlingStarted http-nio-8080-exec-9
异步处理任务开始 async-service-1
异步处理任务结束 async-service-1
TestInterceptor02 preHandle http-nio-8080-exec-10
TestInterceptor02 postHandle http-nio-8080-exec-10
TestInterceptor02 afterCompletion http-nio-8080-exec-10
posted @ 2021-08-25 20:15  行稳致远方  阅读(60)  评论(0)    收藏  举报