控制器用于接收和响应用户发送的请求,一个控制器类需要被@Controller注解:(即controller层)
@Controller
class TestController {
}
注: @RestController实际上是@Controller和@ResponseBody的简化版,被这个注解标注的控制器类的方法默认向客户端直接返回字符串。
过滤器可以在调用控制器方法之前进行一些操作,是基于Servlet规范的。过滤器类一般放在filter包下。(过滤器是处于客户端与服务器资源文件之间的一道过滤网,帮助我们过滤一些不符合要求的请求。通常用作:
统一设置字符编码
过滤敏感字符
进行登录校验以及 url 级别的访问权限控制。
大白话:过滤器就是一道上了锁的密码门,进入都需要指定密码才能进入,密码不对就进不去)。
主要遵循Java Servlet规范中定义的过滤器生命周期。一个实现了javax.servlet.Filter接口的类,在Spring Boot应用中作为过滤器使用时,会经历以下生命周期阶段:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan
public class LogbackApplication {
public static void main(String[] args) {
SpringApplication.run(LogbackApplication.class, args);
}
}
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author zjh
* @className AnnotateFilter
* @date 2024/7/26
* @description TODO
**/
// urlPatterns 过滤路径 filterName 过滤器name
@WebFilter(urlPatterns = "/zjh/*" ,filterName = "urlFilter")
public class AnnotateFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//过滤逻辑
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
import javax.servlet.*;
import java.io.IOException;
/**
* @author zjh
* @className NoAnnotateFilter
* @date 2024/7/26
* @description TODO
**/
public class NoAnnotateFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//过滤逻辑
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author zjh
* @className FilterConfig
* @date 2024/7/26
* @description TODO
**/
@Configuration
public class FilterConfig {
@Bean
public NoAnnotateFilter noAnnotateFilter(){
return new NoAnnotateFilter();
}
@Bean
public FilterRegistrationBean getFilterRegistrationBean(NoAnnotateFilter noAnnotateFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(noAnnotateFilter);
filterRegistrationBean.setOrder(2);
filterRegistrationBean.addUrlPatterns("/zjh/*");
filterRegistrationBean.setName("noAnnotateFilter");
return filterRegistrationBean;
}
}
是Spring MVC框架中的一个重要特性,它允许你在请求处理流程中的特定点执行自定义逻辑。拦截器主要用于在控制器(Controller)处理请求之前和之后执行一些预处理和后处理工作。拦截器的主要目的是在不修改原有代码的情况下,实现对请求和响应的统一处理。
拦截器的本质上是面向切面编程 AOP,符合切面编程的关注点都可以通过拦截器来实现。它通常用于以下场景:
权限控制: 拦截器可以在请求到达处理器之前进行权限验证,从而实现对不同用户的访问控制。
日志记录: 拦截器可以在请求处理过程中记录请求和响应的详细信息,便于后期分析和调试(可实现日志的脱敏)。
接口幂等性校验: 拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。
数据校验: 拦截器可以在请求到达处理器之前对请求数据进行校验,确保数据的合法性。
缓存处理: 拦截器可以在请求处理之后对响应数据进行缓存,提高系统性能。
通用行为: 读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现)
当有多个拦截器时,它们的执行顺序取决于注册顺序。先注册的拦截器先执行,后注册的拦截器后执行。在请求处理过程中,拦截器的preHandle方法按注册顺序执行,而postHandle和afterCompletion方法按注册顺序的逆序执行。
当有多个拦截器时,它们的执行流程如下:
执行所有拦截器的preHandle方法,按注册顺序执行。如果某个拦截器的preHandle方法返回false,则中断请求处理,直接执行该拦截器的afterCompletion方法。
执行处理器的处理方法。
执行所有拦截器的postHandle方法,按注册顺序的逆序执行。
渲染视图。
执行所有拦截器的afterCompletion方法,按注册顺序的逆序执行。
拦截器的生命周期由Spring容器管理。当Spring容器启动时,拦截器会被实例化并初始化;当Spring容器关闭时,拦截器会被销毁。
在更高版本中HandlerInterceptorAdapter已经弃用,推荐实现HandlerInterceptor接口。实现拦截类的拦截逻辑。
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author zjh
* @className LogbackInterceptor
* @date 2024/7/29
* @description 拦截器处理类
**/
@Component
public class LogbackInterceptor implements HandlerInterceptor {
/**
* 在 controller 执行之前进行拦截,返回结果将决定后续操作是否执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 编写拦截器逻辑
System.out.println("===拦截器1前处理===");
return true;
}
/**
* 在执行完 controller 的方法后进行拦截,可对 model 和 view 进行修改
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("===拦截器1后处理===");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 在整个请求完成之后执行,即视图渲染之后执行,若返回 json 数据则不会执行此方法
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
注: preHandle方法的返回值如果为true表示正常执行,如果为false表示阻止请求正常执行
用户发送请求时,先执行preHandle()方法。会先按照顺序执行所有拦截器的preHandle方法,一直遇到return false为止,比如第二个preHandle方法是return false,则第三个以及以后所有拦截器都不会执行。若都是return true,则执行用户请求的url方法。
调用了Service并返回ModelAndView,但未进行页面渲染,可以在这里继续修改ModelAndView或者返回值
已经渲染了页面,在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。
注: 一般使用preHandle这个拦截器进行预处理,对url进行请求拦截
需要继承WebMvcConfigurerAdapter类,在更高版本WebMvcConfigurerAdapter已经弃用。推荐实现WebMvcConfigurer接口,并重写addInterceptors(InterceptorRegistry registry)方法。也可以通过继承WebMvcConfigurationSupport类来实现,但是只能继承一次,而且会导致其他不注册在此类中的拦截器失效,不建议使用。
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author zjh
* @className InterceptorConfig
* @date 2024/7/29
* @description 拦截器注册
**/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
private final LogbackInterceptor logbackInterceptor;
public InterceptorConfig(LogbackInterceptor logbackInterceptor) {
this.logbackInterceptor = logbackInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry)
{
//注册自己的拦截器并设置拦截的请求路径
registry.addInterceptor(logbackInterceptor).addPathPatterns("/**");
}
}
拦截器在请求处理过程中可能会影响系统性能,以下是一些性能优化策略:
减少拦截器数量:尽量将相关功能集中到一个拦截器中,避免创建过多的拦截器。
精确配置拦截规则:通过addPathPatterns和excludePathPatterns方法精确配置拦截规则,避免不必要的拦截。
使用异步处理:在拦截器中使用异步处理,避免阻塞请求处理过程。
使用缓存:在拦截器中使用缓存,减少对数据库或其他资源的访问。
在Spring Boot中,监听器(Listener)的概念并不直接等同于传统Java EE或Servlet环境中的监听器,如ServletContextListener、HttpSessionListener等。Spring Boot作为一个现代化的、基于Spring框架的简化开发平台,它更多地依赖于Spring的事件发布-监听机制(Application Events)来处理类似监听器的功能。
Spring的事件发布-监听机制允许应用程序组件之间进行解耦的通信。当一个组件发生某个特定事件时,它可以发布一个事件,然后其他组件可以监听这个事件并做出响应。这种机制是通过ApplicationEvent类和ApplicationListener接口实现的。
ApplicationEvent:所有Spring事件类的基类。自定义事件需要继承这个类。
ApplicationListener:这是一个泛型接口,用于监听特定类型的事件。实现这个接口的类必须指定它们希望监听的事件类型。
ApplicationStartingEvent:应用程序开始启动时触发
ApplicationEnvironmentPreparedEvent:应用程序环境准备好时触发,此时上下文还没有创建
ApplicationContextInitializedEvent:应用程序上下文创建之前触发
ApplicationPreparedEvent:应用程序上下文创建完成后触发,此时所有的bean已经加载完成
ApplicationStartedEvent:应用程序启动完成时触发
ApplicationReadyEvent:应用程序已经准备好接收请求时触发
ApplicationFailedEvent:应用程序启动失败时触发
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* @author zjh
* @className CustomEventListener
* @date 2024/7/29
* @description TODO
**/
@Component
public class CustomEventListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 处理事件逻辑
System.out.println("Spring Boot 应用启动... " + event.toString());
}
}
注: @Component 是 Spring 框架中的一个核心注解,它用于标记一个类作为 Spring 容器中的一个组件(Bean),以便 Spring 可以自动检测到它,并对其进行管理。当 Spring 容器启动时,它会扫描带有 @Component 注解的类,并将其实例化、组装成 Spring 应用上下文(ApplicationContext)中的 Bean。
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventListener {
@EventListener
public void handleContextRefreshedEvent(ContextRefreshedEvent event) {
// 你的逻辑
}
}