【Spring】Filter、Interceptor、Aspect的设计及区别

Filter、Interceptor、Aspect的设计及区别

在这里插入图片描述

1) Filter

定义

基于Servlet架构,Spring无法控制,也无法拿到Spring控制的参数

public class TimeFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        long start = new Date().getTime();
        chain.doFilter(request, response);
        System.out.println("time filter" + (new Date().getTime() - start));
    }
}

原理: 函数回调

Spring Boot引入第三方Filter

  • 使用application.xml的形式

  • Spring boot的纯注解模式,外部的Filter可能不带Component

    1. 新增配置类
    2. 定义FilterRegistrationBean为返回值的bean
    @Configuration
    public class FilterRegister {
    
        @Bean
        public FilterRegistrationBean timeFilter(){
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
            TimeFilter timeFilter = new TimeFilter();
            List<String> urls = new ArrayList<>();
            urls.add("/testFilter");
            filterRegistrationBean.setFilter(timeFilter);
            filterRegistrationBean.setUrlPatterns(urls);
            return filterRegistrationBean;
        }
    
    }
    

Filter的不足

  • 只拥有ServletRequestServletResponse 对象,无法获取Controller相关的方法信息

2) Interceptor

定义

来自Spring MVC,能够对Spring管理的Controller 进行方法粒度的拦截,能够获得方法名对应的controller的bean,request.getParam可以获取基于Servlet的参数,@RequestBodyspring管理的参数无法解析获得

public class TimeInterceptor implements HandlerInterceptor {

    /**
     * @param handler
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return false;
    }
    
    @Override
    public void postHandle(...){}

    @Override
    public void afterCompletion(...){}
}

原理: 反射

HandlerInterceptor

  • preHandle 返回布尔值,若返回false 调用链断开
  • preHandle返回true , postHandle 按照preHandle的执行顺序倒叙执行

WebRequestInterceptor

  • preHandle 无返回值,常常用作资源准备

  • WebRequestsetAttribute(name, value, scope)

    scope

    • SCOPE_REQUEST,表示只有在request中可以访问。
    • SCOPE_SESSION,如果环境允许的话,它表示的是一个局部的隔离的session,否则就代表普通的session,并且在该session范围内可以访问。
    • SCOPE_GLOBAL_SESSION,如果环境允许的话,它表示的是一个全局共享的session,否则就代表普通的session,并且在该session范围内可以访问。

更详细的资料:详述 Spring MVC 框架中拦截器 Interceptor 的使用方法

MVC中的Handler

指的是RequestMapping标记的方法

DispatcherServlet源码看handler的获取
在这里插入图片描述

DispatcherServlet.getrHandler() 源码看 Interceptor

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

HandlerExecutionChain 内部是有Interceptor 的容器,也即是Spring 拦截器作用的类
在这里插入图片描述

使用

  1. 继承HandlerInterceptorAdapter 或实现 HandlerInterceptor
  2. 同理,还可以继承WebRequestInterceptorAdapter 或实现 WebRequestInterceptor

注入拦截器-XML

<mvc:interceptors>  
    <!-- 使用 bean 定义一个 Interceptor,这个位置的bean将将拦截所有的请求 -->  
    <bean class="com.hit.interceptor.WrongCodeInterceptor"/>  
    <mvc:interceptor>  
        <mvc:mapping path="/demo/hello.do"/>  
        <!-- 定义在 mvc:interceptor 下面的 Interceptor,表示对特定的请求进行拦截 -->  
        <bean class="com.hit.interceptor.LoginInterceptor"/>  
    </mvc:interceptor>  
</mvc:interceptors>  

注入拦截器-Spring Boot

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private TimeInterceptor timeInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(timeInterceptor);
        interceptorRegistration.addPathPatterns("/**");
    }
}

Interceptor 的不足

无法获取请求参数拼装后的结果,@RequestBody 反序列化成的参数将不会出现在拦截器里。

3) Aspect

定义

面向切面编程,进过Spring MVCInterceptor后再进入ControllerAdvice (一般用作异常处理)

再进入到ControllerContrller 需要用到动态代理的技术实现,Spring 使用两种技术实现AOP

  • JDK 动态代理 - 实现类
  • CGLib 动态代理 - 非实现类强制使用

两种代理技术的区别

JDK 只能代理接口,通过字节码底层继承要代理类来实现除了final 类 都可以被代理。

原理: 动态代理

名词解释

JoinPoint: 连接点

​ 类里面哪些方法可以被增强,这些方法称为连接点. 在spring的AOP中,指的是所有现有的方法。

Pointcut: 切入点

​ 在类里面可以有很多方法被增强,但是实际开发中,我们只对具体的某几个方法而已,那么这些实际增强的方法就称之为切入点

Advice: 通知/增强

​ 增强的逻辑、称为增强,比如给某个切入点(方法) 扩展了校验权限的功能,那么这个校验权限即可称之为增强 或者是通知

​ 通知分为:

  • Before 前置通知: 在原来方法之前执行.
  • AfterReturning 后置通知: 在原来方法之后执行. 特点: 可以得到被增强方法的返回值
  • Around 环绕通知:在方法之前和方法之后执行. 特点:可以阻止目标方法执行
  • AfterThrowing 异常通知: 目标方法出现异常执行. 如果方法没有异常,不会执行. 特点:可以获得异常的信息
  • After 最终通知: 指的是无论是否有异常,总是被执行的。
Aspect: 切面

​ 切入点和通知的结合。

使用

配置切面-XML

	<bean id="myAscept" class="com.itheima.aspect.MyAscept"/>
    <aop:config>
        <aop:pointcut id="pointCut01" expression="execution(* com.itheima.dao.impl.AccountDaoImpl.save(..))"/>
        <!--配置切面(切入点和通知的结合)-->
     	<aop:aspect ref="myAscept">
            <!--前置通知-->
            <aop:before method="checkPrivilege" pointcut-ref="pointCut01">						</aop:before>
       	</aop:aspect>
    </aop:config> 

配置切面-Spring Boot

@Aspect
@Component
public class TimeAspect {

    /**
     * 环绕通知, 代理包下的所有方法
     * @param joinPoint 切入点(被增强的方法)
     * @return 被增强的返回值
     */
    @Around("execution(* com.changgou.aspect.TimeController.*(..))")
    public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("time aspect start");
        for (Object arg : joinPoint.getArgs()) {
            System.out.println(arg);
        }
        // 调用原来的方法
        Object proceed = joinPoint.proceed();
        System.out.println("time aspect end");
        return proceed;
    }
}

Aspect 的不足

获取原生的ServletRequest 需要借助 RequestContextHolder

对于分布式应用,基于ThreadLocal的实现是有很大瓶颈的

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页