SpringBoot Listener、Filter、Interceptor、ControllerAdvice、Aspect、Controller

文章目录

前言

一、介绍

1.监听器(Listener)

2.过滤器(Filter)

3.拦截器(Interceptor)

4.全局处理数据(ControllerAdvice)

5.aspect(AOP)

6.区别

二、监听器使用

1.监听器接口介绍

2.实现步骤

三、过滤器使用

1.Filter接口介绍

2.实现方式

3.实践步骤

 方式一:Filter+@Configuration

方式二:@WebFilter 注解来

四、拦截器使用

1.HandlerInterceptor接口介绍

2.WebMvcConfigurer接口介绍

3.实践步骤

4.WebMvcConfigurer接口

4.1 addInterceptors:拦截器配置

4.2 configurePathMatch  路径匹配规则

4.3 configureContentNegotiation  内容协商策略

4.4 configureAsyncSupport  异步调用支持

4.5 configureDefaultServletHandling  静态资源默认处理器

4.6 addFormatters  格式化器和转换器

4.7 addInterceptors  拦截器

4.8 addResourceHandlers  静态资源处理器

4.9 addCorsMappings  跨域设置

4.10 addViewControllers  视图控制器

4.11 configureViewResolvers  视图解析器

4.12 addArgumentResolvers  参数解析器

4.13 addReturnValueHandlers  返回值处理器

4.14 configureMessageConverters 信息转化器

4.15 extendMessageConverters  信息转化器

4.16 configureHandlerExceptionResolvers  异常处理器

4.17 extendHandlerExceptionResolvers  异常处理器扩展

5.总结

五.(ControllerAdvice)使用

1.实现说明

2.实践步骤

六.aspect(AOP)使用

1.介绍

2.实现方式

3.实践步骤


提示:

以下是本篇文章正文内容,初级菜鸟水平有限,下面案例可供参考(我是Super Rookie

前言

本文介绍SpringBoot 中的Listener、Filter、Interceptor、AOP、 @ControllerAdviceController的区别及其用法。

整体顺序为:Listener=> Filter=> Interceptor=> AOP=> @ControllerAdvice=> Controller。

执行顺序概述

如果Listener、Filter、Interceptor、AOP、@ControllerAdvice的@ModelAttribute、@ControllerAdvice的@ExceptionHandler、@ControllerAdvice实现beforeBodyWrite接口都存在,则它们的执行顺序为:

不抛异常执行顺序:
Listener=> Filter>Interceptor(前处理)=> AOP(前处理)=> @ControllerAdvice的@ModelAttribute=> 自己的controller=> AOP(后处理)=> @ControllerAdvice实现beforeBodyWrite接口=> Interceptor(后处理)

抛异常执行顺序:
Listener=> Filter=> Interceptor(前处理)=> AOP(前处理)=> @ControllerAdvice的@ModelAttribute=> 自己的controller=> AOP(后处理)=>  @ControllerAdvice的@ExceptionHandler=> @ControllerAdvice实现beforeBodyWrite接口=> Interceptor(后处理)。

调用顺序

Filter前=> Interceptor前=> AOP=> Controller=> AOP=> Interceptor后=> Filter后


一、介绍

1.监听器(Listener)

主要用来监听对象的创建与销毁等状态的变化以及定义一些事件发生后接下来要进行的动作;
主要监听的三个域对象为:ServletRequest域、HttpSession域 和ServletContext域;

使用场景

用于统计在线人数和在线用户,
系统启动时加载初始化信息,
统计网站访问量,
记录用户访问路径

2.过滤器(Filter)

拦截请求,对请求或响应(Request、Response)统一设置,对请求、响应头加密、解密,对请求、响应压缩,过滤掉非法url,做网关转发等

使用场景 
统一设置编码 
跨域设置 
过滤敏感字符(防止sql注入) 
登录校验 
URL级别的访问权限控制 
数据压缩

3.拦截器(Interceptor)

作用于controller层,拦截到请求,对请求或响应(Request、Response)统一设置进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等和filter的作用有些类似,但二者又有不同的应用场景。

使用场景
登录验证,判断用户是否登录。
权限验证,判断用户是否有权限访问资源,如校验token
日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。
处理cookie、本地化、国际化、主题等。
性能监控,监控请求处理时长等。
通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现)

4.全局处理数据(ControllerAdvice)

@ControllerAdvice 就是 @Controller 的增强版。
@RestControllerAdvice = @ControllerAdvice + @ResponseBody

@ControllerAdvice 是在类上声明的注解,其用法主要有三种场景:

  • @ExceptionHandler(全局异常处理):用于捕获Controller中抛出的不同类型的异常,从而达到异常全局处理的目的;
  • @InitBinder(全局数据预处理):用于请求中注册自定义参数的解析,从而达到自定义请求参数格式的目的;
  • @ModelAttribute(全局数据绑定):表示此方法会在执行目标Controller方法之前执行

异常类:

5.aspect(AOP)

AOP(Aspect Oriented Programming)是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,即解剖对象的内部,将那些影响了多个类的公共行为抽取到一个可重用模块里,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性。AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理、增强处理。
 

使用场景
记录日志
监控方法运行时间 (监控性能)
权限控制
缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )

6.区别

在springboot中使用rest服务时,往往需要对controller层的请求进行拦截或者获取请求数据和返回数据,就需要过滤器、拦截器或者切片。

区别
过滤器(Filter):对HttpServletRequest处理,也可以对HttpServletResponse 进行后处理,无法获取请求方法的信息。


拦截器(Interceptor):可以获取HttpServletRequest、HttpServletResponse的数据,也可以获取请求方法的信息,但是无法获取请求的参数和返回参数。


AOP切片(Aspect):aop的切片可以获取请求的参数和返回的值,但是无法获取HttpServletRequest、HttpServletResponse的数据。

二、监听器使用

1.监听器接口介绍

以下来源网络摘录

1.ServletContextListener -- 监听servletContext对象的创建以及销毁
    1.1    contextInitialized(ServletContextEvent arg0)   -- 创建时执行
    1.2    contextDestroyed(ServletContextEvent arg0)  -- 销毁时执行
    
2.HttpSessionListener  -- 监听session对象的创建以及销毁
    2.2   sessionCreated(HttpSessionEvent se)   -- 创建时执行
    2.2   sessionDestroyed(HttpSessionEvent se) -- 销毁时执行

3.ServletRequestListener -- 监听request对象的创建以及销毁
    3.1    requestInitialized(ServletRequestEvent sre) -- 创建时执行
    3.2    requestDestroyed(ServletRequestEvent sre) -- 销毁时执行

4.ServletContextAttributeListener  -- 监听servletContext对象中属性的改变
    4.1    attributeAdded(ServletContextAttributeEvent event) -- 添加属性时执行
    4.2    attributeReplaced(ServletContextAttributeEvent event) -- 修改属性时执行
    4.3    attributeRemoved(ServletContextAttributeEvent event) -- 删除属性时执行

5.HttpSessionAttributeListener  --监听session对象中属性的改变
    5.1    attributeAdded(HttpSessionBindingEvent event) -- 添加属性时执行
    5.2    attributeReplaced(HttpSessionBindingEvent event) -- 修改属性时执行
    5.3    attributeRemoved(HttpSessionBindingEvent event) -- 删除属性时执行

6.ServletRequestAttributeListener  --监听request对象中属性的改变
    6.1    attributeAdded(ServletRequestAttributeEvent srae) -- 添加属性时执行
    6.2    attributeReplaced(ServletRequestAttributeEvent srae) -- 修改属性时执行
    6.3    attributeRemoved(ServletRequestAttributeEvent srae) -- 删除属性时执行

2.实现步骤

呀 呀  呀  这里怎么忘记写案例了..

三、过滤器使用

1.Filter接口介绍

Filter接口包含三个方法: 初始化方法init,拦截方法doFilter,销毁方法destroy
package javax.servlet;
public interface Filter {
    /**
     *  servlet 容器在实例化过滤器后只调用一次 init 方法。
	 *	init 方法必须成功完成,然后才会要求筛选器执行任何筛选工作。
     */
    public default void init(FilterConfig filterConfig) throws ServletException {}
    /**
     *doFilter每次由于客户端请求链末端的资源而导致请求/响应对通过链时,容器都会调用筛选器的方法。
	传递给此方法的筛选器链允许筛选器将请求和响应传递给链中的下一个实体。
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

        /*由 Web 容器调用,以向筛选器指示它正在停止服务。
        仅当筛选器的 doFilter 方法中的所有线程退出或超时期限过后,才会调用此方法。
        Web 容器调用此方法后,它不会在此筛选器实例上再次调用 doFilter 方法。 
        此方法使筛选器有机会清理正在保留的任何资源(例如,内存、文件句柄、线程),
        并确保任何持久状态与筛选器在内存中的当前状态同步。默认实现是无操作的。*/
    public default void destroy() {}
}

2.实现方式

        方式一:实现Filter过滤器接口+@Configuration
                【1、实现过滤器  2、配置过滤器(1>注册过滤器  2>指定实现类和规则)】

        方式二:通过@WebFilter 注解来配置;
                【1、@WebFilter注解实现过滤器  2、启动类添加@ServletComponentScan】

3.实践步骤

方式一:

  1. 定义过滤器;(MyFilter1.java、MyFilter2.java  定义2个过滤器并实现过滤器Filter接口)
  2. 配置过滤器;(MyFilterConfig.java  1>.注册过滤器   2>.配置规则)
  3. 控制器测试。

方式二:

        1.定义过滤器;(MyFilter3.java   1.添加@WebFilter过滤器的注解 2.实现过滤器Filter接口)
        2.启动配置@ServletComponentScan;
        3.控制器测试。

项目目录

 方式一:Filter+@Configuration

MyFilter1.java 过滤器1 

package com.xl.filter;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * TODO
 * 1.定义过滤器 MyFilter1
 * 过滤器 Filter [简单案例]
 * 需要MyFilterConfig 配置器
 *
 * filter的作用:拦截请求,对请求或响应(Request、Response)统一设置,
 * 对请求、响应头加密、解密,对请求、响应压缩,过滤掉非法url,做网关转发等
 * 直接实现Filter接口,并使用@Component注解标注为组件自动注入bean
 * @author xiaolan
 */
@Slf4j
public class MyFilter1 implements Filter {

    protected Logger log = LoggerFactory.getLogger(MyFilter1.class);

    // 构造方法
    public MyFilter1() {
        log.info("MyFilter1构造方法");
    }

    /**
     * 初始化  创建对象后马上被调用,用来对Filter做一些初始化的操作
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        log.info("MyFilter1过滤器-初始化");
    }

    /**
     * 过滤 【重点】核心方法,过滤器的入口,用来处理过滤逻辑,可通过过滤器链对象的doFilter方法进行放行
     * @param servletRequest 请求对象
     * @param servletResponse 响应对象
     * @param filterChain 过滤器链对象,调用doFilter方法进行放行
     * @throws IOException IO异常
     * @throws ServletException Servlet异常
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        request.setCharacterEncoding("UTF-8");
        log.info("MyFilter1过滤器-拦截url:" + request.getRequestURI());
        log.info("MyFilter1 请求参数 token==" + servletRequest.getParameter("token"));

        /* ------ 跨域 ------ */
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");

        /* ------ Token验证 ------ */
        //进行校验,如token校验
        final String TOKEN = "676765765765765765";
        //获取请求头中Header的验证token
        //String token = request.getHeader("token");
        //获取请求Parameter的验证token
        String token = request.getParameter("token");
        if (TOKEN.equals(token)) {
            //token 校验通过
            log.info("token校验通过");
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            //token校验不通过,重定向到登录页面
            log.info("token校验不通过,重定向到登录页面");
        }
    }

    /**
     * 销毁  服务器停止时调用,用来释放资源。
     */
    @Override
    public void destroy() {
        Filter.super.destroy();
        log.info("MyFilter1过滤器-销毁");
    }
}

MyFilter2.java 过滤器2

package com.xl.filter;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * TODO
 * 1.定义过滤器 MyFilter2
 * 过滤器 Filter [简单案例]
 * 需要MyFilterConfig 配置器
 *
 * filter的作用:拦截请求,对请求或响应(Request、Response)统一设置,
 * 对请求、响应头加密、解密,对请求、响应压缩,过滤掉非法url,做网关转发等
 * 直接实现Filter接口,并使用@Component注解标注为组件自动注入bean
 * @author xiaolan
 */
@Slf4j
public class MyFilter2 implements Filter {

    protected Logger log = LoggerFactory.getLogger(MyFilter2.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        log.info("MyFilter2过滤器-初始化");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        log.info("MyFilter2过滤器-拦截url:"+request.getRequestURI());
        log.info("MyFilter2 请求参数 token==" + servletRequest.getParameter("token"));
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
        log.info("MyFilter2过滤器-销毁");
    }
}

MyFilterConfig.java 配置文件

package com.xl.config;

import com.xl.filter.MyFilter1;
import com.xl.filter.MyFilter2;
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.WebMvcConfigurationSupport;

/**
 * TODO
 * 2.配置过滤器【  1>.注册过滤器   2>.配置规则 】
 *  1>.注册过滤器:将过滤器注册到spring ioc容器;
 *  2>.指定过滤器规则:实现WebMvcConfigurationSupport接口,重写业务需要的内部方法;
 * @author xiaolan
 */

@Configuration
public class MyFilterConfig extends WebMvcConfigurationSupport {

    /**
     * 注册过滤器MyFilter1到容器中
     * @return
     */
    @Bean
    public MyFilter1 myFilter1(){
        return new MyFilter1();
    }

    /**
     * 注册过滤器MyFilter2到容器中
     * @return
     */
    @Bean
    public MyFilter2 myFilter2(){
        return new MyFilter2();
    }


    /**
     * MyFilter1
     * @return
     */
    @Bean
    public FilterRegistrationBean setFilter1() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        //添加Filter实现类
        filterRegistrationBean.setFilter(myFilter1());
        //匹配路径
        filterRegistrationBean.addUrlPatterns("/demo/info1/*");
        //order的数值越小,在所有的filter中优先级越高
        filterRegistrationBean.setOrder(1);
        return filterRegistrationBean;
    }

    /**
     * MyFilter2
     * @return
     */
    @Bean
    public FilterRegistrationBean setFilter2(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        //添加Filter实现类
        filterRegistrationBean.setFilter(myFilter2());
        //匹配路径
        filterRegistrationBean.addUrlPatterns("/demo/info2/*");
        //order的数值越小,在所有的filter中优先级越高
        filterRegistrationBean.setOrder(2);
        return filterRegistrationBean;
    }

}

访问测试

http://localhost:8080/demo/info1?token=676765765765765765

方式二:@WebFilter 注解来

MyFilter3.java 定义过滤器

        1.定义过滤器;(MyFilter3.java   1.添加@WebFilter过滤器的注解 2.实现过滤器Filter接口)
        2.启动类 添加@ServletComponentScan;

package com.xl.filter;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * TODO
 *  自定义注解
 *  基于@WebFilter()注解  方式
 *  不需要MyFilterConfig配置器配置
 *
 * @author xiaolan
 */
@Slf4j
/** @WebFilter过滤器的注解 ,urlPatterns="/*" 定义过滤器过滤的路径  **/
@WebFilter(urlPatterns = "/demo/info3/*",filterName = "MyFilter3")
public class MyFilter3 implements Filter {

    protected Logger log = LoggerFactory.getLogger(MyFilter3.class);

    /**
     * 初始化
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        log.info("MyFilter3过滤器-初始化");
    }

    /**
     * 过滤
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        log.info("MyFilter3过滤器-拦截url:"+request.getRequestURI());
        log.info("MyFilter3 请求参数 token==" + servletRequest.getParameter("token"));

        filterChain.doFilter(servletRequest, servletResponse);
    }

    /**
     * 销毁
     */
    @Override
    public void destroy() {
        Filter.super.destroy();
        log.info("MyFilter3过滤器-销毁");
    }

}

访问测试

http://localhost:8080/demo/info3?token=676765765765765765

四、拦截器使用

1.HandlerInterceptor接口介绍

public interface HandlerInterceptor {

    default boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, java.lang.Object handler) throws java.lang.Exception { /* compiled code */ }

    default void postHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, java.lang.Object handler, @org.springframework.lang.Nullable org.springframework.web.servlet.ModelAndView modelAndView) throws java.lang.Exception { /* compiled code */ }

    default void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, java.lang.Object handler, @org.springframework.lang.Nullable java.lang.Exception ex) throws java.lang.Exception { /* compiled code */ }
}

1>、preHandle:前置拦截 预处理
       调用前提:已实现方法
       调用时机:Controller方法调用之前
       应用场景:登录拦截、编码处理、安全控制、权限效验等

2>、postHandle:后置拦截 处理
       调用前提:preHandle前置处理完,并返回true
       调用时机:Controller方法调用之后,DispatcherServlet进行视图渲染之前调用
       应用场景:可以修改ModelAndView,这个比较少用

2>、afterCompletion:结束后拦截 处理
       调用前提:preHandle前置处理完,并返回true
       调用时机:DispatcherServlet进行视图的渲染之后
       应用场景:清理资源等

interceptor 的执行顺序

1. 请求到达 DispatcherServlet
2. DispatcherServlet 发送至 Interceptor ,执行 preHandle
3. 请求达到 Controller
4. 请求结束后,postHandle 执
5. afterCompletion,请求结束后调用,可以用来统计请求耗时等

2.WebMvcConfigurer接口介绍

public interface WebMvcConfigurer {
    default void configurePathMatch(org.springframework.web.servlet.config.annotation.PathMatchConfigurer configurer) { /* compiled code */ }

    default void configureContentNegotiation(org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer configurer) { /* compiled code */ }

    default void configureAsyncSupport(org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer configurer) { /* compiled code */ }

    default void configureDefaultServletHandling(org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer configurer) { /* compiled code */ }

    default void addFormatters(org.springframework.format.FormatterRegistry registry) { /* compiled code */ }

    default void addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry registry) { /* compiled code */ }

    default void addResourceHandlers(org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry registry) { /* compiled code */ }

    default void addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry registry) { /* compiled code */ }

    default void addViewControllers(org.springframework.web.servlet.config.annotation.ViewControllerRegistry registry) { /* compiled code */ }

    default void configureViewResolvers(org.springframework.web.servlet.config.annotation.ViewResolverRegistry registry) { /* compiled code */ }

    default void addArgumentResolvers(java.util.List<org.springframework.web.method.support.HandlerMethodArgumentResolver> resolvers) { /* compiled code */ }

    default void addReturnValueHandlers(java.util.List<org.springframework.web.method.support.HandlerMethodReturnValueHandler> handlers) { /* compiled code */ }

    default void configureMessageConverters(java.util.List<org.springframework.http.converter.HttpMessageConverter<?>> converters) { /* compiled code */ }

    default void extendMessageConverters(java.util.List<org.springframework.http.converter.HttpMessageConverter<?>> converters) { /* compiled code */ }

    default void configureHandlerExceptionResolvers(java.util.List<org.springframework.web.servlet.HandlerExceptionResolver> resolvers) { /* compiled code */ }

    default void extendHandlerExceptionResolvers(java.util.List<org.springframework.web.servlet.HandlerExceptionResolver> resolvers) { /* compiled code */ }

    @org.springframework.lang.Nullable
    default org.springframework.validation.Validator getValidator() { /* compiled code */ }

    @org.springframework.lang.Nullable
    default org.springframework.validation.MessageCodesResolver getMessageCodesResolver() { /* compiled code */ }
}

WebMvcConfigurer接口介绍

public interface WebMvcConfigurer {

    default void configurePathMatch  路径匹配规则
    default void configureContentNegotiation  内容协商策略
    default void configureAsyncSupport  异步调用支持
    default void configureDefaultServletHandling  静态资源默认处理器
    default void addFormatters  格式化器和转换器
    default void addInterceptors  拦截器
    default void addResourceHandlers  静态资源处理器
    default void addCorsMappings  跨域设置
    default void addViewControllers  视图控制器
    default void configureViewResolvers  视图解析器

    default void addArgumentResolvers  参数解析器
    default void addReturnValueHandlers  返回值处理器
    default void configureMessageConverters 信息转化器
    default void extendMessageConverters  信息转化器
    default void configureHandlerExceptionResolvers  异常处理器
    default void extendHandlerExceptionResolvers  异常处理器扩展

}

3.实践步骤

1>.创建 MyInterceptor 定义拦截器实现类 (实现HandlerInterceptor接口,重写preHandle、postHandle、afterCompletion方法)

2>.创建 MvcConfig  配置拦截器 1.注册拦截器:将拦截器注册到spring ioc容器 2.指定拦截器规则:实现WebMvcConfigurer接口,重写业务需要的内部方法;)

3>.创建 ExampleController 控制器(访问请求测试)

项目目录

4.WebMvcConfigurer接口

4.1 addInterceptors:拦截器配置

(1) 实现HandlerInterceptor接口

创建  MyInterceptor.java 拦截器,内容如下:

/**
 * 1.定义拦截器
 * @author xiaolan
 */
@Slf4j
public class MyInterceptor 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.debug("【前置拦截】这是MyInterceptor的preHandle方法。");
        return true; //如果返回false,请求将会被拦截
    }

    /**
     * 后置拦截
     * 请求处理之后进行调用,但是在视图被渲染之前(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.debug("【后置拦截】这是MyInterceptor的postHandle方法。");
    }

    /**
     * 结束后拦截
     * 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
     * @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.debug("【结束后拦截】这是MyInterceptor的afterCompletion方法。");
    }
}

(2)  实现WebMvcConfigurer接口

创建  MvcConfig.java 配置拦截器,(1.注册拦截器:将拦截器注册到spring ioc容器  2.指定拦截器规则:实现WebMvcConfigurer接口,重写业务需要的内部方法;)

案例 addInterceptors:拦截器

  • addInterceptor:需要一个实现HandlerInterceptor接口的拦截器实例
  • addPathPatterns:用于设置拦截器的过滤路径规则;
  • addPathPatterns("/**")对所有请求都拦截
  • excludePathPatterns:用于设置不需要拦截的过滤规则

代码如下(示例):

/**
 * 2。定义配置类,配置拦截器
 * 实现 WebMvcConfigurer 接口
 */
@Slf4j
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    /**
     * 将拦截器注册到spring ioc容器
     * @return
     */
    @Bean
    public MyInterceptor myInterceptor(){
        log.info("========= 注册拦截器---myInterceptor =========");
        return new MyInterceptor();
    }

    /**
     *  拦截器配置
     * 重写该方法;往拦截器链添加自定义拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        addInterceptor:需要一个实现HandlerInterceptor接口的拦截器实例
//        addPathPatterns:用于设置拦截器的过滤路径规则;addPathPatterns("/**")对所有请求都拦截
//        excludePathPatterns:用于设置不需要拦截的过滤规则
//        拦截器主要用途:进行用户登录状态的拦截,日志的拦截等

        //通过registry添加myInterceptor拦截器,并设置拦截器路径为 /*
        registry.addInterceptor(myInterceptor())
                // 设置拦截器的过滤路径规则
                .addPathPatterns("/*")
                //设置不需要拦截的过滤规则
                .excludePathPatterns("/demo/info");
    }

//    /* 视图跳转控制器 */
//    /* 静态资源处理 */
//    /* 默认静态资源处理器 */
//    /* 这里配置视图解析器 */
//    /* 配置内容裁决的一些选项*/
//    /** 解决跨域问题 **/

}

(3)  访问ExampleController

0>.ExampleController 内容

/**
 * 测试控制器
 * @author xiaolan
 */
@Slf4j
@RestController
@RequestMapping("/demo")
public class ExampleController {

    /**
     * 验证:拦截
     * @return
     */
    @GetMapping("/interceptor")
    public Map<String, Object> interceptor() {
        System.out.println("========= 执行 controller 测试拦截=========");
        System.out.println("========= 调用 service 进行业务处理 =========");
        //body体
        Map<String, Object> mListBody = Maps.newLinkedHashMap();

        Map<String, Object> map = Maps.newLinkedHashMap();
        map.put("id", "324354");
        map.put("name", "测试拦截");
        //body 本次请求的体
        mListBody.put("data", map);
        mListBody.put("code", "200");
        // msg 错误描述
        mListBody.put("msg", "请求成功");
        return mListBody;
    }

    /**
     * 验证:不拦截,放行
     * @return
     */
    @GetMapping("/info")
    public Map<String, Object> getInterceptor(){
        log.info("=========  执行 controller 测试放行=========");
        //body体
        Map<String, Object> mListBody = Maps.newLinkedHashMap();
        //模块数据
        Map<String, Object> mListType = Maps.newLinkedHashMap();

        // map 数据A
        List<Map<String, Object>> mapList_a= Lists.newArrayList();
        for (int i = 0; i < 3; i++){
            Map<String, Object> map = Maps.newLinkedHashMap();
            map.put("id", i);
            map.put("name", "A-xl-"+i);
            map.put("sex", "男");
            map.put("createTime", new Date());
            mapList_a.add(map);
        }
        
        mListType.put("list-A", mapList_a);
        
        //body 本次请求的体
        mListBody.put("data", mListType);
        mListBody.put("code", "200");
        // msg 错误描述
        mListBody.put("msg", "请求成功");
        return mListBody;
    }

}

1>.启动项目
将拦截器注册到spring ioc容器,如图

2>.访问ExampleController 验证 HandlerInterceptor 执行

到此简单拦截器已完成。

4.2 configurePathMatch  路径匹配规则


4.3 configureContentNegotiation  内容协商策略


4.4 configureAsyncSupport  异步调用支持


4.5 configureDefaultServletHandling  静态资源默认处理器


4.6 addFormatters  格式化器和转换器


4.7 addInterceptors  拦截器


4.8 addResourceHandlers  静态资源处理器


4.9 addCorsMappings  跨域设置

addCorsMappings  添加规则

    /**
     * 此种设置跨域的方式,在自定义拦截器的情况下可能导致跨域失效
     * 原因:当跨越请求在跨域请求拦截器之前的拦截器处理时就异常返回了,那么响应的response报文头部关于跨域允许的信息就没有被正确设置,导致浏览器认为服务不允许跨域,而造成错误。
     * 解决:自定义跨域过滤器解决跨域问题(该过滤器最好放在其他过滤器之前)
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*") //浏览器允许所有的域访问 / 注意 * 不能满足带有cookie的访问,Origin 必须是全匹配
                .allowedHeaders("*")
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .allowCredentials(true) // 允许带cookie访问
                .maxAge(3600);
    }


4.10 addViewControllers  视图控制器


4.11 configureViewResolvers  视图解析器


4.12 addArgumentResolvers  参数解析器


4.13 addReturnValueHandlers  返回值处理器


4.14 configureMessageConverters 信息转化器


4.15 extendMessageConverters  信息转化器


4.16 configureHandlerExceptionResolvers  异常处理器


4.17 extendHandlerExceptionResolvers  异常处理器扩展

5.总结

  1. 统⼀⽤户登录权限的效验使⽤ WebMvcConfigurer+ HandlerInterceptor来实现

  2. 统⼀异常处理使⽤ @ControllerAdvice + @ExceptionHandler 来实现

  3. 统⼀返回值处理使⽤ @ControllerAdvice + ResponseBodyAdvice 来处理

五.(ControllerAdvice)使用

1.实现说明

统⼀全局异常处理 :@ControllerAdvice + @ExceptionHandler

统⼀全局数据预处理:@ControllerAdvice +@InitBinder

统⼀全局数据绑定   :@ControllerAdvice +@ModelAttribute

2.实践步骤

2.1 创建类

GlobalControllerAdvice 增强器 
ExampleController 控制器

2.2 目录结构

 2.3  GlobalControllerAdvice

/**
 * Controller 增强器
 * @author xiaolan
 */
@Slf4j
@ControllerAdvice
public class GlobalControllerAdvice {
    //================= @ModelAttribute 全局数据绑定 =================

    //应用到所有@RequestMapping注解方法

    //   1.【使用Model】无返回值方法,放入Model,自定义 key ,value
    @ModelAttribute
    public void modelInfo(Model model) {
        log.info("全局数据绑定:ModelAttribute");
        HashMap<String, String> map = new HashMap<>();
        map.put("name", "XiaoLan");
        map.put("age", "10");
        model.addAttribute("mapList", map);
    }

    // 2.不指定name,返回值方法,返回值是ModelMap
    @ModelAttribute()
    public Map<String, String> presetParam2(ModelMap modelMap) {
        HashMap<String, String> map = new HashMap<>();
        map.put("name", "XiaoLan");
        map.put("age", "10");
        modelMap.addAttribute("modelMap", map);
        return map;
    }

    // 3.指定name,返回值方法,返回值是map
    @ModelAttribute(name = "map3")
    public Map<String, String> presetParam3() {
        Map<String, String> map = new HashMap<String, String>();
//        HashMap<String, String> map = new HashMap<>();
        map.put("name", "XiaoLan");
        map.put("age", "10");
        return map;
    }

    // 4.可以接受请求参数
    @ModelAttribute()
    public void presetParam4(@RequestParam("name") String name, Model model) {
        model.addAttribute("name", name);
    }

    //================= @InitBinder 全局数据预处理 =================
    //(2)全局数据预处理
    //应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
    //用来设置WebDataBinder
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        log.info("全局数据预处理:InitBinder");
        // 只要是String类型,就去除字符串前后的空格
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));

        // 格式化date
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        CustomDateEditor dateEditor = new CustomDateEditor(df, true);
        binder.registerCustomEditor(Date.class, dateEditor);
    }

    //================= @ExceptionHandler 全局异常处理 =================

    /**
     * 未知异常
     * java.lang.Exception
     * @param req
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Object exceptionHandler(HttpServletRequest req, Exception e) {
        log.error("全局异常处理:Exception");
        log.error("URL : " + req.getRequestURL().toString());
        log.error("HTTP_METHOD : " + req.getMethod());
        log.error("未知异常!原因是:",e.getMessage());
        return Result.failure().message(e.getMessage());
    }

    /**
     * 算术异常
     * java.lang.ArithmeticException
     * @param e
     * @param request
     * @return
     */
    @ResponseBody
    @ExceptionHandler(ArithmeticException.class)
    public Object arithmeticExceptionHandler(ArithmeticException e, HttpServletRequest request){
        log.error("全局异常处理:ArithmeticException");
        StringBuffer requestURL = request.getRequestURL();
        log.error("请求地址'{}',异常'{}'", requestURL, e.getMessage());
        return Result.failure().message(e.getMessage());
    }

    /**
     * 运行异常
     * java.lang.RuntimeException
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(RuntimeException.class)
    public Object runtimeExceptionHandler(RuntimeException e) {
        log.error("全局异常处理:RuntimeException");
        return Result.failure().message(e.getMessage());
    }

    /**
     * 空指针异常
     * java.lang.NullPointerException
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(NullPointerException.class)
    public Object nullPointerExceptionHandler(NullPointerException e) {
        log.error("全局异常处理:NullPointerException");
        return Result.failure().message(e.getMessage());
    }


    /**
     * 类型转换异常
     * java.lang.ClassCastException
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(ClassCastException.class)
    public Object classCastExceptionHandler(ClassCastException e) {
        log.error("全局异常处理:ClassCastException");
        return Result.failure().message(e.getMessage());
    }

    /**
     * IO异常 入流和输出流时可能出现的异常
     * java.lang.IOException
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(IOException.class)
    public Object iOExceptionHandler(IOException e) {
        log.error("全局异常处理:IOException");
        return Result.failure().message(e.getMessage());
    }

    /**
     * 未知方法异常  方法未找到抛出的异常
     * java.lang.NoSuchMethodException
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(NoSuchMethodException.class)
    public Object noSuchMethodExceptionHandler(NoSuchMethodException e) {
        log.error("全局异常处理:NoSuchMethodException");
        return Result.failure().message(e.getMessage());
    }

}

2.4  ExampleController

/**
 * @author xiaolan
 */
@Slf4j
@RestController
@RequestMapping("/demo")
public class ExampleController {

    //================= @ModelAttribute 全局数据绑定 =================

    //1.使用Model取出 globalAttr全局参数
    @GetMapping("getModel")
    public Map<String, Object> modelInfo(Model model) {
        Map<String, Object> modelMap = model.asMap();

        // 获取全局数据
        Map<String, Object> mListBody = Maps.newLinkedHashMap();
        Map<String, Object> map = Maps.newLinkedHashMap();
        // 获取全局数据
        Map<String, String> info = (Map<String, String>)modelMap.get("mapList");
        map.put("mapList", info);
        mListBody.put("data",map);
        mListBody.put("code", "200");
        mListBody.put("msg", "请求成功");
        return mListBody;
    }

    //2.使用ModelMap取出
    @GetMapping("getModelMap")
    public Map<String, Object> getPresetParam2(ModelMap modelMap) {
        Map<String, Object> map = Maps.newLinkedHashMap();
        map.put("data", modelMap.get("modelMap").toString());
        map.put("code", "200");
        map.put("msg", "请求成功");
        return map;
    }

    //3.@ModelAttribute()指定key,直接取出
    @GetMapping("getModelAttribute")
    public Map<String, Object> getPresetParam3(@ModelAttribute("map3") Map maplist) {
        log.info(maplist.toString());
        Map<String, Object> map = Maps.newLinkedHashMap();
        map.put("data", maplist.toString());
        map.put("code", "200");
        map.put("msg", "请求成功");
        return map;
    }

    //4.使用Model取出 请求的输入的参数
    @GetMapping("getPresetParam4")
    public Map<String, Object> presetParam4(Model model) {
        Map<String, Object> modelMap = model.asMap();

        Map<String, Object> mListBody = Maps.newLinkedHashMap();
        Map<String, Object> map = Maps.newLinkedHashMap();
        map.put("name", modelMap.get("name").toString());
        mListBody.put("data",map);
        mListBody.put("code", "200");
        mListBody.put("msg", "请求成功");
        return mListBody;
    }

    //================= @InitBinder 全局数据预处理 =================
    /**
     * 全局数据预处理
     *
     * @return
     */
    @GetMapping("/getInitBinder")
    public Result getInitBinder(String param,Date date){
        Map<String, Object> map = Maps.newLinkedHashMap();
        map.put("date", date);
        map.put("param", param);
        return Result.success().data(map);
    }


    //================= @ExceptionHandler 全局异常处理 =================

    /**
     * 正常返回
     * @return
     */
    @GetMapping("/getInfo")
    public Result getInfo(){
        //body体
        Map<String, Object> mListBody = Maps.newLinkedHashMap();
        //模块数据
        Map<String, Object> mListType = Maps.newLinkedHashMap();
        // map 数据A
        List<Map<String, Object>> mapList_a= Lists.newArrayList();
        for (int i = 0; i < 2; i++){
            Map<String, Object> map = Maps.newLinkedHashMap();
            map.put("id", i);
            map.put("name", "A-xl-"+i);
            map.put("sex", "男");
            map.put("createTime", new Date());
            mapList_a.add(map);
        }
        // map 数据B
        List<Map<String, Object>> mapList_b = Lists.newArrayList();
        for (int i2 = 0; i2 < 1; i2++){
            Map<String, Object> map = Maps.newLinkedHashMap();
            map.put("id", i2);
            map.put("name", "B-xl-"+i2);
            map.put("sex", "女");
            map.put("createTime", new Date());
            mapList_b.add(map);
        }
        mListType.put("list-A", mapList_a);
        mListType.put("list-B", mapList_b);
        return Result.success().data(mListType);
    }

    /**
     * 通用返回失败
     * @return
     */
    @GetMapping("/failure")
    public Result failure() {
        Map m = null;
        return Result.failure().data(m);
    }

    /**
     * 未知异常
     * @throws Exception
     */
    @RequestMapping("/exception")
    public void no() throws Exception{
        throw new Exception("未知异常");
    }

    /**
     * 算术异常
     * @return
     */
    @GetMapping("/arithmeticException")
    public Integer arithmeticException(){
        int res  = 1/0;
        return res;
    }

    /**
     * 运行异常
     * @throws RuntimeException
     */
    @RequestMapping("/runtimeException")
    public void runtimeException() throws RuntimeException {
        throw new RuntimeException("运行异常");
    }

    /**
     * 空指针异常
     * @throws NullPointerException
     */
    @RequestMapping("/nullPointerException")
    public void nullPointerException() throws NullPointerException {
        throw new NullPointerException("空指针异常");
    }

    /**
     * 类型转换异常
     * @throws ClassCastException
     */
    @RequestMapping("/classCastException")
    public void classCastException() throws ClassCastException {
        throw new ClassCastException("类型转换异常");
    }

    /**
     * IO异常
     * @throws IOException
     */
    @RequestMapping("/iOException")
    public void iOException() throws IOException {
        throw new IOException("IO异常");
    }

}

2.5  测试

//================= @ModelAttribute 全局数据绑定 =================
http://localhost:8080/demo/getModel?v=1.1&name="hello"
http://localhost:8080/demo/getModelMap?v=1.1&name="hello"
http://localhost:8080/demo/getModelAttribute?v=1.1&name="hello"
http://localhost:8080/demo/getPresetParam4?v=1.1&name="hello"

//================= @InitBinder 全局数据预处理 =================
http://localhost:8080/demo/getInitBinder?v=1.1&date=2022-10-11 03:25:00&name=hello&param=hello 

//================= @ExceptionHandler 全局异常处理 =================
正常返回
http://localhost:8080/demo/getInfo?v=1.1&name="hello"

通用返回失败
http://localhost:8080/demo/failure?v=1.1&name="hello"

未知异常
http://localhost:8080/demo/exception?v=1.1&name="hello"

算术异常
http://localhost:8080/demo/arithmeticException?v=1.1&name="hello"

运行异常
http://localhost:8080/demo/runtimeException?v=1.1&name="hello"

空指针异常
http://localhost:8080/demo/nullPointerException?v=1.1&name="hello"

类型转换异常
http://localhost:8080/demo/classCastException?v=1.1&name="hello"

IO异常
http://localhost:8080/demo/iOException?v=1.1&name="hello"

未知方法异常
http://localhost:8080/demo/noSuchMethodException?v=1.1&name="hello"
//================= @ModelAttribute 全局数据绑定 =================

//================= @InitBinder 全局数据预处理 =================

//================= @ExceptionHandler 全局异常处理 =================

六.aspect(AOP)使用

1.介绍

2.实现方式

        注解方式:
        1>. 创建切面类:LogAspect.java (声明切面、优先级、切点、定义通知)
         2>. 创建控制器:ExampleController

3.实践步骤

3.1 引入依赖

<!-- SpringBoot 拦截器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.2 声明切面

@AspectJ注解对POJO进行标注,该注解表明该类不仅仅是一个POJO,还是一个切面。切面是切点和通知的结合,那么定义一个切面就需要编写切点和通知。在代码中,只需要添加@Aspect注解即可。

切面类 @Aspect: 定义切面类,加上@Aspect、@Component注解

3.3 定义切点

切点是通过@Pointcut注解和切点表达式定义的。

 切点常用方法

1)execution(public * (..))——表示匹配所有public方法
2)execution( set(..))——表示所有以“set”开头的方法
3)execution( com.xl.service.AccountService.(..))——表示匹配所有AccountService接口的方法
4)execution( com.xl.service..(..))——表示匹配service包下所有的方法
5)execution(* com.xl.service...(..))——表示匹配service包和它的子包下的方法
 

切入点,即能通过@PointCut中的模式字符串匹配到的方法。模式字符串有多种写法,可以分为类使用、方法使用、实例使用三类:

方法使用:

execution:用于匹配方法执行的连接点
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法
@args:用于匹配当前执行的方法传入的参数是指定类型的
@annotation:用于匹配当前执行方法持有指定注解的方法
切点引入,在切点定义类中对应方法上定义@PointCut,然后在@Aspect类中使用完整方法路径()引用,以分离切点定义和使用,方便切点集中管理


类使用:

within:用于匹配指定类内的方法执行
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配
@within:用于匹配所以持有指定注解的类型内的方法
@target:用于配当前目标对象类型的执行方法,其中目标对象持有指定的注解


对象使用:

bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法
 

3.4 定义通知

通知有五种类型,分别是:

注解名称描述
前置通知(@Before)在目标方法调用之前调用通知
后置通知(@After)在目标方法完成之后调用通知
环绕通知(@Around)在被通知的方法调用之前和调用之后执行自定义的方法
返回通知(@AfterReturning)在目标方法成功执行之后调用通知
异常通知(@AfterThrowing)在目标方法抛出异常之后调用通知

  • 正常情况下执行顺序:环绕通知(@Around)begin ->前置通知(@Before)->返回通知(@AfterReturning)->后置通知(@After)->环绕通知(@Around)end
  • 有异常情况下执行顺序:
    环绕通知(@Around)begin->前置通知(@Before)->返回通知(@AfterReturning)->后置通知(@After)->异常通知(@AfterThrowing)Around end 逻辑不会执行
  • 当有多有切面时,按照 @Order 顺序依次执行,从上面日志可以看出

3.5 LogAspect

打印请求信息

/**
 * TODO
 * 日志切面 AOP [简单案例]
 * @author xiaolan
 */

@Slf4j
@Aspect //声明切面 (日志切面)
@Component
@Order(1) //标记切点的优先级,i越小,优先级越高
public class LogAspect {

    /** 定义横切点,标记方法 **/
    @Pointcut("execution(public * com.xl.web.*.*(..))")
    public void webLog(){}

    /**
     * 前置通知,切点之前执行
     * 前置通知:在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        log.info("============= @Before 前置通知 begin =============");

        log.info("前置通知:模拟执行权限检查...");
        log.info("------------- 目标信息 -------------");
        log.info("目标类是:" + joinPoint.getTarget());
        log.info("目标方法所属类的包名+类名:" + joinPoint.getSignature().getDeclaringTypeName());
        log.info("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
        log.info("目标方法包+名:" + joinPoint.getSignature());
        log.info("目标方法名:" + joinPoint.getSignature().getName());
        log.info("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));

        log.info("------------- 获得客户机信息 -------------");

        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 记录下请求内容
        log.info("请求方式Method: " + request.getMethod());
        log.info("客户端请求URL: " + request.getRequestURL());
        log.info("URL部分参数: " + request.getRequestURI());
        log.info("方法请求参数: " + request.getQueryString());
        log.info("客户端主机名: " + request.getRemoteHost());
        log.info("客户端IP地址: " + request.getRemoteAddr());
        log.info("客户端端口: " + request.getRemotePort());

        log.info("------------- 方法 参数 信息 -------------");
        log.info("CLASS_METHOD 请求类方法ClassMethod: " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("ARGS 请求参数Args: " + Arrays.toString(joinPoint.getArgs()));


        log.info("============= @Before 前置通知 end =============");
    }

    /**
     * 切点执行成功之后执行
     * 后置通知:在某连接点正常完成后执行的通知,通常在一个匹配的方法返回的时候执行
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        log.info("============= @AfterReturning 后置通知 begin =============");
        // 处理完请求,返回内容
        log.info("方法的返回值 : " + ret);
        log.info("============= @AfterReturning 后置通知 end =============");
    }

    /**
     * 后置异常通知,切点抛出异常后执行
     * 异常通知:在方法抛出异常退出时执行的通知。 
     * @param jp
     */
    @AfterThrowing("webLog()")
    public void throwss(JoinPoint jp){
        log.info("============= @AfterThrowing 异常通知 begin =============");
        log.info("方法异常时执行.....");
        log.info("============= @AfterThrowing 异常通知 end =============");
    }

    /**
     * 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行;切点执行之后执行
     * @param jp
     */
    @After("webLog()")
    public void after(JoinPoint jp){
        log.info("============= @After 最终通知 begin =============");
        log.info("方法最后执行.....");
        log.info("============= @After 最终通知 end =============");
    }

    /**
     * 环绕通知,环绕增强,相当于MethodInterceptor
     * 绕通知:包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。
     * 它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object arround(ProceedingJoinPoint pjp) throws Throwable{
        log.info("============= @Around 环绕通知 begin =============");
        Object obj =null;
        log.info("方法环绕start.....");
        try {
            //前置通知
            log.info("目标方法执行前...");

            //耗时计时器(ms级别)
            StopWatch stopWatch = new StopWatch();
            //记录开始时间点
            stopWatch.start();
            //执行目标方法
            obj = pjp.proceed(pjp.getArgs());
            //记录结束时间点
            stopWatch.stop();
            //最终耗时ms
            long cost = stopWatch.getTotalTimeMillis();
            //
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            String methodName = signature.getDeclaringTypeName() + "." + signature.getName();

            log.info("执行方法:" + methodName);
            log.info("执行方法耗时(ms):" + cost + "ms");
            log.info("方法环绕proceed,结果是 :" + obj);
        } catch (Throwable e) {
            //异常通知
            log.info("执行目标方法异常后...");
            throw new RuntimeException(e);
        }
        //后置通知
        log.info("目标方法执行后...");

        log.info("============= @Around 环绕通知 end =============");
        return obj;
    }

}

3.6 Controller

/**
 * TODO
 * 测试控制器
 */
@RestController
@RequestMapping("/demo")
public class ExampleController {


    /**
     * AOP 控制器 测试
     * @return
     */
    @GetMapping("/info")
    public Map<String, Object> getInfo(String v ){
        //body体
        Map<String, Object> mListBody = Maps.newLinkedHashMap();
        //模块数据
        Map<String, Object> mListType = Maps.newLinkedHashMap();

        // map 数据A
        List<Map<String, Object>> mapList_a= Lists.newArrayList();
        for (int i = 0; i < 1; i++){
            Map<String, Object> map = Maps.newLinkedHashMap();
            map.put("id", i);
            map.put("name", "A-xl-"+i);
            map.put("sex", "男");
            map.put("createTime", new Date());
            mapList_a.add(map);
        }
        // map 数据B
        List<Map<String, Object>> mapList_b = Lists.newArrayList();
        for (int i2 = 0; i2 < 1; i2++){
            Map<String, Object> map = Maps.newLinkedHashMap();
            map.put("id", i2);
            map.put("name", "B-xl-"+i2);
            map.put("sex", "女");
            map.put("createTime", new Date());
            mapList_b.add(map);
        }

        mListType.put("list-A", mapList_a);
        mListType.put("list-B", mapList_b);
        mListBody.put("body", mListType);
        mListBody.put("status", "200");
        mListBody.put("msg", "请求成功");
        return mListBody;
    }


    /**
     * AOP 测试异常
     * @return
     */
    @GetMapping("/error")
    public Map<String, Object> getError(String v ){
        //body体
        Map<String, Object> mListBody = Maps.newLinkedHashMap();
        //模块数据
        Map<String, Object> mListType = Maps.newLinkedHashMap();
        // map 数据A
        List<Map<String, Object>> mapList_a= Lists.newArrayList();
        for (int i = 0; i < 1; i++){
            Map<String, Object> map = Maps.newLinkedHashMap();
            map.put("id", i);
            map.put("name", "A-xl-"+i);
            map.put("sex", "男");
            map.put("createTime", new Date());
            mapList_a.add(map);
        }
        mListType.put("list-A", mapList_a);
        //body 本次请求的体
        mListBody.put("body", mListType);
        mListBody.put("status", "200");
        // msg 错误描述
        mListBody.put("msg", "请求成功");
        int i=5/0; 
        return mListBody;
    }
}

3.7 测试结果正常情况下执行顺序
正常情况下执行顺序

环绕通知(@Around)begin ->前置通知(@Before)->返回通知(@AfterReturning)->后置通知(@After)->环绕通知(@Around)end

访问地址:http://localhost:8080/demo/info?v=1.1

有异常情况下执行顺序
 

环绕通知(@Around)begin->前置通知(@Before)->返回通知(@AfterReturning)->后置通知(@After)->异常通知(@AfterThrowing)环绕通知(@Around) end 逻辑不会执行

访问地址:http://localhost:8080/demo/error?v=1.1

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值