使用到的注解:@Configuration,@Component,@Bean,@Aspect,@PostConstruct,@Slf4j
使用到的技术点:Filter声明注册以及执行顺序,Interceptor声明注册以及执行顺序,WebMvcConfigurer接口应用,Slf4j注解配置文件定义
以日志分析,以上配置设置的执行顺序
测试项目目录如下:
在项目启动过程中执行的内容
SpringBoot项目有@EnableAutoConfiguration自动装配功能,即项目启动时会自动执行组件注解下@Bean注解的所有方法
自定义过滤器Filter
1. 先声明自定义Filter,继承Filter接口,重写init,doFilter,destroy方法
AuthenticationFilter类
package com.test.springboot.filter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
public class AuthenticationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("******Filter的init方法执行******");
}
/**
* 过滤哪些请求可以放行,一般此方法中会写是否在登录有效期
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
log.info("******Filter的doFilter方法执行******");
try {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
filterChain.doFilter(servletRequest,servletResponse);
} catch (Exception e) {
log.error("doFilter 执行异常,异常信息 : {}",e);
}
}
@Override
public void destroy() {
log.info("******Filter的destroy方法执行******");
}
}
2. 注册自定义Filter,FilterRegistrationBean中set自定义的Filter
FilterConfig类
package com.test.springboot.config;
import com.test.springboot.filter.AuthenticationFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
public class FilterConfig {
/**
* 注册自定义的filter过滤器
* @return
*/
@Bean
public FilterRegistrationBean registrationBean(){
log.info("******注册自定义的filter过滤器******");
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new AuthenticationFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setName("authenticationFilter");
return filterRegistrationBean;
}
}
自定义拦截器Interceptor
1. 先声明自定义的Interceptor,继承HandlerInterceptor接口,重写preHandler,postHandler,afterCompletion方法
TimeCostInterceptor类
package com.test.springboot.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
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;
import java.util.Date;
@Slf4j
@Component
public class TimeCostInterceptor implements HandlerInterceptor {
/**
* 该方法在Controller处理之前调用
* 若存在多个HanderInterceptor,则SpringMVC会按照声明的顺序进行执行preHandler
* preHandler返回false时,整个链路中断
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try {
log.info("******拦截器TimeCostInterceptor的方法preHandler执行了******");
request.setAttribute("beginTime",System.currentTimeMillis());
return true;
} catch (Exception e){
return false;
}
}
/**
* 该方法只有在preHandler方法返回true才会执行
* 在Controller执行之后,DispatcherServlet视图渲染之前 执行,即这个方法可以对ModelAndView进行操作
* 若存在多个HanderInterceptor,则声明在前的反而是最后执行,与preHandler执行相反
* @param request
* @param response
* @param handler
* @param modelAndView
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) {
log.info("******拦截器TimeCostInterceptor的方法postHandle执行了******");
try {
long interval = System.currentTimeMillis() - (Long)request.getAttribute("beginTime");
log.info("URI: {} Controller exec time: {}",request.getRequestURI(),interval);
} catch (Exception e){
log.info("TimeCostInterceptor postHandle 方法执行异常,异常信息:{}",e);
}
}
/**
* 该方法只有在preHandler方法返回true才会执行
* 在Controller执行之后,DispatcherServlet视图渲染之后 执行,即整个请求资源完成后执行,主要用于资源清理
* 若存在多个HanderInterceptor,则声明在前的反而是最后执行,与preHandler执行相反
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
log.info("******拦截器TimeCostInterceptor的方法afterCompletion执行了******");
try {
long interval = System.currentTimeMillis() - (Long)request.getAttribute("beginTime");
log.info("URI: {} Controller exec time: {}",request.getRequestURI(),interval);
} catch (Exception e){
log.info("TimeCostInterceptor afterCompletion 方法执行异常,异常信息:{}",e);
}
}
}
PermissionInterceptor类
package com.test.springboot.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
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;
@Slf4j
@Component
public class PermissionInterceptor implements HandlerInterceptor {
/**
* 在执行controller之前拦截某个请求,一般用在请求的权限验证上,即登录账户是否有权限进行某个操作
* 可结合shiro权限控制注解@RequiresPermissions使用
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try {
if (request.getRequestURI().contains("/addMember")) {
log.info("******拦截器PermissionInterceptor的方法preHandler执行***addMember请求拦截***");
return false;
}
log.info("******拦截器PermissionInterceptor的方法preHandler执行******");
return true;
} catch (Exception e){
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
log.info("******拦截器PermissionInterceptor的方法postHandle执行******");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
log.info("******拦截器PermissionInterceptor的方法afterCompletion执行******");
}
}
2. 注册自定义Interceptor,继承WebMvcConfigurer接口,重写addInterceptors方法,InterceptorRegistry中add自定义的Interceptor
AdapterConfig类
package com.test.springboot.config;
import com.test.springboot.interceptor.PermissionInterceptor;
import com.test.springboot.interceptor.TimeCostInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@Slf4j
public class AdapterConfig implements WebMvcConfigurer {
public PermissionInterceptor permissionInterceptor(){
return new PermissionInterceptor();
}
public TimeCostInterceptor timeCostInterceptor(){
return new TimeCostInterceptor();
}
/**
* 配置url访问页面
* @param viewControllerRegistry
*/
@Override
public void addViewControllers(ViewControllerRegistry viewControllerRegistry){
log.info("******注册自定义的ViewControllers方法******");
//若直接访问域名http://localhost:8080/springboot,则页面展示index.html
viewControllerRegistry.addViewController("/").setViewName("index");
//访问/index时,展示/login页面
viewControllerRegistry.addViewController("/index").setViewName("login");
//设置优先级
viewControllerRegistry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
/**
* 注册自定义的Interceptor拦截器
* 设置addInterceptor的顺序影响项目执行时preHandler、postHandler、afterCompletion方法的执行顺序
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("******注册自定义的Interceptor拦截器******");
registry.addInterceptor(permissionInterceptor()).addPathPatterns("/**").excludePathPatterns("/index","/login","/logout");
registry.addInterceptor(timeCostInterceptor()).addPathPatterns("/**");
}
}
@PostConstruct注解
spring容器启动时执行,可作为一些数据的常规化加载
OtherConfig类
package com.test.springboot.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Slf4j
@Component
public class OtherConfig {
/**
* 当一个class被注解为一个Bean(组件),那么class上被@PostConstruct注解的方法将会在程序启动的时候执行
*/
@PostConstruct
public void testPostConstruct(){
log.info("******Component组件注解下,PostConstruct注解执行******");
}
@Bean
public void testBean(){
log.info("******Component组件注解下,Bean注解执行******");
}
}
在接口请求过程中执行的内容
- 先执行过滤器,过滤器doFilter返回true,程序继续执行
- 再执行Interceptor的preHandler
- 再执行针对方法的控制@Aspect注解内容@Around,@Before
- 执行Controller方法程序
- 执行@Aspect注解的@Around,@After
- 执行Interceptor的postHandler
- 执行Interceptor的afterCompletion
补充LogAspect类
package com.test.springboot.support;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.beans.Expression;
@Aspect
@Component
@Slf4j
public class LogAspect {
/**
* 切点定义
*/
@Pointcut("execution(public * com.test.springboot.controller.*.*(..))")
private void logPointCut(){
log.info("切点执行中******");
}
/**
* 前置通知
* 1、在执行目标方法前执行,比如请求接口之前的登录验证;
* 2、在前置通知中设置请求日志信息,如开始时间,请求参数,注解内容等
* @param joinPoint
*/
@Before("logPointCut()")
public void doBefore(JoinPoint joinPoint){
log.info("切点Before********");
joinPoint.getThis();
}
/**
* 后置通知
* 1、在执行目标方法后执行
* @param joinPoint
*/
@After("logPointCut()")
public void doAfter(JoinPoint joinPoint){
log.info("切点After********");
}
/**
* 返回通知
* 1、在目标方法正常结束之后执行
* 2、在返回通知中补充请求日志信息,如返回时间,方法耗时,返回值,并且保存日志信息
* @param ret
* @throws Throwable
*/
// @AfterReturning(returning = "ret" , value = "")
// public void doAfterReturning(JoinPoint joinPoint,Object ret) throws Throwable{
// log.info("切点AfterReturning********");
// }
/**
* 异常通知
* 1、在目标方法非正常结束,发生异常或抛出异常时执行
* 2、在异常通知中设置异常信息,并将其保存
* @param throwable
*/
// @AfterThrowing(value = "logPointCut()" , throwing = "throwable")
// public void doAfterThrowing(Throwable throwable){
// log.info("切点Afterthrowing");
//
// }
/**
* 在执行方法前后调用advice,相当于@Before和@AfterReturning做的事情
* @param pjp
* @throws Throwable
*/
@Around("logPointCut()")
public void doAround(ProceedingJoinPoint pjp) throws Throwable{
log.info("切点Around环绕前****");
pjp.proceed();
log.info("切点Around环绕后****");
}
}