Springboot 系列(六)Spring Boot web 开发之拦截器和三大组件

1. 拦截器

Springboot 中的 Interceptor 拦截器也就是 mvc 中的拦截器,只是省去了 xml 配置部分。并没有本质的不同,都是通过实现 HandlerInterceptor 中几个方法实现。几个方法的作用一一如下。

  1. preHandle

    进入 Habdler 方法之前执行,一般用于身份认证授权等。

  2. postHandle

    进入 Handler 方法之后返回 modelAndView 之前执行,一般用于塞入公共模型数据等。

  3. afterCompletion

    最后处理,一般用于日志收集,统一后续处理等。

1.1 引入依赖

 <!-- Spring Boot web 开发整合 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-json</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- 阿里 fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>

        <!-- Lombok 工具 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 导入配置文件处理器,在配置springboot相关文件时候会有提示 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 单元测试 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>

1.2 编写拦截器

package net.codingme.boot.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>
 * 拦截器
 *
 * @Author niujinpeng
 * @Date 2019/1/6 16:54
 */
@Slf4j
public class LogHandlerInterceptor implements HandlerInterceptor {

    /**
     * 请求方法执行之前
     * 返回true则通过
     *
     * @param request
     * @param response
     * @param handler
     * @return
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        StringBuffer requestURL = request.getRequestURL();
        log.info("preHandle请求URL:" + requestURL.toString());
        return true;
    }

    /**
     * 返回modelAndView之前执行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        log.info("postHandle返回modelAndView之前");
    }

    /**
     * 执行Handler完成执行此方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        log.info("afterCompletion执行完请求方法完全返回之后");
    }
}

1.3 配置拦截器

省去了 XML 中的拦截器配置部分后,使用 springboot 推荐的方式配置自定义拦截器。

package net.codingme.boot.config;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 1.使用FastJSON
 * 2.配置时间格式化
 * 3.解决中文乱码
 * 4.添加自定义拦截器
 *
 * @Author niujinpeng
 * @Date 2018/12/13 15:35
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 自定义JSON转换器
     *
     * @param converters
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        //日期格式化
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
        //处理中文乱码问题
        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);

        converter.setSupportedMediaTypes(fastMediaTypes);
        converter.setFastJsonConfig(fastJsonConfig);
        converters.add(converter);
    }

    /**
     * 添加自定义拦截器
     * .addPathPatterns("/**")  拦截的请求路径
     * .excludePathPatterns("/user"); 排除的请求路径
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogHandlerInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user");
    }
}

2 切面编程

  1. AOP:面向切面(方面)编程,扩展功能不修改源代码实现
  2. AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
  3. AOP底层使用动态代理实现
    • 有接口情况使用动态代理创建接口实现类代理对象
    • 没有接口情况使用动态代理创建类的子类代理对象
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * <p>
 * 使用AOP记录访问日志
 * 使用@Before在切入点开始处切入内容
 * 使用@After在切入点结尾处切入内容
 * 使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
 * 使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容
 * 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑
 * <p>
 * 注解:
 * Aspect:AOP
 * Component:Bean
 * Slf4j:可以直接使用log输出日志
 * Order:多个AOP切同一个方法时的优先级,越小优先级越高越大。
 * 在切入点前的操作,按order的值由小到大执行
 * 在切入点后的操作,按order的值由大到小执行
 *
 * @Author niujinpeng
 * @Date 2019/1/4 23:29
 */

@Aspect
@Component
@Slf4j
@Order(1)
public class LogAspect {
    /**
     * 线程存放信息
     */
    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 定义切入点
     * 第一个*:标识所有返回类型
     * 字母路径:包路径
     * 两个点..:当前包以及子包
     * 第二个*:所有的类
     * 第三个*:所有的方法
     * 最后的两个点:所有类型的参数
     */
    @Pointcut("execution(public * net.codingme.boot.controller..*.*(..))")
    public void webLog() {
    }

    /**
     * 在切入点开始处切入内容
     *
     * @param joinPoint
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 记录请求时间
        startTime.set(System.currentTimeMillis());
        // 获取请求域
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        // 记录请求内容
        log.info("Aspect-URL: " + request.getRequestURI().toLowerCase());
        log.info("Aspect-HTTP_METHOD: " + request.getMethod());
        log.info("Aspect-IP: " + request.getRemoteAddr());
        log.info("Aspect-REQUEST_METHOD: " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("Aspect-Args: " + Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 在切入点之后处理内容
     */
    @After("webLog()")
    public void doAfter() {

    }

    /**
     * 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
     */
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        log.info("Aspect-Response: " + ret);
        Long endTime = System.currentTimeMillis();
        log.info("Aspect-SpeedTime: " + (endTime - startTime.get()) + "ms");
    }

}

访问查看拦截器和 AOP 的日志输出。

09:57:15.408  INFO 2836 --- [nio-8080-exec-1] n.c.boot.config.LogHandlerInterceptor    : preHandle请求URL:http://localhost:8080/
09:57:15.413  INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect       : Aspect-URL: /
09:57:15.413  INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect       : Aspect-HTTP_METHOD: GET
09:57:15.413  INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect       : Aspect-IP: 0:0:0:0:0:0:0:1
09:57:15.414  INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect       : Aspect-REQUEST_METHOD: net.codingme.boot.controller.HelloController.index
09:57:15.415  INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect       : Aspect-Args: []
09:57:15.424  INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect       : Aspect-Response: Greetings from Spring Boot!SpringBoot是一个spring应用程序
09:57:15.425  INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect       : Aspect-SpeedTime: 12ms
09:57:15.436  INFO 2836 --- [nio-8080-exec-1] n.c.boot.config.LogHandlerInterceptor    : postHandle返回modelAndView之前
09:57:15.437  INFO 2836 --- [nio-8080-exec-1] n.c.boot.config.LogHandlerInterceptor    : afterCompletion执行完请求方法完全返回之后

3. Servlet,Filter,Listener

Servlet, Filter, Listener 是 Java web 的核心内容,那么在 Springboot 中如何使用呢?

3.1 编写 Servlet

package net.codingme.boot.servlet;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * <p>
 * @WebServlet(urlPatterns = "/myservlet") // 定义访问路径
 * @Author niujinpeng
 * @Date 2019/1/24 16:25
 */
@Slf4j
@WebServlet(urlPatterns = "/myservlet")
public class MyServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        log.info("Servlet 开始初始化");
        super.init();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        log.info("Servlet 开始处理 GET 方法");
        PrintWriter writer = resp.getWriter();
        writer.println("Hello Servlet");
        writer.flush();
        writer.close();
    }

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

    @Override
    public void destroy() {
        log.info("Servlet 开始销毁");
        super.destroy();
    }
}

3.2 编写 Filter

package net.codingme.boot.filter;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * <p>
 *
 * @Author niujinpeng
 * @Date 2019/1/24 16:35
 */
@Slf4j
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        log.info("拦截器开始拦截");
        filterChain.doFilter(request, response);
    }

}

3.3 编写 Listener

package net.codingme.boot.listener;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * <p>
 *
 * @Author niujinpeng
 * @Date 2019/1/24 16:45
 */
@Slf4j
@WebListener
public class MyListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        log.info("监听器开始初始化");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("监听器开始销毁");
    }
}

3.4 添加到容器

添加到容器有两种方式,第一种使用注解扫描。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;

/**
 * @ServletComponentScan 扫描Servlet,Filter,Listener 添加到容器
 */
@SpringBootApplication
@ServletComponentScan
public class BootApplication {

    public static void main(String[] args) {
        SpringApplication.run(BootApplication.class, args);
    }

}

或者使用配置类想容器中添加。

/**
 * <p>
 * 在这里注册Servlet Filter Listener 或者使用 @ServletComponentScan
 *
 * @Author niujinpeng
 * @Date 2019/1/24 16:30
 */
@Configuration
public class WebCoreConfig {

    @Bean
    public ServletRegistrationBean myServlet() {
        return new ServletRegistrationBean<>(new MyServlet());
    }

    @Bean
    public FilterRegistrationBean myFitler() {
        return new FilterRegistrationBean<>(new MyFilter());
    }

    @Bean
    public ServletListenerRegistrationBean myListener() {
        return new ServletListenerRegistrationBean(new MyListener());
    }

}

启动可以在控制台看到监听器启动。

 11:35:03.744  INFO 8616 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1364 ms
 11:35:03.798  INFO 8616 --- [           main] net.codingme.boot.listener.MyListener    : 监听器开始初始化
 11:35:03.892  INFO 8616 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService ‘applicationTaskExecutor‘
 11:35:04.055  INFO 8616 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ‘‘

访问 Servlet 可以看到拦截器和 Servlet 生效。

 11:36:55.552  INFO 3760 --- [nio-8080-exec-1] net.codingme.boot.servlet.MyServlet      : Servlet 开始初始化
 11:36:55.556  INFO 3760 --- [nio-8080-exec-1] net.codingme.boot.filter.MyFilter        : 拦截器开始拦截
 11:36:55.556  INFO 3760 --- [nio-8080-exec-1] net.codingme.boot.servlet.MyServlet      : Servlet 开始处理 GET 方法

文章代码已经上传到 GitHub Spring Boot Web开发 - 拦截处理

文章代码已经上传到 GitHub Spring Boot Web开发 - Servlet,Filter,Listener

<完>

本文原发于个人博客:https://www.codingme.net 转载请注明出处

原文地址:https://www.cnblogs.com/niumoo/p/10411506.html

时间: 2024-11-10 07:36:10

Springboot 系列(六)Spring Boot web 开发之拦截器和三大组件的相关文章

Springboot 系列(五)Spring Boot web 开发之静态资源和模版引擎

前言 Spring Boot 天生的适合 web 应用开发,它可以快速的嵌入 Tomcat, Jetty 或 Netty 用于包含一个 HTTP 服务器.且开发十分简单,只需要引入 web 开发所需的包,然后编写业务代码即可. 自动配置原理? 在进行 web 开发之前让我再来回顾一下自动配置,可以参考系列文章第三篇.Spring Boot 为 Spring MVC 提供了自动配置,添加了如下的功能: 视图解析的支持. 静态资源映射,WebJars 的支持. 转换器 Converter 的支持.

SpringBoot系列:Spring Boot集成Spring Cache,使用RedisCache

前面的章节,讲解了Spring Boot集成Spring Cache,Spring Cache已经完成了多种Cache的实现,包括EhCache.RedisCache.ConcurrentMapCache等. 这一节我们来看看Spring Cache使用RedisCache. 一.RedisCache使用演示 Redis是一个key-value存储系统,在web应用上被广泛应用,这里就不对其过多描述了. 本章节示例是在Spring Boot集成Spring Cache的源码基础上进行改造.源码地

spring boot web开发

json 接口开发 在以前的spring 开发的时候需要我们提供json接口的时候需要做如下配置: 1 添加jackjson等jar包 2 配置spring controller扫描 3 对接的方法添加@ResponseBody 如果使用spring boot 如何做呢,只需要类添加 @RestController即可,默认类中的方法都以json格式返回. 举例1: /** * @RestController = @Controller + @ResponseBody. 所以,以后定义contr

Spring Boot 优雅的配置拦截器方式

其实spring boot拦截器的配置方式和springMVC差不多,只有一些小的改变需要注意下就ok了.下面主要介绍两种常用的拦截器: 一.基于URL实现的拦截器: public class LoginInterceptor extends HandlerInterceptorAdapter{ /** * 在请求处理之前进行调用(Controller方法调用之前) * 基于URL实现的拦截器 * @param request * @param response * @param handler

SpringBoot系列:Spring Boot异步调用@Async

在实际开发中,有时候为了及时处理请求和进行响应,我们可能会多任务同时执行,或者先处理主任务,也就是异步调用,异步调用的实现有很多,例如多线程.定时任务.消息队列等, 这一章节,我们就来讲讲@Async异步方法调用. 一.@Async使用演示 @Async是Spring内置注解,用来处理异步任务,在SpringBoot中同样适用,且在SpringBoot项目中,除了boot本身的starter外,不需要额外引入依赖. 而要使用@Async,需要在启动类上加上@EnableAsync主动声明来开启异

SpringBoot系列:Spring Boot集成定时任务Quartz

一.关于Quartz Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.在java企业级应用中,Quartz是使用最广泛的定时调度框架. 在Quartz中的主要概念: Scheduler:调度任务的主要API ScheduleBuilder:用于构建Scheduler,例如其简单实现类SimpleScheduleBuilder Job:调度任务执行的接口,也即定时任务执行的方法 JobDetai

spring boot web开发 简单的增删改查和spring boot 自带的Junit测试 案例

创建 web项目 配置pom.xml文件   ------相当于jar包 配置application.yml -----配置文件(spring数据库连接.server服务.logging日志等) 创建 package  (entity.controller.service.configuration.repository.interceptor等) 小例子: entity:Person 数据持久化(dao): controller: spring 自带Junit测试@SpringBootTest

SpringBoot系列:Spring Boot定时任务Spring Schedule

Spring Schedule是Spring提供的定时任务框架,相较于Quartz,Schedule更加简单易用,在中小型应用中,对于大部分需求,Schedule都可以胜任. 一.Spring Schedule使用演示 在 SpringBoot使用Spring Schedule非常简单,因为SpringBoot自身的starter中已经集成了Schedule,而不需要我们做更多的处理. 使用@EnableScheduling注解开启定时功能,该注解可以使用在启动类上,也可以注解于定时任务的类上.

Spring boot - Web综合开发(转)

Web 开发 Spring Boot Web 开发非常的简单,其中包括常用的 json 输出.filters.property.log 等 json 接口开发 在以前使用 Spring 开发项目,需要提供 json 接口时需要做哪些配置呢 添加 jackjson 等相关 jar 包 配置 Spring Controller 扫描 对接的方法添加 @ResponseBody 就这样我们会经常由于配置错误,导致406错误等等,Spring Boot 如何做呢,只需要类添加 @RestControll