乐趣区

关于springboot:Springboot-系列六web-开发之拦截器和三大组件

文章曾经收录在 Github.com/niumoo/JavaNotes,更有 Java 程序员所须要把握的外围常识,欢送 Star 和指教。
欢送关注我的公众号,文章每周更新。

1. 拦截器

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

  1. preHandle
    进入 Habdler 办法之前执行,个别用于身份认证受权等。
  2. postHandle
    进入 Handler 办法之后返回 modelAndView 之前执行,个别用于塞入公共模型数据等。
  3. afterCompletion
    最初解决,个别用于日志收集,对立后续解决等。

<!– more –>

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。

最初的话

文章曾经收录在 Github.com/niumoo/JavaNotes,欢送 Star 和指教。更有一线大厂面试点,Java 程序员须要把握的外围常识等文章,也整顿了很多我的文字,欢送 Star 和欠缺,心愿咱们一起变得优良。

文章有帮忙能够点个「」或「 分享 」,都是反对,我都喜爱!
文章每周继续更新,要实时关注我更新的文章以及分享的干货,能够关注「未读代码」公众号或者我的博客。

退出移动版