学习了这个久对于以上的四个在javaweb开发中经常会用的的几个东西相信很多java开发人员都会遇到,为了能够更好的区分以上的概念以及运用场景,我将对过滤器、监听器、拦截器进行收集和整理。
概念
首先我们需要先认识下servlet这个东西,servlet是一种运行在服务器端的java应用程序,具有独立于平台和协议的特性,可以处理客户端请求,执行service,可以动态生成web页面,工作在客户端请求与服务器相应的中间层。
生命周期:
装入(服务器)-> 初始化 -> 调用 -> 销毁。
(1)、装入:启动服务器时加载Servlet的实例;
(2)、初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工作有init()方法负责执行完成;
(3)、调用:从第一次到以后的多次访问,都是只调用doGet()或doPost()方法;
(4)、销毁:停止服务器时调用destroy()方法,销毁实例。
监听器(Listener)
是servlet规范中定义的一种特殊类,用于监听servletContext、HttpSession、servletRequest等域对象的创建和销毁、属性修改事件,用于在事件发生前和发生后进行处理。实现了javax.servlet.ServletContextListener 接口的服务器端程序;
随web应用的启动而启动;只初始化一次;
随web应用的停止而销毁;
作用:
做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。如SpringMVC的监听器org.springframework.web.context.ContextLoaderListener,实现了SpringMVC容器的加载、Bean对象创建、DispatchServlet初始化等。
使用示例:
1.统计在线人数和在线用户
2.系统启动时加载初始化信息
3.统计网站访问量
4.跟spring结合
使用方式:
1.基本类实现
1、监听域对象自身的创建和销毁的事件监听器创建类并且实现以下接口:
servletContextListener、HttpSessionListener、ServletRequestListener
2、监听域对象中的属性的增加和删除的事件监听器创建类并且实现以下接口:
ServletContextAttributeListener\HttpSessionAttributeListener\ServletRequestAttributeListener
3、监听绑定到HttpSession域中的某个对象的状态的事件监听器实现两个接口
HttpSessionBindingListener()\HttpSessionActivationListener()
- 在servlet3.0下监听器的用法
使用注解@WebListener
该注解用于将类声明为监听器,被@WebListener标注的类必须实现以下至少一个接口:
ServletContextListener
ServletContextAttributeListener
HttpSessionListener
HttpSessionAttributeListener
ServletRequestListener
ServletRequestAttributeListener
- 使用springboot进行事件监听有四种方式:
1.手工向ApplicationContext中添加监听器
2.将监听器装载入spring容器
3.在application.properties中配置监听器
4.通过@EventListener注解实现事件监听。
示例1:
public class MyListener1 implements ApplicationListener<MyEvent>
{
Logger logger = Logger.getLogger(MyListener1.class);
public void onApplicationEvent(MyEvent event)
{
logger.info(String.format("%s监听到事件源:%s.", MyListener1.class.getName(), event.getSource()));
}
}
@SpringBootApplication
public class LisenterApplication
{
public static void main(String[] args)
{
ConfigurableApplicationContext context = SpringApplication.run(LisenterApplication.class, args);
//装载监听
context.addApplicationListener(new MyListener1());
}
}
过滤器(Filter)
Java的过滤器能够为我们提供系统级别的过滤,也就是说,能过滤所有的web请求,这一点,是拦截器无法做到的。
在Java Web中,传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者struts的action前统一设置字符集,或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话)。
filter 流程是线性的,url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收。
使用示例
- 过滤非法url
2.过滤非法字符(低俗字符)
3.做过滤操作获取我么你想要的数据(过滤器中修改字符编码)。
使用方式
1.springboot+servlet3.0注释方式
编写实现接口的类+web.xml中配置
过滤器只要实现javax.servlet.filter重写doFilter(...)、init(...)和destroy(..)方法即可
实现doFilter方法,完成对请求或响应的过滤
实现init方法,读取过滤器的初始化参数destroy(),过滤器销毁的时候做一些操作.
@Order(1)
@WebFilter(filterName = "piceaFilter", urlPatterns = "/*" , initParams = {
@WebInitParam(name = "URL", value = "http://localhost:8080")})
public class PiceaFilter implements Filter {
private String url;
/**
* 可以初始化Filter在web.xml里面配置的初始化参数
* filter对象只会创建一次,init方法也只会执行一次。
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.url = filterConfig.getInitParameter("URL");
System.out.println("我是过滤器的初始化方法!URL=" + this.url + ",生活开始.........");
}
/**
* 主要的业务代码编写方法
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("我是过滤器的执行方法,客户端向Servlet发送的请求被我拦截到了");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("我是过滤器的执行方法,Servlet向客户端发送的响应被我拦截到了");
}
/**
* 在销毁Filter时自动调用。
*/
@Override
public void destroy() {
System.out.println("我是过滤器的被销毁时调用的方法!,活不下去了................" );
}
}
启动类中增加注解,自动注册Filter
@ServletComponentScan :在SpringBootApplication上使用@ServletComponentScan注解后,Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码。
拦截器
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
使用示例:
处理所有请求共性问题:
1、乱码问题:用request,response参数去设置编码;
2、解决权限验证问题(是否登陆,取session对象查看)
拦截器与过滤器的区别
1、拦截器Interceptor依赖于框架容器,基于反射机制,只过滤请求
2、过滤器Filter依赖于Servlet容器,基于回调函数,过滤范围大
使用方式
- springboot的方式
继承WebMvcConfigurerAdapter spring5.0 以弃用,不推荐
实现WebMvcConfigurer 推荐
继承WebMvcConfigurationSupport 会导致springboot自动配置失效。
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
private HandlerInterceptor urlInterceptor;
private static List<String> myPathPatterns = new ArrayList<>();
/**
* 在初始化Servlet服务时(在Servlet构造函数执行之后、init()之前执行),@PostConstruct注解的方法被调用
*/
@PostConstruct
void init() {
System.out.println("Servlet init ... ");
// 添加匹配的规则, /** 表示匹配所有规则,任意路径
myPathPatterns.add("/**");
}
/**
* 在卸载Servlet服务时(在Servlet的destroy()方法之前执行),@PreDestroy注解的方法被调用
*/
@PreDestroy
void destroy() {
System.out.println("Servlet destory ... ");
}
/**
* 注册配置的拦截器
* @param registry 拦截器注册器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// addPathPatterns 用于添加拦截规则
// excludePathPatterns 用户排除拦截
registry.addInterceptor(urlInterceptor).addPathPatterns(myPathPatterns).excludePathPatterns("/user/login");
}
// 下面的方法可以选择性重写
/**
* 添加类型转换器和格式化器
* @param registry
*/
@Override
public void addFormatters(FormatterRegistry registry) {
// registry.addFormatterForFieldType(LocalDate.class, new USLocalDateFormatter());
}
/**
* 跨域支持
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600 * 24);
}
/**
* 添加静态资源映射--过滤swagger-api
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//过滤swagger
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/swagger-resources/**")
.addResourceLocations("classpath:/META-INF/resources/swagger-resources/");
registry.addResourceHandler("/swagger/**")
.addResourceLocations("classpath:/META-INF/resources/swagger*");
registry.addResourceHandler("/v2/api-docs/**")
.addResourceLocations("classpath:/META-INF/resources/v2/api-docs/");
}
/**
* 配置消息转换器--这里用的是ali的FastJson
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//1. 定义一个convert转换消息的对象;
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
//2. 添加fastJson的配置信息,比如:是否要格式化返回的json数据;
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteDateUseDateFormat);
//3处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
//4.在convert中添加配置信息.
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
//5.将convert添加到converters当中.
converters.add(fastJsonHttpMessageConverter);
}
/**
* 访问页面需要先创建个Controller控制类,再写方法跳转到页面
* 这里的配置可实现直接访问http://localhost:8080/toLogin就跳转到login.jsp页面了
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/toLogin").setViewName("login");
}
/**
* 开启默认拦截器可用并指定一个默认拦截器DefaultServletHttpRequestHandler,比如在webroot目录下的图片:xx.png,
* Servelt规范中web根目录(webroot)下的文件可以直接访问的,但DispatcherServlet配置了映射路径是/ ,
* 几乎把所有的请求都拦截了,从而导致xx.png访问不到,这时注册一个DefaultServletHttpRequestHandler可以解决这个问题。
* 其实可以理解为DispatcherServlet破坏了Servlet的一个特性(根目录下的文件可以直接访问),DefaultServletHttpRequestHandler
* 可以帮助回归这个特性的
* @param configurer
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
// 这里可以自己指定默认的拦截器
configurer.enable("DefaultServletHttpRequestHandler");
}
/**
* 在该方法中可以启用内容裁决解析器,configureContentNegotiation()方法是专门用来配置内容裁决参数的
* @param configurer
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// 表示是否通过请求的Url的扩展名来决定media type
configurer.favorPathExtension(true)
// 忽略Accept请求头
.ignoreAcceptHeader(true)
.parameterName("mediaType")
// 设置默认的mediaType
.defaultContentType(MediaType.TEXT_HTML)
// 以.html结尾的请求会被当成MediaType.TEXT_HTML
.mediaType("html", MediaType.TEXT_HTML)
// 以.json结尾的请求会被当成MediaType.APPLICATION_JSON
.mediaType("json", MediaType.APPLICATION_JSON);
}
}
启动顺序:
监听器 > 过滤器 > 拦截器.
三者关系
取材自:
https://blog.csdn.net/c_royi/article/details/80563131
https://segmentfault.com/a/1190000017342619?utm_source=tag-newest