1. 程式人生 > 其它 >tomcat與springmvc 結合 之---第16篇 servlet如何解析成員變數和DispatcherServlet如何解析

tomcat與springmvc 結合 之---第16篇 servlet如何解析成員變數和DispatcherServlet如何解析

writedby 張豔濤,用了兩個星期將深入刨析tomcat看完了,那麼接下來該看什麼呢?真是不知道,知識這東西上一個月看的jvm,鎖.多執行緒併發 又都忘了....

tomcat學完,我打算看springmvc因為,spring本質就是一個servlet, 叫DispatcherServlet,那麼倆者聯絡緊密,打算結合二者,進行學習

昨天看了一天發現spring原始碼,看起來比tomcat要難,因為springmv太雜了

以前看過知道web.xml中的<servlet>標籤解析,那麼遇到了

 <servlet>
        <servlet-name>loginServlet</servlet-name>
        <servlet-class
>com.qcc.study.servlet02.LoginServlet</servlet-class> <!-- 配置Servlet初始化引數 --> <init-param> <param-name>initParam</param-name> <param-value>qcc</param-value> </init-param> <!-- Web容器啟動時就載入並例項化該Servlet --> <load-on-startup>0
</load-on-startup> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> </servlet>

以上的引數該如何解析呢?這個init-param目的就是要給成員變數初始化值,看如何使用

package com.qcc.study.servlet02;
 
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class LoginServlet extends HttpServlet {    成員變數1 initParam = null;
    成員變數2=null;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取servlet初始化引數: String initParam = getServletConfig().getInitParameter("initParam"); System.out.println("initParam: ---->" + initParam); }

那麼可以看到,

<init-parm>配置在<servlet>標籤中,用來初始化當前的Servlet的,屬於當前Servlet的配置,因此存放在 servletConfig物件中;
通過getServletConfig().getInitParameter("initParam")的方式獲取;
如果通過原始碼來看webruleset中對"web-app/servlet/init-param"解析

看addInitParameter StandardWrapper中

可以看到其實引數以hashmap的方法來組織成鍵值對,如果使用的時候,實際上是從wrapper容器中取得的,和servlet.class沒關係

如果在形成的servlet物件的成員變數賦值寫在init()方法裡面,最後形成的servlet物件的成員變數就是完全體了,這其實就是dispatchServlet的做法


先看一個dispatchservlet的配置檔案web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
    <display-name>springmvcFirst</display-name>
    <!-- springMVC前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- contextConfigLocation配置springmvc載入的配置檔案(配置處理器、對映器等) 如果不配置contextConfigLocation,預設載入的是/WEB-INF/servlet名稱-servlet.xml(springmvc-servlet.xml) -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!-- url-pattern:*.action的請交給DispatcherServlet處理。 -->
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>

</web-app>

看這其實是給servlet設值,那麼看dispatchservlet的成員變數

重點看FrameworkServlet,是dispatcherServlet的父類, 其中有一個成員變數叫contextConfigLocation,對應了標籤中的引數名

那麼在哪裡賦值的呢?

在HttpServletBean的init()方法裡面

其中init(servletConfig)的呼叫邏輯

GenericServlet===>

    public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
    }

本類的this.init(),其實這裡使用了模板方法,這個this是指的dispatchservlet物件,

    public void init() throws ServletException {

    }

所以呼叫的init()方法在HttpServletBean 中

    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters.
        try {
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            throw ex;
        }

        // Let subclasses do whatever initialization they like.
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

這個方法解析了wrapper中的屬性,重點看config就是StandardWrapper,呼叫了上文貼上的獲取parameter的方法了

最後能看到BW是通過反射來實現屬性的注入的


另外說一點,這servlet類的設計,使用了模板方法,是在GenericServlet中的

    public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
    }

那麼看tomcat的呼叫

這個servlet物件就是dispatchservelet,這樣子類呼叫父類的GenericServlet的init(servletconfig)方法,接著呼叫this(這個this是子類disptacherservlet物件,可以sout(this)驗證).init()方法,

整個dispatcherservlet的呼叫思路就弄清楚了