上節講到的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(); } }