Java實現Http服務器(二)


上節講到的JDK自帶的HttpServer組件,實現方法大概有三十個類構成,下面嘗試着理解下實現思路。

由於Java的source代碼中有很多注釋,粘貼上來看着費勁,自己寫個程序消除注釋。

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015-10-30 下午02:38:17 類說明 處理從JDK當中的注釋
 */
public class Test
{
    public static void main(String[] args) throws IOException
    {
        FileInputStream inputStream = new FileInputStream("source");

        FileOutputStream outputStream = new FileOutputStream("res");

        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

        final StringBuilder builder = new StringBuilder();
        String tempstr = null;
        while ((tempstr = br.readLine()) != null)
        {
            if (tempstr.indexOf('*') == -1)
            {
                builder.append(tempstr + '\n');
            }
        }

        outputStream.write(builder.toString().getBytes("gbk"));
    }
}

 com.sun.net.httpserver包下的類和接口提供了一系列的標准

 sun.net.httpserver包下類根據標准做了一系列的實現

 

 

com.sun.net.httpserver.HttpServer.java

package com.sun.net.httpserver;

import com.sun.net.httpserver.spi.HttpServerProvider;


public abstract class HttpServer {

    protected HttpServer () {
    }
	
    //默認創建HttpServer對象
    public static HttpServer create () throws IOException {
        return create (null, 0);
    }

    //根據InetSocketAddress對象和backlog對象創建HttpServer對象
    public static HttpServer create (InetSocketAddress addr, int backlog) throws IOException 
    {   
	    //HttpServer實例的服務提供者HttpServerProvider
        HttpServerProvider provider = HttpServerProvider.provider();
        //由服務提供者創建HttpServer對象
		return provider.createHttpServer (addr, backlog);
    }
    
	//綁定網絡地址接口
    public abstract void bind (InetSocketAddress addr, int backlog) throws IOException;
    
	//啟動httpServer接口
    public abstract void start () ;

	//設置線程池
    public abstract void setExecutor (Executor executor);


    public abstract Executor getExecutor () ;

    public abstract void stop (int delay);
    
	//指定url和相應的處理Handler
    public abstract HttpContext createContext (String path, HttpHandler handler) ;
    
    public abstract HttpContext createContext (String path) ;

    public abstract void removeContext (String path) throws IllegalArgumentException ;

    public abstract void removeContext (HttpContext context) ;

    public abstract InetSocketAddress getAddress() ;
}

  

 com.sun.net.httpserver.spi.HttpServerProvider

 
        
package com.sun.net.httpserver.spi;

import java.io.FileDescriptor;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import sun.misc.Service;
import sun.misc.ServiceConfigurationError;
import sun.security.action.GetPropertyAction;

public abstract class HttpServerProvider {

    public abstract HttpServer createHttpServer (InetSocketAddress addr, int backlog) throws IOException;

    public abstract HttpsServer createHttpsServer (InetSocketAddress addr, int backlog) throws IOException;



    private static final Object lock = new Object();
    private static HttpServerProvider provider = null;

    protected HttpServerProvider() {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null)
            sm.checkPermission(new RuntimePermission("httpServerProvider"));
    }

    private static boolean loadProviderFromProperty() {
        String cn = System.getProperty("com.sun.net.httpserver.HttpServerProvider");
        if (cn == null)
            return false;
        try {
            Class c = Class.forName(cn, true,
                                    ClassLoader.getSystemClassLoader());
            provider = (HttpServerProvider)c.newInstance();
            return true;
        } catch (ClassNotFoundException x) {
            throw new ServiceConfigurationError(x);
        } catch (IllegalAccessException x) {
            throw new ServiceConfigurationError(x);
        } catch (InstantiationException x) {
            throw new ServiceConfigurationError(x);
        } catch (SecurityException x) {
            throw new ServiceConfigurationError(x);
        }
    }

    private static boolean loadProviderAsService() {
        Iterator i = Service.providers(HttpServerProvider.class,
                                       ClassLoader.getSystemClassLoader());
        for (;;) {
            try {
                if (!i.hasNext())
                    return false;
                provider = (HttpServerProvider)i.next();
                return true;
            } catch (ServiceConfigurationError sce) {
                if (sce.getCause() instanceof SecurityException) {
                    // Ignore the security exception, try the next provider
                    continue;
                }
                throw sce;
            }
        }
    }

    public static HttpServerProvider provider () {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return (HttpServerProvider)AccessController
                .doPrivileged(new PrivilegedAction<Object>() {
                        public Object run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = new sun.net.httpserver.DefaultHttpServerProvider();
                            return provider;
                        }
                    });
        }
    }

}

 

 

 

-----------------------分割線---------------------

上面說到com.sun.net.httpServer包下類和接口都是提供了一套標准,應用程序使用API的時候只關心這套標准,具體標准的實現應用程序是不關心的,實現了應用程序開發者和服務提供者的解耦,服務提供者可以提供多種多樣的實現。

對於同一個功能,不同的廠家會提供不同的產品,比如不同品牌的輪胎、插頭等。在軟件行業,情況也是如此。比如,對於數據的加密解密,不同的廠家使用不同的算法,提供強度各異的不同軟件包。應用軟件根據不同的開發需求,往往需要使用不同的軟件包。每次更換不同的軟件包,都會重復以下過程:更改應用軟件代碼 -> 重新編譯 -> 測試 -> 部署。這種做法一般被稱為開發時綁定。這其實是一種比較原始的做法,缺乏靈活性和開放性。於是應用運行時綁定服務提供者的做法流行開來。具體做法是,使用配置文件指定,然后在運行時載入具體實現Java SE 平台提供的 Service Provider 機制是折衷了開發時綁定和運行時綁定兩種方式,很好的滿足了高效和開放兩個要求。

  構成一個 Service Provider 框架需要大致三個部分,圖 1 給出了一個典型的 Service Provider 組件結構。Java SE 平台的大部分 Service Provider 框架都提供了 3 個主要個組件:面向開發者的 Application 接口,面向服務提供商的 Service Provider 接口和真正的服務提供者。

 

---------------------------分割線------------------------

重點關注Application接口的兩個方法

com.sun.net.httpServer.HttpServer.java

方法作用: 

創建一個HttpServer實例,該實例綁定於一個確定的網絡地址(由IP地址和端口號組成)
指定一個最大的監聽backlog的長度,這個長度是指允許在這個監聽Socket上排隊等待連接的最大數量。

該HttpServer實例來自於當前的HttpServerProvider

/**
     * Create a <code>HttpServer</code> instance which will bind to the
     * specified {@link java.net.InetSocketAddress} (IP address and port number)
     *
     * A maximum backlog can also be specified. This is the maximum number of
     * queued incoming connections to allow on the listening socket.
     * Queued TCP connections exceeding this limit may be rejected by the TCP implementation.
     * The HttpServer is acquired from the currently installed {@link HttpServerProvider}
     *
     * @param addr the address to listen on, if <code>null</code> then bind() must be called
     *  to set the address
     * @param backlog the socket backlog. If this value is less than or equal to zero,
     *          then a system default value is used.
     * @throws BindException if the server cannot bind to the requested address,
     *          or if the server is already bound.
     * @throws IOException
     */

    public static HttpServer create (
        InetSocketAddress addr, int backlog
    ) throws IOException {
        HttpServerProvider provider = HttpServerProvider.provider();
        return provider.createHttpServer (addr, backlog);
    }

這個模式和JAXP獲取XML解析對象的過程很像

 

com.sun.net.httpServer.HttpServerProvider.java  

方法作用:

     針對JVM的請求返回系統的HttpServerProvider,查找過程

1:如果系統屬性(system property) com.sun.net.httpserver.HttpServerProvider被定義過,找到相應的類

private static boolean loadProviderFromProperty() {
        String cn = System.getProperty("com.sun.net.httpserver.HttpServerProvider");
        if (cn == null)
            return false;
        try {
            Class c = Class.forName(cn, true,
                                    ClassLoader.getSystemClassLoader());
            provider = (HttpServerProvider)c.newInstance();
            return true;
        } catch (ClassNotFoundException x) {
            throw new ServiceConfigurationError(x);
        } catch (IllegalAccessException x) {
            throw new ServiceConfigurationError(x);
        } catch (InstantiationException x) {
            throw new ServiceConfigurationError(x);
        } catch (SecurityException x) {
            throw new ServiceConfigurationError(x);
        }
    }

  

    2:第三方jar包的屬性文件當中是否有相應設置

查找所有加載的jar包中META-INF/services目錄下的配置文件,文件名為

 private static boolean loadProviderAsService() {
        Iterator i = Service.providers(HttpServerProvider.class,ClassLoader.getSystemClassLoader());
        for (;;) {
            try {
                if (!i.hasNext())
                    return false;
                provider = (HttpServerProvider)i.next();
                return true;
            } catch (ServiceConfigurationError sce) {
                if (sce.getCause() instanceof SecurityException) {
                    // Ignore the security exception, try the next provider
                    continue;
                }
                throw sce;
            }
        }
    }

sun.misc.Service.java

 /**
     * Locates and incrementally instantiates the available providers of a
     * given service using the given class loader.
     *
     * <p> This method transforms the name of the given service class into a
     * provider-configuration filename as described above and then uses the
     * <tt>getResources</tt> method of the given class loader to find all
     * available files with that name.  These files are then read and parsed to
     * produce a list of provider-class names.  The iterator that is returned
     * uses the given class loader to lookup and then instantiate each element
     * of the list.
     *
     * <p> Because it is possible for extensions to be installed into a running
     * Java virtual machine, this method may return different results each time
     * it is invoked. <p>
     *
     * @param  service
     *         The service's abstract service class
     *
     * @param  loader
     *         The class loader to be used to load provider-configuration files
     *         and instantiate provider classes, or <tt>null</tt> if the system
     *         class loader (or, failing that the bootstrap class loader) is to
     *         be used
     *
     * @return An <tt>Iterator</tt> that yields provider objects for the given
     *         service, in some arbitrary order.  The iterator will throw a
     *         <tt>ServiceConfigurationError</tt> if a provider-configuration
     *         file violates the specified format or if a provider class cannot
     *         be found and instantiated.
     *
     * @throws ServiceConfigurationError
     *         If a provider-configuration file violates the specified format
     *         or names a provider class that cannot be found and instantiated
     *
     * @see #providers(java.lang.Class)
     * @see #installedProviders(java.lang.Class)
     */
    public static Iterator providers(Class service, ClassLoader loader)
        throws ServiceConfigurationError
    {
        return new LazyIterator(service, loader);
    }

Service.java下面的內部類

 /**
     * Private inner class implementing fully-lazy provider lookup
     */
    private static class LazyIterator implements Iterator {

        Class service;
        ClassLoader loader;
        Enumeration configs = null;
        Iterator pending = null;
        Set returned = new TreeSet();
        String nextName = null;

        private LazyIterator(Class service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }

        public boolean hasNext() throws ServiceConfigurationError {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    String fullName = prefix + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, ": " + x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, (URL)configs.nextElement(), returned);
            }
            nextName = (String)pending.next();
            return true;
        }

        public Object next() throws ServiceConfigurationError {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            String cn = nextName;
            nextName = null;
            try {
                return Class.forName(cn, true, loader).newInstance();
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            } catch (Exception x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated: " + x,
                     x);
            }
            return null;        /* This cannot happen */
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

    }

  

 

 

    /**
     * Returns the system wide default HttpServerProvider for this invocation of
     * the Java virtual machine.
     *
     * <p> The first invocation of this method locates the default provider
     * object as follows: </p>
     *
     * <ol>
     *
     *   <li><p> If the system property
     *   <tt>com.sun.net.httpserver.HttpServerProvider</tt> is defined then it is
     *   taken to be the fully-qualified name of a concrete provider class.
     *   The class is loaded and instantiated; if this process fails then an
     *   unspecified unchecked error or exception is thrown.  </p></li>
     *
     *   <li><p> If a provider class has been installed in a jar file that is
     *   visible to the system class loader, and that jar file contains a
     *   provider-configuration file named
     *   <tt>com.sun.net.httpserver.HttpServerProvider</tt> in the resource
     *   directory <tt>META-INF/services</tt>, then the first class name
     *   specified in that file is taken.  The class is loaded and
     *   instantiated; if this process fails then an unspecified unchecked error or exception is
     *   thrown.  </p></li>
     *
     *   <li><p> Finally, if no provider has been specified by any of the above
     *   means then the system-default provider class is instantiated and the
     *   result is returned.  </p></li>
     *
     * </ol>
     *
     * <p> Subsequent invocations of this method return the provider that was
     * returned by the first invocation.  </p>
     *
     * @return  The system-wide default HttpServerProvider
     */
    public static HttpServerProvider provider () {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return (HttpServerProvider)AccessController
                .doPrivileged(new PrivilegedAction<Object>() {
                        public Object run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = new sun.net.httpserver.DefaultHttpServerProvider();
                            return provider;
                        }
                    });
        }
    }

  

 最終如果前兩種方法都沒有找到相應的HttpServerProvider實例,則使用sun公司為我們提供的HttpServerProvider實例

sun.net.httpserver.DefaultHttpServerProvider類

也就是我們通常使用的類。

 

sun.net.httpserver.DefaultHttpServerProvider類

package sun.net.httpserver;

import java.net.*;
import java.io.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;

public class DefaultHttpServerProvider extends HttpServerProvider {
    public HttpServer createHttpServer (InetSocketAddress addr, int backlog) throws IOException {
        return new HttpServerImpl (addr, backlog);
    }

    public HttpsServer createHttpsServer (InetSocketAddress addr, int backlog) throws IOException {
        return new HttpsServerImpl (addr, backlog);
    }
}

 sun.net.httpserver.HttpServerImpl類 (其實還有HTTPS的實現類,這里先不講)

package sun.net.httpserver;

import java.net.*;
import java.io.*;
import java.nio.*;
import java.security.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import javax.net.ssl.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;

public class HttpServerImpl extends HttpServer {

    ServerImpl server;

    HttpServerImpl () throws IOException {
        this (new InetSocketAddress(80), 0);
    }

    HttpServerImpl (
        InetSocketAddress addr, int backlog
    ) throws IOException {
        server = new ServerImpl (this, "http", addr, backlog);
    }

    public void bind (InetSocketAddress addr, int backlog) throws IOException {
        server.bind (addr, backlog);
    }

    public void start () {
        server.start();
    }

    public void setExecutor (Executor executor) {
        server.setExecutor(executor);
    }

    public Executor getExecutor () {
        return server.getExecutor();
    }

    public void stop (int delay) {
        server.stop (delay);
    }

    public HttpContextImpl createContext (String path, HttpHandler handler) {
        return server.createContext (path, handler);
    }

    public HttpContextImpl createContext (String path) {
        return server.createContext (path);
    }

    public void removeContext (String path) throws IllegalArgumentException {
        server.removeContext (path);
    }

    public void removeContext (HttpContext context) throws IllegalArgumentException {
        server.removeContext (context);
    }

    public InetSocketAddress getAddress() {
        return server.getAddress();
    }
}

  

 


免責聲明!

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



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