一.原因
- 這兩天在寫代碼的時候遇到一個問題,為什么使用SpringBoot的時候,攔截器中使用@Autowired注入bean會報空指針.如下面代碼所示,我們知道,Spring管理的bean發(fā)現(xiàn)有這個注解時候,它會直接注入相應(yīng)的另一個Spring管理的bean.當 Spring 容器啟動時,AutowiredAnnotationBeanPostProcessor (繼承InstantiationAwareBeanPostProcessorAdapter)將掃描 Spring 容器中所有 Bean飞袋,當發(fā)現(xiàn) Bean 中擁有@Autowired 注解時就找到和其匹配(默認按類型匹配)的 Bean恳守,并注入到對應(yīng)的地方中去巾遭。那為什么這里的注解沒有生效呢?
- 網(wǎng)上傳說:了解SpringBoot的都知道SpringBoot的目錄格式有著明確的規(guī)定,它減輕編程人員負擔的同時,更加要求了編程的規(guī)范化,SpringBoot初始化的時候,會加載com.boot.app下的bean,一層一層加載,當注冊LoggerInterceptor的時候,發(fā)現(xiàn)LoggerInterceptor中有@Autowired注解,就會去另外一個spring管理器中索取另外一個LoggerJpa,而這時候LoggerJpa根本沒有初始化.所以就無法注入LoggerJpa的bean類完成相應(yīng)的操作.
- 自我理解:注冊攔截器時直接通過new LoggerInterceptor(),并沒有觸發(fā)Spring去管理bean,所以@Autowired沒有生效.
/** * ======================== * * @author 小龍蝦 * Created with IntelliJ IDEA. * Date:2018/1/22 * Time:20:02 * ======================== */ 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 { //創(chuàng)建日記實體類 LoggerEntity entity = new LoggerEntity(); //獲得sessionId String sessionId = httpServletRequest.getRequestedSessionId(); //請求地址信息 String requestURI = httpServletRequest.getRequestURI(); //請求參數(shù)信息(利用fastJson轉(zhuǎn)換參數(shù)) String params = JSON.toJSONString(httpServletRequest.getParameterMap(), SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue); //設(shè)置客戶端ip entity.setClientIp(LoggerUtil.getCliectIp(httpServletRequest)); //設(shè)置請求方法 entity.setMethod(httpServletRequest.getMethod()); //設(shè)置請求類型 entity.setType(LoggerUtil.getRequestType(httpServletRequest)); //設(shè)置請求參數(shù) entity.setParamData(params); //設(shè)置請求地址 entity.setUri(requestURI); //設(shè)置sessionId entity.setSessionId(sessionId); //設(shè)置請求開始時間 httpServletRequest.setAttribute(SEND_TIME, System.currentTimeMillis()); //設(shè)置請求實體到request內(nèi),方便afterCompletion調(diào)用 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 { //請求狀態(tài) int status = httpServletResponse.getStatus(); //當前時間 long time = System.currentTimeMillis(); //上次請求時間 Long requestTime = Long.valueOf(httpServletRequest.getAttribute(SEND_TIME).toString()); //獲取請求日記的實體 LoggerEntity entity = (LoggerEntity) httpServletRequest.getAttribute(DATA); //設(shè)置時間差 entity.setConsuming(Long.valueOf(time-requestTime).toString()); //設(shè)置錯誤碼 entity.setStatusCode(status+""); //設(shè)置返回值 entity.setReturnData(JSON.toJSONString(httpServletRequest.getAttribute(LoggerUtil.LOGGER_RETURN), SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue)); //通過WebApplicationContextUtils獲取loggerDao //LoggerJpa loggerDao = getDao(LoggerJpa.class,httpServletRequest); //將日記寫入數(shù)據(jù)庫 loggerDao.save(entity); } }
二.解決方法
- 了解到原因之后,那么如何解決呢?在這里,有兩種常見的解決方法.
①.利用WebApplicationContextUtils去獲取WebApplicationContext,然后在通過WebApplicationContext去獲取相應(yīng)的bean.
public <T> T getDao(Class<T> clazz,HttpServletRequest request){ //通過該方法獲得的applicationContext 已經(jīng)是初始化之后的applicationContext 容器 WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); return applicationContext.getBean(clazz); }
②.在初始化LoggerInterceptor 之前就初始化LoggerJpa
/** * ======================== * * @author 小龍蝦 * Created with IntelliJ IDEA. * Date:2018/1/23 * Time:11:51 * ======================== */ @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){ //在放入攔截器之前調(diào)用loggerInterceptor(),觸發(fā)LocalContainerEntityManagerFactoryBean使得攔截器的在注冊之前所有的bean都持久化 registry.addInterceptor(loggerInterceptor()).addPathPatterns("/**"); System.out.println("呵呵呵呵"); } }
③.構(gòu)造器
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; } //攔截器三個方法略... }
/** * ======================== * * @author 小龍蝦 * Created with IntelliJ IDEA. * Date:2018/1/23 * Time:11:51 * ======================== */ @Configuration public class LoggerConfiguration extends WebMvcConfigurerAdapter { //@Bean //public LoggerInterceptor loggerInterceptor(){ // System.out.println("嘻嘻嘻嘻") // return new LoggerInterceptor(loggerDao); //} @Autowired private LoggerJpa loggerDao; /** * LoggerInterceptor,形成攔截鏈 * @param registry 攔截器注冊類 */ @Override public void addInterceptors(InterceptorRegistry registry){ //利用構(gòu)造方法注入 registry.addInterceptor(new LoggerInterceptor(loggerDao)).addPathPatterns("/**"); System.out.println("呵呵呵呵"); } }