1. 程式人生 > >Spring詳解:WebServlet 中不能注入Bean物件

Spring詳解:WebServlet 中不能注入Bean物件

1. 前言

最近在研究Spring IOC、AOP以及和Mybatis整合的時候發現在Spring中使用Servlet+Service+Dao(Mybatis)的時候,發現在Controller層也就是Servlet中不能通過@Autowired注入Bean物件。這個時候我就納悶了,在Spring中明明物件的建立和管理交給Spring IOC容器去管理,納悶為什麼不能再容器中注入Bean物件? 下面我們貼出問題程式碼:

@WebServlet(value = "/servlet/AirportServelt")
public class AirportServelt extends
HttpServlet { @Autowired private AirportService airportService; @Override public void init() throws ServletException { super.init(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost
(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //報錯NullPointExecuption異常, airportService這個類null指標 List<Airport> allAirport = airportService.findAllAirport(); req.setAttribute
("airPortList",allAirport); System.out.println(allAirport); req.getRequestDispatcher("/index.jsp").forward(req,resp); } }

2. Spring WebServlet中不能注入Bean原理

想了解此問題的原理,就要了解tomcat啟動後 servlet和spring的載入順序。在TomcatWeb 容器載入一個Web專案的時候:

  1. tomcat啟動後先載入web.xml檔案。web.xml主要配置了servlet 、filter、listenner三種javaEE規範的類,載入順序跟在web.xml文件中的位置無關。順序為 listenner>filter>servlet 。
  2. 而spring的初始化類為org.springframework.web.context.ContextLoaderListener,就是一個listenner,它是先於servlet載入的。普通servlet和springmvc的入口servlet的載入順序,就要看servlet的設定了。它們按照在Web.xml中定義的Servlet順序載入。其中springmvc需要指定org.springframework.web.servlet.DispatcherServlet攔截所有的Web請求。
  3. 在 servletA類上加@WebServlet等註解時,spring或springmvc會掃面相關包,自動例項化一個servlet例項A;這個例項A的引用是spring IOC容器管理的。這個時候Spring ContextLoaderListener監聽器首先初始化,掃描所有的java包,建立Bean物件。然後Tomcat容器在載入Servlet類,包括我們定義的Servlet以及Spring的DispatcherServlet。
  4. Tomcat容器接下來會在web.xml配置載入Servlet類,這個時候載入DispatcherServle以及我們定義的Servlet類。這是tomcat容器會根據servler配置啟動時或者第一次請求該url時例項化我們定義的Web servlet例項B.這個例項B的引用是tomcat容器管理的。
  5. 所以最終結果就是:攔截url的servlet和spring依賴注入的servlet不是同一個例項!!所以就產生了不能依賴注入或者註解不起作用的現象。

3. 解決方法

3.1 利用在Servlet中init()方法,重新注入Bean

@WebServlet(value = "/servlet/AirportServelt")
public class AirportServelt extends HttpServlet {

    @Autowired
    private AirportService airportService;

    @Override
    public void init() throws ServletException {
        //SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,getServletContext());
        WebApplicationContext webApplicationContext= WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
        airportService=webApplicationContext.getBean("airportService",AirportService.class);
        super.init();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List<Airport> allAirport = airportService.findAllAirport();
        req.setAttribute("airPortList",allAirport);
        System.out.println(allAirport);
        req.getRequestDispatcher("/index.jsp").forward(req,resp);
    }
}

3.2 servlet init方法里加入spring根據註解注入屬性的方法

@WebServlet(value = "/servlet/AirportServelt")
public class AirportServelt extends HttpServlet {

    @Autowired
    private AirportService airportService;

    @Override
    public void init() throws ServletException {
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,getServletContext());
        super.init();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List<Airport> allAirport = airportService.findAllAirport();
        req.setAttribute("airPortList",allAirport);
        System.out.println(allAirport);
        req.getRequestDispatcher("/index.jsp").forward(req,resp);
    }
}