概述
Spring中常见的全局过滤方式有三种:Filter、Conterceptor、Aspect。
- 三者的执行顺序是:Filter > Conterceptor > Aspect。
- Filter在请求到达具体的Controller之前执行,所以无法获取到Controller相关的数据,仅仅能处理请求及响应数据流。
- Conterceptor可以获取到处理的Controller方法的相关的数据,但是此时的Controller是以静态方法(非动态)的形式出现的。
- Aspect可以获取到赋值给处理方法的参数值。
- 还有一个自定义参数解析器,可以针对某个参数实现一些特殊过滤逻辑(可能用来从cookie中解析出来真实用户)。
一、过滤器 Filter
属于 javax.servlet.Filter 技术。
1、定义具体逻辑
public class XxxxFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
/*前置过滤逻辑*/
filterChain.doFilter(servletRequest, servletResponse);
/*后置过滤逻辑*/
}
}
2、配置该过滤器
// 1、 web.xml方式配置
<filter>
// filter名,随便起
<filter-name>AdminFilter</filter-name>
// 实现filter的类
<filter-class>cn.kihyou.b2c.filter.AdminFilter</filter-class>
// 初始化参数
<init-param></init-param>
</filter>
<filter-mapping>
// 对应的filter名
<filter-name>AdminFilter</filter-name>
// 要进行拦截过滤的目录
<url-pattern>/api/admin/*</url-pattern>
</filter-mapping>
// 2、注解的形式配置
在 Filter 的实现类上添加 @WebFilter 注解
eg. @WebFilter(filterName="log",urlPatterns={"/*"})
二、拦截器 Conterceptor
属于 org.springframework.web.servlet.HandlerInterceptor 技术。
1、定义具体逻辑:
public class XxxxInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
/*前置过滤逻辑*/
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, ModelAndView modelAndView) throws Exception {
// 其中 handler参数为处理该请求的 Controller 方法
/*后置过滤逻辑1*/
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception e) throws Exception {
/*后置过滤逻辑2,常用于清理资源*/
}
}
2、配置该拦截器
// 1、 SpringMVC的配置文件中
<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 配置一个全局拦截器,拦截所有请求 -->
<bean class="interceptor.TestInterceptor" />
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<!-- 配置不需要拦截作用的路径 -->
<mvc:exclude-mapping path="" />
<!-- 定义<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
<bean class="interceptor.Interceptor1" />
</mvc:interceptor>
</mvc:interceptors>
// 2、注解的形式配置
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private XxxxInterceptor xxxxInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(xxxxInterceptor);
registration.addPathPatterns("/**"); //所有路径都被拦截
registration.excludePathPatterns("/static/*.html","/static/*.js",) //添加不拦截的路径
}
}
Tips:SpringMVC的配置文件有两种方式:
项目中到底使用的是哪一种,需要在在web.xml中查看 <servlet></servlet>配置。
// 1、 指定位置
<servlet>
<servlet-name>dispatcherSerlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value> // 指定SpringMVC的配置文件是 classess文件夹下的springmvc.xml文件
</init-param>
</servlet>
// 2、默认位置
<servlet>
<servlet-name>dispatcherSerlet</servlet-name> // 默认SpringMVC的配置文件是:在WEB-INF文件夹下的 {此处的servlet-name}-servlet.xml
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
三、切面 Aspect
属于 org.aspectj.lang.annotation.Aspect 技术。
1、定义作用标识的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XxxxXxx {
}
2、定义具体逻辑
@Aspect
@Component
public class XxxxAop{
//定义切点
@Pointcut("@annotation(com.xxx.annotation.XxxxXxx)")
public void audit() {
}
@Around("audit()")
public Object handleControllerMethod(ProceedingJoinPoint point) throws Throwable {
/*前置过滤逻辑*/
Object returnValue = point.proceed()
/*后置过滤逻辑*/
}
@Before("audit()")
public void before(JoinPoint joinPoint) {
/*前置过滤逻辑*/
}
@After("audit()")
public void after(JoinPoint joinPoint) {
/*后置过滤逻辑*/
}
@AfterReturning()
public void afterReturning(JoinPoint joinPoint, Object result) {}
@AfterThrowing()
public void afterThrowing(JoinPoint joinPoint, Throwable exception) {}
}
3、配置启动AOP
// 1、xml文件配置
<!-- 声明类 ,此类作为切面类使用 -->
<bean id="logInterceptor" class="com.aop.LogInterceptor" />
<aop:config>
<!-- 设置切面名,及切面类 -->
<aop:aspect id="logAspect" ref="logInterceptor">
<!-- 运行前方法配置,先择要执行的方法 ,并设置切入点 -->
<aop:before method="before" pointcut="execution(public * com.userService.*.add(..))" />
<!-- 先设置切入点,待使用 -->
<aop:pointcut id="servicePointcut" expression="execution(public * com.userService.*.add(..))" />
<!-- 运行后方法配置,先择要执行的方法,参考预先设置好的切入点 -->
<aop:after method="after" pointcut-ref="servicePointcut" />
</aop:aspect>
</aop:config>
// 2、启动注解配置, Spring默认不支持@Aspect风格的切面声明,通过如下配置开启@Aspect支持
<aop:aspectj-autoproxy/>
四、自定以参数解析器
属于 org.springframework.web.method.support.HandlerMethodArgumentResolver 技术。
对于大型项目,通常会有很多引用包,其中封装了一些自定义注解,具体的实现逻辑就需要好好翻翻。
1、定义作用标识的注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface XxxxXxx {
}
2、定义具体逻辑
public class XxxxHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
// 用于判定是否需要处理该参数,返回 true 为需要,并会去调用下面的方法resolveArgument。
return methodParameter.getParameterType().isAssignableFrom(UserParam.class)
&& methodParameter.hasParameterAnnotation(XxxxXxx.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
// 具体的得到参数值的逻辑,return 返回参数值
return value;
}
}
3、配置该参数解析器
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new XxxxXxxHandlerMethodArgumentResolver());
}
}
五、寻找非当前项目实现的自定义参数的逻辑代码位置Tips
当前项目声明的自定义参数实现逻辑通过在当前项目文件夹搜索HandlerMethodArgumentResolver 便可快速定位。
第一步、首先找到一个使用位置
第二步、定位到具体的声明位置
第三步、通过 Find Usages 功能查找resolve的相关文件
关注公众号获取更多干货