能够注入Bean的Interceptor

为什么使用SpringBoot的时候,拦截器中使用@Autowired注入bean会报空指针.如下面代码所示,我们知道,Spring管理的bean发现有这个注解时候,它会直接注入相应的另一个Spring管理的bean.当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor (继承InstantiationAwareBeanPostProcessorAdapter)将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有@Autowired 注解时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去。那为什么这里的注解没有生效呢?
网上传说:了解SpringBoot的都知道SpringBoot的目录格式有着明确的规定,它减轻编程人员负担的同时,更加要求了编程的规范化,SpringBoot初始化的时候,会加载com.boot.app下的bean,一层一层加载,当注册LoggerInterceptor的时候,发现LoggerInterceptor中有@Autowired注解,就会去另外一个spring管理器中索取另外一个LoggerJpa,而这时候LoggerJpa根本没有初始化.所以就无法注入LoggerJpa的bean类完成相应的操作.

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

public class LoggerInterceptor implements HandlerInterceptor {
    public static final String SEND_TIME = "send_time";
    public static final String DATA = "param_data";
    
    //问题:无法注入loggerDao
    @Autowired
    private LoggerJpa loggerDao;
    
    /**
     * 进入springMVC的controller之前开始记录日志实体
     * @param httpServletRequest request
     * @param httpServletResponse response
     * @param o 实体类
     * @return  boolean
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse         httpServletResponse, Object o) throws Exception {
        //创建日记实体类
        LoggerEntity entity = new LoggerEntity();
        //获得sessionId
        String sessionId = httpServletRequest.getRequestedSessionId();
        //请求地址信息
        String requestURI = httpServletRequest.getRequestURI();
        //请求参数信息(利用fastJson转换参数)
        String params = JSON.toJSONString(httpServletRequest.getParameterMap(),
            SerializerFeature.DisableCircularReferenceDetect,
            SerializerFeature.WriteMapNullValue);
        //设置客户端ip
        entity.setClientIp(LoggerUtil.getCliectIp(httpServletRequest));
        //设置请求方法
        entity.setMethod(httpServletRequest.getMethod());
        //设置请求类型
        entity.setType(LoggerUtil.getRequestType(httpServletRequest));
        //设置请求参数
        entity.setParamData(params);
        //设置请求地址
        entity.setUri(requestURI);
        //设置sessionId
        entity.setSessionId(sessionId);
        //设置请求开始时间
        httpServletRequest.setAttribute(SEND_TIME, System.currentTimeMillis());
        //设置请求实体到request内,方便afterCompletion调用
        httpServletRequest.setAttribute(DATA, entity);
        return true;
      }
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    }
 
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        //请求状态
        int status = httpServletResponse.getStatus();
        //当前时间
        long time = System.currentTimeMillis();
        //上次请求时间
        Long requestTime = Long.valueOf(httpServletRequest.getAttribute(SEND_TIME).toString());
        //获取请求日记的实体
        LoggerEntity entity = (LoggerEntity) httpServletRequest.getAttribute(DATA);
        //设置时间差
        entity.setConsuming(Long.valueOf(time-requestTime).toString());
        //设置错误码
        entity.setStatusCode(status+"");
        //设置返回值
        entity.setReturnData(JSON.toJSONString(httpServletRequest.getAttribute(LoggerUtil.LOGGER_RETURN),
            SerializerFeature.DisableCircularReferenceDetect,
            SerializerFeature.WriteMapNullValue));
        //通过WebApplicationContextUtils获取loggerDao
       //LoggerJpa loggerDao = getDao(LoggerJpa.class,httpServletRequest);
        //将日记写入数据库
        loggerDao.save(entity);
    }
}

二.解决方法

了解到原因之后,那么如何解决呢?在这里,有两种常见的解决方法.
方法一:利用WebApplicationContextUtils去获取WebApplicationContext,然后在通过WebApplicationContext去获取相应的bean.

public <T> T getDao(Class<T> clazz,HttpServletRequest request){
    //通过该方法获得的applicationContext 已经是初始化之后的applicationContext 容器
    WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
    return applicationContext.getBean(clazz);
}

方法二:在初始化LoggerInterceptor 之前就初始化LoggerJpa

@Configuration
public class LoggerConfiguration extends WebMvcConfigurerAdapter {
 
    @Bean
    public LoggerInterceptor loggerInterceptor(){
        System.out.println("嘻嘻嘻嘻");
        return new LoggerInterceptor();
    }
    /**
     * LoggerInterceptor,形成拦截链
     * @param registry 拦截器注册类
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        //在放入拦截器之前调用loggerInterceptor(),触发LocalContainerEntityManagerFactoryBean使得拦截器的在注册之前所有的bean都持久化
        registry.addInterceptor(loggerInterceptor()).addPathPatterns("/**");
        System.out.println("呵呵呵呵");
    }
}

方法三:构造器

public class LoggerInterceptor implements HandlerInterceptor {
    private static final String SEND_TIME = "send_time";
    private static final String DATA = "param_data";
    private LoggerJpa loggerDao;
    public LoggerInterceptor(LoggerJpa loggerDao){
        this.loggerDao = loggerDao;
    }
//拦截器三个方法略...
}

@Configuration
public class LoggerConfiguration extends WebMvcConfigurerAdapter {

    @Autowired
    private LoggerJpa loggerDao;
    
    /**
     * LoggerInterceptor,形成拦截链
     * @param registry 拦截器注册类
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        //利用构造方法注入
       registry.addInterceptor(new LoggerInterceptor(loggerDao)).addPathPatterns("/**");
        System.out.println("呵呵呵呵");
    }
}

总结:
任何时候继承WebMvcConfigurationSupport进行web配置都可以,但是因为有些mvc配置只需要配置一次,所以这种方式没必要使用。
进行mvc配置也可以实现WebMvcConfigurer接口或继承WebMvcConfigurerAdapter类都是可行的,spring5.x.x版本之后直接实现WebMvcConfigurer接口即可。这种方式配置较为灵活,按需配置,固定配置交给WebMvcConfigurationSupport进行即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值