[090]web容器啟動探討

weixin_34402408發表於2017-11-25

tomcat 如何啟動spring容器

我們知道spring通過容器來管理bean,在spring容器啟動的時候會例項化所有的bean包括從xml讀取或者annotation掃描得到,即所有的物件live in applicationContext.
我們想象這樣一個場景,啟動web容器如何啟動spring applicatin Context,它的entrance在哪?
tomcat中,我們通過listener來啟動spring 容器。
以下是我們工程中常用的web.xml結構
web.xml (參考fs-sail-provider/web.xml)

 <context-param>
<!--如果applicationContext在src/main/webapp/WEB-INF下該引數可以不用寫-->
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext.xml</param-value>
  </context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

可以看到Spring容器的entrance觸發是通過ContextLoaderListener來觸發的,Spring Mvc的入口是DispatcherServlet。
至於DispatcherServlet的實現機制我們可以看 一下它的類族圖(intellij中通過ctrl+h調出類圖)。


4011138-16228193f5087f99.png
image.png

如何啟動resteasy-jaxrs

 <listener>
        <listener-class>
            org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
        </listener-class>
    </listener>
    <listener>
        <listener-class>
            com.facishare.paas.appframework.fcp.model.FcpRestServerContextListener
        </listener-class>
    </listener>
    <servlet>
        <servlet-name>Resteasy</servlet-name>
        <servlet-class>
            org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Resteasy</servlet-name>
        <url-pattern>/API/*</url-pattern>
    </servlet-mapping>

與spring中的dispatcher servlet一樣,jboss的httpServletDispatcher也是繼承了javax.servlet.http.HttpServlet


4011138-1a05e1e43525e10e.png
image.png

由於該專案也是 基於spring做了一些定製開發,需要啟動spring容器,其自定義的FcpRestServerContextListener的類族圖如下:


4011138-95f56c0191cb0ca6.png
image.png

如何載入引用jar包中的bean

如何工程A引用了工程B的jar包,如何讓工程B的spring bean注入進去呢。
由於spring容器啟動的入口只有一個就是Listener對應的ContextLoaderListener,其對應的配置檔案為applicationContext.xml。
所以我們必須在A的applicationContext.xml鍾加上 B的配置檔案。
類似如:

....
 <import resource="classpath:spring/metadata.xml"/>
  <import resource="classpath:spring/common.xml"/>

對於dubbo的bean的引用形式怎麼樣的呢?

在談到遠端引用的情況,我們先談談本地引用的情況:
在本地spring容器中,對於如下類

@Service
Class Bean2
{
    @autuowiere
    Bean1 bean1;
}

在啟動spring容器中會做兩件事,先通過spring容器 instant Bean2,然後instant Bean2發現Bean2包含有bean1的成員且需要注入(注入就是變數與例項關聯的過程),那麼就去spring容器中找bean1的例項如果找不到就會報錯。所以這裡其實包含兩個過程a).bean的例項化,b).bean的注入過程。

那麼對應dubbo遠端服務同樣包含,客戶端與服務端bean的例項化和關聯的過程。下面我們先講服務端bean的例項化,然後再講客戶端如何引用它。

首先對於工程A呼叫工程B對應的服務,工程A只引用工程B對應的api jar包,通過A引用B定義的服務達到遠端訪問的目的。

1).服務端:在B的applicationContext中必須定義該服務的實現

<dubbo:service interface="com.facishare.sail.api.service.AuthService" ref="authService" protocol="dubbo"/>
<!--authService 為B工程中例項化的bean-->

其中,B工程中 authService bean定義如下:

@Service("authService")
public class AuthServiceImpl implements AuthService {
......
}

所以我們知道,dubbo:service是把spring容器中的bean authService註冊到註冊中心供遠端呼叫。

2 ).客戶端:在A中的applicatonContext引用B的服務:

<dubbo:reference interface="com.facishare.sail.api.service.AuthService" id="authService"/>

引用了B的服務後,其A工程如何使用呢?請參考如下程式碼:

public class AuthController {
    @Autowired
    private AuthService authService; //通過id="authService"去註冊中心查詢服務
}

說在後面的話

對於Spring dispatcherServlet這個入口如何啟作用,為什麼繼承了httpServlet就會使用dispatcherServlet估計需要了解下tomcat底層的原始碼了。
學習一個東西把邏輯想清楚,不要貌似的以為懂了,在學習上需要閉環。

相關文章