简介:过滤器、拦截器、切面
如何选择?
Filter过滤器
过滤器可以拦截到方法的请求和响应ServletRequest request, ServletResponse response
,并对请求响应做出过滤操作
过滤器依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,一个过滤器实例只能在容器初始化时调用一次。
作用:在服务器获取数据的前提对数据进行过滤
springboot中添加Filter的方法
①@WebFilter注解配置
@WebFilter
用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 )
示例1
#在config包建立过滤器类实现Filter,添加注解@WebFilter
package com.example.fdemo.config;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "myFilter",urlPatterns = "/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("----MyFilter过滤器初始化----");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 对request和response进行一些预处理
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("MyFilter执行前!!!");
chain.doFilter(request, response); // 让目标资源执行,放行
System.out.println("MyFilter执行后!!!");
}
@Override
public void destroy() {
System.out.println("----MyFilter过滤器销毁----");
}
}
#启动类添加扫描包
package com.example.fdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan("com.example.fdemo.config")
public class FdemoApplication {
public static void main(String[] args) {
SpringApplication.run(FdemoApplication.class, args);
}
}
②通过FilterRegistrationBean来完成配置
示例2
#可以去掉注解@WebFilter
package com.example.fdemo.config;
import javax.servlet.*;
import javax.servlet.FilterConfig;
import java.io.IOException;
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("----MyFilter初始化----");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 对request和response进行一些预处理
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("MyFilter执行前!!!");
chain.doFilter(request, response); // 让目标资源执行,放行
System.out.println("MyFilter执行后!!!");
}
@Override
public void destroy() {
System.out.println("----MyFilter过滤器销毁----");
}
}
#主要包括实例化Filter类,然后指定url的匹配模式,设置过滤器名称和执行顺序
package com.example.fdemo.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean registFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyFilter());
registration.addUrlPatterns("/*");
registration.setName("myFilter");
registration.setOrder(1);
return registration;
}
}
Interceptor拦截器
依赖于web框架,在SpringBoot中就是依赖于SpringBoot框架.
在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用
作用:就是在一个方法前,调用一个方法,或者在方法后,调用一个方法。
springboot中添加Interceptor的方法
这里我们需要实现HandlerInterceptor
这个接口,这个接口包括三个方法
1.preHandle
是请求执行前执行的
2.postHandler
是请求结束执行的,但只有preHandle方法返回true的时候才会执行
3.afterCompletion
是视图渲染完成后才执行,同样需要preHandle返回true,该方法通常用于清理资源等工作。
package com.example.fdemo.config;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
long start = System.currentTimeMillis();//获得当前的时间
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
start = System.currentTimeMillis();
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor cost="+(System.currentTimeMillis()-start));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
除了实现上面的接口外,我们还需对其进行配置:实现WebMvcConfigurer
package com.example.fdemo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//主要配置项就两个,一个是指定拦截器,第二个是指定拦截的URL。
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/*");
}
}
Aspect切面
AOP操作可以对操作进行横向的拦截,最大的优势在于他可以获取执行方法的参数,对方法进行统一的处理。常见使用日志,事务,请求参数安全验证等。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
spring最重要的有个知识点就是切面编程AOP
@Aspect注解将表示它是一个切面
@Component表示它是一个Spring的组件
定义切入点表达式
例如定义切入点表达式 execution (* com.sample.service.impl..*. *(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
JoinPoint 对象
JoinPoint
对象封装了SpringAop
中切面方法的信息,在切面方法中添加JoinPoint
参数,就可以获取到封装了该方法信息的JoinPoint
对象.
常用API:
方法名 | 功能 |
---|---|
Signature getSignature()
|
获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息 |
Object[] getArgs()
|
获取传入目标方法的参数对象 |
Object getTarget()
|
获取被代理的对象 |
Object getThis()
|
获取代理对象 |
例子
package com.example.fdemo.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
public class MyAspect {
// 切入点
@Pointcut("execution (* com.example.fdemo.controller.HomeController.GetWg(..))")
public void cut() {}
/**
* 业务方法之前执行
* @param jp
*/
@Before("cut()")
public void before(JoinPoint jp) {
System.out.println("业务方法之前执行");
//获得请求属性对象.
ServletRequestAttributes sc=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//获得请求对象
HttpServletRequest request=sc.getRequest();
//获得请求的url,port,ip,method等等.
System.out.println("url==>"+request.getRequestURI());
System.out.println("port==>"+request.getServerPort());
System.out.println("ip==>"+request.getRemoteHost());
System.out.println("method==>"+request.getMethod());
System.out.println("class==>"+jp.getSignature().getDeclaringTypeName()+"."+jp.getSignature().getName());
//获取传入目标方法的参数
Object[] args = jp.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i+1) + "个参数为:" + args[i]);
}
System.out.println("被代理的对象:" + jp.getTarget());
System.out.println("代理对象自己:" + jp.getThis());
}
/**
* 业务方法之后执行
* @param jp
*/
@After("cut()")
public void after(JoinPoint jp) {
System.out.println("业务方法之后.....");
}
/**
* 执行方法后返回值
* @param result
*/
@AfterReturning(pointcut = "cut()", returning = "result")
public void afterReturning(Object result) {
System.out.println("执行返回值:" + result);
}
}
HomeController.java
package com.example.fdemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class HomeController {
@RequestMapping(value = "/")
public String GetHome(){
return "index";
}
@RequestMapping(value = "/wg")
public String GetWg(Model model,
@RequestParam(value = "a", required = false) String ma,@RequestParam(value = "b",required = false) String mb){
model.addAttribute("a",ma);
model.addAttribute("b",mb);
return "zyx";
}
}
访问url:http://localhost:8080/wg?a=xing&b=wang
控制台输出
总结
三者方式同时采用,那他们的执行顺序是什么呢?
filter -> interceptor -> ControllerAdvice -> aspect -> controller
返回值顺序,或异常返回顺序
controller -> aspect -> controllerAdvice -> Interceptor -> Filter
参考文章https://www.jianshu.com/p/50341c4f3519