拦截器实现增删改操作的日志管理(Interceptor中NullPointException原因)

在项目的系统中,需要写一个日志管理,即将每次用户的增删改操作都存储于数据库中,此时,通过拦截器来设置,通过判断用户的请求,由于项目符合restful风格,但请求methodputdeletepost时,获取用户信息及请求信息,插入数据库中

首先是定义一个拦截器

/**
 * @author fuzihao
 * @date 2019/8/14 16:03
 */
public class OperationLogInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private ILogService logService;
    @Autowired
    private IElementService elementService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof HandlerMethod){
            String methodType = request.getMethod();
            System.out.println(request.getRequestURI());
            //如果是post、delete或put
            if(StringUtils.equalsIgnoreCase(HttpMethodEnum.POST.name(),methodType)||
                    StringUtils.equalsIgnoreCase(HttpMethodEnum.DELETE.name(),methodType)||
                    StringUtils.equalsIgnoreCase(HttpMethodEnum.PUT.name(),methodType)){
                //插入记录到日志表中
                insertToLog(methodType,request.getRequestURI());
            }
        }
        return true;
    }

    private void insertToLog(String methodType, String requestURI) {
		......
    }
}

WebMvcConfigurer全局配置文件中配置拦截器

/**
 * @author fuzihao
 * @date 2019/8/14 16:13
 * 全局拦截器,用于日志操作
 */
@Configuration
public class TrainWebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new OperationLogInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/api/**");
    }
}

此时进行putdeletepost操作,发现elementService报错NPE,经过万能的搜索引擎,发现如下原因:

new Interceptor() 即是我们定义的拦截器。此时若是在Interceptor类内使用@Autowired注解引入bean实例,必然导致无法注入,实例为null的情况。即如果类A中存在成员属性b, b是通过@Autowired自动注入,而类A的实例是通过new的方式产生的,那么自动注入会失效的。

网上传说:了解SpringBoot的都知道SpringBoot的目录格式有着明确的规定,它减轻编程人员负担的同时,更加要求了编程的规范化,SpringBoot初始化的时候,会加载com.boot.app下的bean,一层一层加载,当注册LoggerInterceptor的时候,发现LoggerInterceptor中有@Autowired注解,就会去另外一个spring管理器中索取另外一个LoggerJpa,而这时候LoggerJpa根本没有初始化.所以就无法注入LoggerJpa的bean类完成相应的操作.

自我理解:注册拦截器时直接通过new LoggerInterceptor(),并没有触发Spring去管理bean,所以@Autowired没有生效.

转自简书:https://www.jianshu.com/p/60ff6d0dae7f

此时我们可以手动初始化Bean对象

/**
 * @author fuzihao
 * @date 2019/8/14 16:13
 * 全局拦截器,用于日志操作
 */
@Configuration
public class TrainWebConfig implements WebMvcConfigurer {
    @Bean
    public OperationLogInterceptor operationLogInterceptor(){
        return new OperationLogInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new OperationLogInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/api/**");
    }
}

即可运行。
当然,如果我们也可以设置一个SpringContextUtils,在拦截器中手动注入各service,不过此方法不如以上直接声明Bean来得方便。

/**
 * @author fuzihao
 * @date 2019/8/14 16:52
 */
@Component
public class SpringContextUtils implements ApplicationContextAware {
    /**
     * 上下文对象实例
     */
    private static ApplicationContext applicationContext;

    @Override
    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }

    /**
     * 获取applicationContext
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通过name获取 Bean.
     * @param name
     * @return
     */
    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }
    
}

此时即可将拦截器的注入方式由@Autowired改为:

private ILogService logService= (ILogService) SpringContextUtils.getBean("logServiceImpl");
private IElementService elementService= (IElementService) SpringContextUtils.getBean("elementServiceImpl");

此时TrainWebConfig就不再需要手动注入拦截器了!
以上!

发布了63 篇原创文章 · 获赞 29 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Hpsyche/article/details/99901250