1. 程式人生 > >springboot的依賴注入報null的問題

springboot的依賴注入報null的問題

最近使用springboot開發專案,使用到了依賴注入,頻繁的碰到注入的物件報空指標,錯誤如下

java.lang.NullPointerException: null
    at com.mayihc.audit.controller.MaterialNkDetailController.download(MaterialNkDetailController.java:72)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:
209) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:
877) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:
991) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866) at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61) at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108) at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66) at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387) at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Unknown Source)

第一次碰見的情況是:在new出來的物件中使用@Autowired來獲取注入的物件

 DateCheckUtils dateCheckUtils = new DateCheckUtils();

我要在這個類中使用依賴注入,剛開始寫的是注入方式如下

public class DateCheckUtils {
     @Autowired
    private CompanyInfoService companyInfoService;
 @Autowired    
private MaterialNkDetailService materialNkDetailService;
    

.........
}

這是會報空指標異常,後倆修改程式碼就解決了問題,修改後的程式碼如下

1、建立一個類實現ApplicationContextAware,用來獲取applicationContext 和bean

package com.mayihc.audit.core.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringUtils implements ApplicationContextAware{

    private static ApplicationContext applicationContext = null; 

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringUtils.applicationContext == null){  
            SpringUtils.applicationContext  = applicationContext;  
        } 
        
    }
    //獲取applicationContext  
    public static ApplicationContext getApplicationContext() {  
        return applicationContext;  
    }  
 
    //通過name獲取 Bean.  
    public static Object getBean(String name){  
        return getApplicationContext().getBean(name);  
    }  
 
    //通過class獲取Bean.  
    public static <T> T getBean(Class<T> clazz){  
        return getApplicationContext().getBean(clazz);  
    }  
 
    //通過name,以及Clazz返回指定的Bean  
    public static <T> T getBean(String name,Class<T> clazz){  
        return getApplicationContext().getBean(name, clazz);  
    }  


}

2、在new出來的DateCheckUtils類中獲取ApplicationContext的例項

public class DateCheckUtils {
  //獲取ApplicationContext的例項
    private ApplicationContext applicationContext = SpringUtils.getApplicationContext();
  //通過applicationContext和反射機制來獲取物件
    private CompanyInfoService companyInfoService = applicationContext.getBean(CompanyInfoService.class);
    private MaterialNkDetailService materialNkDetailService = applicationContext.getBean(MaterialNkDetailService.class);
    private MaterialNkLogbookService materialNkLogbookService = applicationContext
            .getBean(MaterialNkLogbookService.class);
    private MaterialNkLedgerService materialNkLedgerService = applicationContext.getBean(MaterialNkLedgerService.class);
    private MaterialGjChangeDetailService materialGjChangeDetailService = applicationContext
            .getBean(MaterialGjChangeDetailService.class);
    private MaterialGjLedgerService materialGjLedgerService = applicationContext.getBean(MaterialGjLedgerService.class);
    .....

}

這樣就可以獲取到要注入的物件

報空指標的原因:當物件宣告為bean元件的時候,他是交給spring容器去管理的,容器會幫你進行初始化;但是如果使用new方法來呼叫物件是=時,會跳過spring容器生成的物件,這是就無法進行初始化,所以在呼叫的時候就會出現物件為null,並且物件裡面以注入方式引用的物件也為null,被宣告為bean物件的元件必修使用注入的方式進行呼叫。

 

上述的問題也可以通過將  DataCheckUtils dataCheckUtils=new DataCheckUtils();改為:

@Autowired

private DataCheckUtils dataCheckUtils;來實現

 

第二種碰見null的情況:在同一個類中只有某個方法在被前端呼叫的時候報null,其餘方法都正常。

錯誤程式碼如下

 1  @RequestMapping("/download")
 2     private void download(HttpServletResponse response, MaterialNkDetail materialNkDetail) throws Exception {
 3         SysUser u = (SysUser) SecurityUtils.getSubject().getPrincipal();
 4         if(!"1".equals(u.getType())) {
 5             materialNkDetail.setCompanyId(u.getCompanyInfoId());
 6         } 
 7         CompanyInfo companyInfo = companyInfoService.selectByPrimaryKey(u.getCompanyInfoId());
 8         if(companyInfo == null) {
 9             companyInfo = new CompanyInfo();
10             companyInfo.setFullName("全部車輛");
11         }
12     .....
13 }    

當執行這個方法時,注入的物件總是報null,經排查後,此方法是用private修飾的,改為public修飾後就OK了。

原因:容器掃描bean生成代理類的時候,public和protected方法可以被正常代理,而private方法不會被正常代理,屬性的注入是在代理類中完成的,所以public和protected方法獲取的注入屬相是完成注入的屬性,private方法獲取的是為完成注入的屬性,所以是null