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