发布时间:2022-08-17 14:35
过滤器是一个基于Servlet的程序,它先于Servlet或JSP执行,因此常常被用作请求前的信息检查,修改编码,重定向请求路径等功能。
SpringBoot定义一个web过滤器只需要其继承HttpFilter
类,并在类上使用@WebFilter
注解声明过滤路径。
@Slf4j
@WebFilter("/*") // 过滤器路径
public class MessageFilter extends HttpFilter {
public void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
log.info("[MessageFilter] 请求地址:" + request.getRequestURI());
chain.doFilter(request, response);
}
}
由于SpringBoot默认采用内嵌的tomcat,所以需要在启动类上添加@ServletComponentScan
注解进行web组件的扫描。
@SpringBootApplication
@ServletComponentScan // 扫描Servlet组件
public class StartSpringBoot {
public static void main(String[] args) {
SpringApplication.run(StartSpringBoot.class, args);
}
}
通过浏览器访问:http://localhost:8080/hello
发现控制台打出过滤器日志,说明过滤器配置成功:
在实际的项目开发中,一个访问路径前可能存在多个过滤器组成的过滤链。传统的Java WEB项目会有一个web.xml
的配置文件,可以在其中配置过滤器的过滤路径,如果一个访问路径在多个过滤器的过滤路径下,那么过滤器的执行顺序就和配置文件的编写顺序有关了:
<filter>
<filter-name>Filter1filter-name>
<filter-class>com.it.filter.Filter1filter-class>
filter>
<filter-mapping>
<filter-name>Filter1filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>Filter2filter-name>
<filter-class>com.it.filter.Filter2filter-class>
filter>
<filter-mapping>
<filter-name>Filter2filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
以上面的配置为例,如果要访问:http://localhost:8080/hello
,那么一定会是以下的顺序:
而SpringBoot的目标就是减少传统java web项目繁杂的配置文件,配置信息可以在application.yml
中编写,也可以自定义配置类,通过代码的方式进行配置。
删除MessageFilter
类上的@WebFilter
注解:
@Slf4j
public class MessageFilter extends HttpFilter {
public void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
log.info("[MessageFilter] 请求地址:" + request.getRequestURI());
chain.doFilter(request, response);
}
}
再编写一个ValidateFilter
:
@Slf4j
public class ValidateFilter extends HttpFilter {
public void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
log.info("[ValidateFilter] 请求地址:" + request.getRequestURI());
chain.doFilter(request, response);
}
}
编写一个配置类控制Filter的执行顺序:
@Configuration
public class WebFilterConfig {
/**
* 配置MessageFilter的执行顺序
* @return FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean getMessageRegistration() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new MessageFilter()); // 过滤器类实例对象
filterRegistrationBean.setName("MessageFilter"); // 过滤器名
filterRegistrationBean.addUrlPatterns("/*"); // 过滤路径
filterRegistrationBean.setOrder(2); // 执行顺序
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean getValidateRegistration() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new ValidateFilter());
filterRegistrationBean.setName("ValidateFilter");
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setOrder(5);
return filterRegistrationBean;
}
}
通过FilterRegistrationBean
类的order属性可以指定过滤器的执行顺序,order值越小则越先执行。
直接访问:http://localhost:8080/
发现ValidateFilter比MessageFilter先执行。
监听器就是监听某个对象的的状态变化的组件,按照被监听的对象可划分为:ServletRequest域 、HttpSession域、 ServletContext域。按照监听的内容可分为:域对象的创建与销毁、域对象的属性变化。
ServletRequest | ServletRequest | ServletRequest | |
---|---|---|---|
域对象的创建与销毁 | ServletContextListener | HttpSessionListener | ServletRequestListener |
域对象的属性变化 | ServletContextAttributeListener | HttpSessionAttributeListener | ServletRequestAttributeListener |
以监听Servlet初始化为例,定义一个监听器类并实现ServletContextListener
接口,在类上添加@WebListener
注解。
@Slf4j
@WebListener
public class DefaultServletListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
log.info("[DefaultServletListener] " + sce.getServletContext().getServerInfo());
log.info("[DefaultServletListener] " + sce.getServletContext().getRealPath("/"));
log.info("[DefaultServletListener] " + sce.getServletContext().getVirtualServerName());
}
}
由于使用了@WebListener
注解,所以需要在SpringBoot启动类上添加@ServletComponentScan
注解扫描Servlet组件。
@SpringBootApplication
@ServletComponentScan({"com.it.listener"})
public class StartSpringBoot {
public static void main(String[] args) {
SpringApplication.run(StartSpringBoot.class, args);
}
}
启动项目,发现Servlet初始化监听生效:
拦截器是基于java反射机制实现的java程序,其不依赖于Servlet 容器,通常用于service层方法调用前后的处理。
值得注意的是:
SpringBoot整合拦截器只需要实现HandlerInterceptor接口。
@Slf4j
public class DefaultInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info("------------------------------ DefaultInterceptor ------------------------------");
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
log.info("[控制器实例]" + handlerMethod.getBean());
log.info("[控制器类型]" + handlerMethod.getBeanType());
log.info("[控制器方法]" + handlerMethod.getMethod());
}
log.info("------------------------------------------------------------------------------------");
return true;
}
}
定义好的拦截器需要在配置类中进行启用。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 注册拦截器
* @param registry InterceptorRegistry
*/
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getDefaultInterceptor()) // 添加拦截器
.addPathPatterns("/**"); // 配置拦截器路径
}
@Bean
public HandlerInterceptor getDefaultInterceptor() {
return new DefaultInterceptor();
}
}
基于AspectJ切面表达式实现的AOP处理是Spring的一个重要特性,AOP拦截器可以直接进行指定类结构的拦截处理。
SpringBoot如果想要使用AOP,则需要添加依赖支持:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
<version>2.5.5version>
dependency>
新建IMessageService
业务层接口和实现类:
public interface IMessageService {
String echo(String message);
}
public class MessageServiceImpl implements IMessageService {
@Override
public String echo(String message){
return "[echo] : " + message;
}
}
再aspect包下新建ServiceAspect
类并配置业务层环绕拦截。
@Slf4j
@Aspect
@Component
public class ServiceAspect { // AOP切面管理
/**
* service环绕切面
* @param point
* @throws Throwable
*/
@Around("execution(* *..service..*.*(..))") // 配置所有service生效
public Object aroundInvoke(ProceedingJoinPoint point) throws Throwable {
log.info("[invoke before] " + Arrays.toString(point.getArgs()));
Object obj = point.proceed(point.getArgs()); // 调用真实业务
log.info("[invoke after] return: " + obj);
return obj; // 业务执行的结果
}
}
新建RestController测试AOP拦截结果:
@RestController
@RequestMapping("/message/*")
public class MessageAction {
private final IMessageService messageService;
public MessageAction(IMessageService messageService) {
this.messageService = messageService;
}
@RequestMapping("/get")
public String getMessage(String message) {
return messageService.echo(message);
}
}
访问:http://localhost:8080/message/get?message=hello
,查看控制台日志: