HttpInvoker-----服務端實現


前言

Spring開發小組意識到在RMI服務和基於HTTP的服務(如Hessian和Burlap)之間的空白。一方面,RMI使用Java標准的對象序列化,但很難穿越防火牆;另一方面。Hessian/Burlap能很好的穿過防火牆工作,但是使用自己一套的對象序列化機制。

就這樣,Spring的HttpInvoker應運而生。HttpInvoker是一個新的遠程調用模型,作為Spring框架的一部分,來執行基於HTTP的遠程調用(讓防火牆高興的事),並使用Java的序列化機制(這是Java程序員高興的事)。

HttpInvoker是基於HTTP的遠程調用,同時也是以Spring提供的web服務作為基礎。

服務端實現

服務端的入口為org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter,那么同樣的,先來看一下這個類的類圖:

HttpInvokerServiceExporter不僅實現了HttpRequestHandler接口,也實現了InitializingBean接口。分析RMI服務時我們已經了解到了,當某個bean繼承自InitializingBean接口的時候,Spring會確保這個bean在初始化時調用其afterPropertiesSet方法,而對於HttpRequestHandler接口,因為我們在配置文件中已經將此接口配置成web服務,那么當有相應請求的時候,Spring的web服務就會將程序引導至HttpRequestHandler的handleRequest方法中,首先,我們從afterPropertiesSet方法開始分析,看看bean的初始化過程中做了哪些邏輯。

1.創建代理

 跟蹤代碼,創建代理的核心代碼在getProxyForService方法中。

protected Object getProxyForService() {
        //驗證service
        checkService();
        //驗證serviceInterface
        checkServiceInterface();
        //使用JDK的方式創建代理
        ProxyFactory proxyFactory = new ProxyFactory();
        //添加代理接口
        proxyFactory.addInterface(getServiceInterface());

        if (this.registerTraceInterceptor != null ? this.registerTraceInterceptor : this.interceptors == null) {
            //加入代理的橫切面RemoteInvocationTraceInterceptor並記錄Exporter名稱
            proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));
        }
        if (this.interceptors != null) {
            AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
            for (Object interceptor : this.interceptors) {
                proxyFactory.addAdvisor(adapterRegistry.wrap(interceptor));
            }
        }
        //設置要代理的目標類
        proxyFactory.setTarget(getService());
        proxyFactory.setOpaque(true);
        //創建代理
        return proxyFactory.getProxy(getBeanClassLoader());
    }

 可以看到,初始化的邏輯主要是創建了一個代理,代理中封裝了對於特定請求的處理方法以及接口等信息,而這個代理最關鍵的目的是加入了RemoteInvocationTraceInterceptor增強器,當然創建代理還有其他好處,比如代碼優雅、方便擴展等。RemoteInvocationTraceInterceptor中的增強主要是對增強的目標方法進行一些相關信息的日志打印,並沒有在此基礎上進行任何功能性的增強。

2.處理來自客戶端的request

 當有web請求時,根據配置中的規則會把路徑匹配的訪問直接引入對應的HttpRequestHandler中。本例中的請求與普通的web請求時有些區別的,因為此處的請求包含着HttpInvoker的處理過程。

public void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        try {
            //從request中讀取序列化對象
            RemoteInvocation invocation = readRemoteInvocation(request);
            //執行調用
            RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy());
            //將結果的序列化對象寫入輸出流
            writeRemoteInvocationResult(request, response, result);
        }
        catch (ClassNotFoundException ex) {
            throw new NestedServletException("Class not found during deserialization", ex);
        }
    }

在handleRequest函數中,我們很清楚的看到了HttpInvoker處理的大致框架,HttpInvoker服務簡單點說就是將請求的方法,也就是RemoteInvocation對象,從客戶端序列化並通過Web請求出入服務端,服務端在對傳過來的序列化對象進行反序列化還原RemoteInvocation實例,然后通過實例中的相關信息進行相關方法的調用,並將執行結果再次的返回給客戶端。從handleRequest函數中我們也可以清晰的看到程序執行的框架結構。

(1)從request中讀取序列化對象

主要是從HttpServletRequest提取相關的信息,也就是提取HttpServletRequest中的RemoteInvocation對象的序列化信息以及反序列化的過程。

protected RemoteInvocation readRemoteInvocation(HttpServletRequest request)
            throws IOException, ClassNotFoundException {

        return readRemoteInvocation(request, request.getInputStream());
    }
protected RemoteInvocation readRemoteInvocation(HttpServletRequest request, InputStream is)
            throws IOException, ClassNotFoundException {
        //創建對象輸入流
        ObjectInputStream ois = createObjectInputStream(decorateInputStream(request, is));
        try {
            //從輸入流中讀取序列化對象
            return doReadRemoteInvocation(ois);
        }
        finally {
            ois.close();
        }
    }
protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois)
            throws IOException, ClassNotFoundException {

        Object obj = ois.readObject();
        if (!(obj instanceof RemoteInvocation)) {
            throw new RemoteException("Deserialized object needs to be assignable to type [" +
                    RemoteInvocation.class.getName() + "]: " + ClassUtils.getDescriptiveType(obj));
        }
        return (RemoteInvocation) obj;
    }

 對於序列化的提取與轉換過程其實沒有太多需要解釋的東西,這里完全是按照標准的方式進行操作,包括創建ObjectInputStream以及從ObjectInputStream中提取對象實例。

(2)執行調度

根據反序列化的方式得到的RemoteInvocation對象中的信息,進行方法調用。注意,此時調用的實體並不是服務接口或者服務類,而是之前在初始化的時候構造的封裝了服務接口以及服務類的代理。

完成了RemoteInvocation實例的提取,也就意味着可以通過RemoteInvocation實例中提供的信息進行方法調用了。

protected RemoteInvocationResult invokeAndCreateResult(RemoteInvocation invocation, Object targetObject) {
        try {
            //激活代理類中對應Invocation中的方法
            Object value = invoke(invocation, targetObject);
            //封裝結果以便於序列化
            return new RemoteInvocationResult(value);
        }
        catch (Throwable ex) {
            return new RemoteInvocationResult(ex);
        }
    }

上述函數需要說明兩個地方。

  ❤  對應方法的激活也就是invoke方法的調用,雖然經過層層環繞,但是最終還是實現了一個我們熟知的調用invocation.invoke(targetObject),也就是執行RemoteInvocation類中的invoke方法,大致的邏輯還是通過RemoteInvocation中對應的方法信息在targetObject上去執行,此方法在分析RMI功能的時候已經分析過,不再贅述。但是對於當前方法的targetObject參數,此targetObject是代理類,調用代理類的時候需要考慮增強方法的調用。

  ❤ 對於返回結果需要使用RemoteInvocationResult進行封裝,之所以需要通過使用RemoteInvocationResult類進行封裝,是因為無法保證對於所有操作的返回結果都繼承Serializable接口,也就是說無法保證所有返回結果都可以直接進行序列化,那么,就必須使用RemoteInvocationResult類進行統一封裝。

(3)將結果的序列化對象寫入輸出流

同樣這里也包括結果的序列化過程。

protected void writeRemoteInvocationResult(
            HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result)
            throws IOException {

        response.setContentType(getContentType());
        writeRemoteInvocationResult(request, response, result, response.getOutputStream());
    }
protected void writeRemoteInvocationResult(
            HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result, OutputStream os)
            throws IOException {
        //獲取輸入流
        ObjectOutputStream oos =
                createObjectOutputStream(new FlushGuardedOutputStream(decorateOutputStream(request, response, os)));
        try {
            //將序列化對象寫入輸入流
            doWriteRemoteInvocationResult(result, oos);
        }
        finally {
            oos.close();
        }
    }
protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos)
            throws IOException {

        oos.writeObject(result);
    }

至此,HttpInvoker的服務器端解析已經結束。

參考:《Spring源碼深度解析》 郝佳 編著:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM