Analysis of the source code of Serviceloader in JDK

Source: Internet
Author: User
Tags iterable throw exception

Premise

Immediately after the previous article, "through the source of the JDK resource loading", Serviceloader is the SPI (Service Provider Interface) in the Services class loaded core class, that is, this article first introduced the use of Serviceloader, Re-analyze its source code.

Use of Serviceloader

Here is a classic example, MySQL Java driver is loaded through the Serviceloader, the first to introduce mysql-connector-java the dependency:

<dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>5.1.47</version></dependency>

View the Meta-inf directory under this dependent source package, which is visible:

We then look at Java.lang.DriverManager, the static code block contains:

static {    loadInitialDrivers();    println("JDBC DriverManager initialized");}

Where you can see loadInitialDrivers() the following code snippet:

Java.lang.DriverManager is the underlying class that initiates the classloader load, but it can load rt.jar classes outside of the package, as mentioned in the previous article, where the parent delegation model was broken because the thread context ClassLoader was used to load classes in Serviceloader. The process of JDBC loading here is the typical use of SPI, which summarizes the following rules:

    • 1, need to define an interface.
    • 2, the interface provider needs to implement the 1th step of the interface.
    • 3, the interface provider in the Meta-inf/services directory to create a text file, the file name is the 1th step of the interface defined by the fully qualified class name, the text content is the interface implementation class of the fully qualified class name, each different implementation of a separate row.
    • 4. Use Serviceloader to load the interface class and get an instance iterator for the implementation of the interface.

To give a simple example, first define an interface and two implementations:

public interface Say {  void say();}public class SayBye implements Say {    @Override    public void say() {        System.out.println("Bye!");    }}public class SayHello implements Say {    @Override    public void say() {        System.out.println("Hello!");    }}

Next, add the following file in the project's meta-inf/services:

Finally through the main function validation:

The implementation of this approach based on SPI or Serviceloader loading interfaces can also be widely used in relatively basic components, as this is a mature specification.

Serviceloader Source Code Analysis

Above through a classic example and an example of how the use of Serviceloader, and then we dig into the Serviceloader source code. Let's look at the class signature and attribute definitions for Serviceloader:

  public final class Serviceloader<s> implements iterable<s>{//directory of the path of the resource that needs to be loaded,    Fixed is the meta-inf/services/private static final String PREFIX = "meta-inf/services/" under classpath; Serviceloader requires a class or interface that needs to be loaded//the class or interface representing the service being loaded private final CLASS&L T    S> Service; Serviceloader class loader reference used for class loading//The class loader used to locate, load, and instantiate providers private final    ClassLoader loader; Permission control contexts//The access controls context taken when the Serviceloader is created private final accesscontrolcontext    Acc An implementation instance of an instance-based sequential cache class where key is the fully qualified class name of the implementation class//Cached providers, in instantiation order private linkedhashmap<string,s>    providers = new linkedhashmap<> ();    Current "lazy find" iterator, this is the core of Serviceloader//The Lazy-lookup iterator private lazyiterator lookupiterator;    Temporarily ignore other code ...} 

Serviceloader implements the Iterable interface, which suggests that when we analyze its source code, we need to focus on iterator() the implementation of analytical methods. Serviceloader relies on class loader instances for class loading, and its core attribute, Lazyiterator, is used to implement iterator() the method, and the following focuses on the analysis. Next, we analyze the Serviceloader constructor:

public void reload() {    //清空缓存    providers.clear();    //构造LazyIterator实例    lookupIterator = new LazyIterator(service, loader);}private ServiceLoader(Class<S> svc, ClassLoader cl) {    service = Objects.requireNonNull(svc, "Service interface cannot be null");    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;    reload();}

Serviceloader has only one private constructor, that is, it cannot be instantiated by a constructor, but to instantiate a serviceloader must rely on its static method to invoke the private construct to complete the instantiation operation, and the instantiation process takes several steps:

    • 1. The class instance cannot be null if it is passed into the interface, or an exception will be thrown.
    • 2. If the incoming ClassLoader instance is null, the Application class loader (application ClassLoader) is used.
    • 3. Instantiate the access control context.
    • 4. Invoke the instance method reload() , empty the cache of the implementation class instance of the target load class and construct the Lazyiterator instance.

Note that reload() the modifier of the instance method is public, that is, you can invoke the active call to clear the cache of the implementation class instance of the target load class and reconstruct the Lazyiterator instance. Then look at the static method provided by Serviceloader:

  public static <S> serviceloader<s> load (class<s> service, ClassLoader Loader) {return new serviceloader<> (service, loader);} public static <S> serviceloader<s> load (class<s> service) {ClassLoader cl = Thread.CurrentThread (). g    Etcontextclassloader (); return Serviceloader.load (service, CL);} public static <S> serviceloader<s> loadinstalled (class<s> service) {ClassLoader CL = Classloader.get    Systemclassloader ();    ClassLoader prev = null;        while (CL! = null) {prev = cl;    CL = Cl.getparent (); } return Serviceloader.load (service, prev);}  

The three public static methods above are used to construct Serviceloader instances where load (class<s> service, ClassLoader loader) is a typical static factory method, The private constructor of the Serviceloader is called directly to instantiate, and in addition to specifying the target type of the load class, you need to pass in an instance of the ClassLoader. Load (class<s> service) is actually delegated to load (class<s> service, ClassLoader loader) , However, the ClassLoader used by the class loader is specified as the thread context class loader, and in general the thread context class loader obtains the application ClassLoader (the system ClassLoader). The loadinstalled (class<s> Service) method also sees the shadow of the parental delegation model, which specifies that the ClassLoader is the topmost startup class loader and, finally, the delegate to the load (class <S> service, ClassLoader loader) . Then we need to focus on the analysis of Serviceloader#iterator () :

  public iterator<s> Iterator () {//iterator Anonymous implementation return new Iterator<s> () { The target class implements an iterator instance of the Entry of the class instance cache Map, iterator<map.entry<string,s>> knownproviders = Providers.entryset (). iter                Ator (); First determine whether there is a next instance from the cache, otherwise the lazy load iterator lazyiterator to determine if there is a next instance public boolean Hasnext () {if (Knownproviders.hasne            XT ()) return true;        return Lookupiterator.hasnext (); }//If the cache determines if there is a next instance, if there is a value from the cache directly returned//otherwise by lazy loading iterator lazyiterator Gets the next instance public S next () {if (k            Nownproviders.hasnext ()) return Knownproviders.next (). GetValue ();        return Lookupiterator.next ();        }//Do not support remove operation, throw exception public void remove () {throw new unsupportedoperationexception (); }    };}

iterator () internal is only an anonymous implementation of the iterator interface, Hasnext () and next () Method is the first to determine whether there is already an instance of the implementation class in the cache, if there is a direct return from the cache, or call the lazy load iterator lazyiterator instance to get, and Lazyiterator itself is a iterator interface implementation, It is a private internal class of Serviceloader, the source code is as follows:

Private class lazyiteratorimplements iterator<s>{class<s> service;        ClassLoader loader;        The URL collection of the loaded resource enumeration<url> configs = null;        A collection of fully qualified class names for all implementation classes that need to be loaded iterator<string> pending = null;        The fully qualified class name of the next implementation class that needs to be loaded String nextname = null;            Private Lazyiterator (class<s> service, ClassLoader loader) {this.service = service;        This.loader = loader; Private Boolean Hasnextservice () {///if the fully qualified class name of the next implementation class that needs to be loaded is not NULL, then the content exists in the resource if (nextname! = N            ull) {return true; }//If the URL collection of the loaded resource is NULL then try to load if (configs = = null) {try {//resource Name , Meta-inf/services + ' The fully qualified class name of the class that needs to be loaded '//This gets exactly the resource name of the file that needs to be loaded String FullName = PREFIX                    + Service.getname ();                  Here in fact the ClassLoader instance should not be null if (loader = = null)      Configs = Classloader.getsystemresources (fullName);                else//Load resources from Classpath configs = loader.getresources (fullName);                } catch (IOException x) {Fail (service, "Error Locating configuration Files", x);                }}//resolves the fully qualified class name of all implementation classes to be loaded from the resource while ((pending = = null) | |!pending.hasnext ()) {                if (!configs.hasmoreelements ()) {return false;            } pending = Parse (service, configs.nextelement ());            }//Gets the fully qualified class name of the next implementation class that needs to be loaded Nextname = Pending.next ();        return true;            } private S Nextservice () {if (!hasnextservice ()) throw new Nosuchelementexception ();            String cn = nextname;            Nextname = null;            Class<?> c = null; try {//Reflection construct class<s> Instance C = CLass.forname (CN, false, loader);            } catch (ClassNotFoundException x) {Fail (service, "Provider" + CN + "not found");                }//A type judgment is made here, that is, the implementation class must be a derived class of the currently loaded class or interface, otherwise throw an exception to terminate if (!service.isassignablefrom (c)) {            Fail (service, "Provider" + CN + "not a subtype"); } try {//is instantiated by Class#newinstance () and is coerced into an instance of the corresponding type S p = service.cast (c.newinst                Ance ());                Add cache, key is the fully qualified class name of the implementation class, value is the instance of the implementation class Providers.put (CN, p);            return p;                     } catch (Throwable x) {Fail (service, "Provider" + CN + "could not is instantiated",            x);          } throw new Error (); This cannot happen} public boolean hasnext () {if (acc = = null) {return Hasne            Xtservice ();               } else { privilegedaction<boolean> action = new privilegedaction<boolean> () {public Boolean run ()                {return Hasnextservice ();}                };            Return accesscontroller.doprivileged (Action, ACC);            }} public S next () {if (acc = = null) {return nextservice (); } else {privilegedaction<s> action = new privilegedaction<s> () {public S R                Un () {return Nextservice ();}                };            Return accesscontroller.doprivileged (Action, ACC);        }} public void Remove () {throw new unsupportedoperationexception (); }    }

The

Lazyiterator is also an implementation of the iterator interface, and its lazy nature indicates that it is always implemented anonymously on the iterator interface of Serviceloader iterator () Execute hasnext () to determine if there is a next implementation or next () to get an instance of the next implementation class, or lazy-load the next instance of the implementation class. Finally, the source code for the parsing process of the resource file after loading the resource file:

Private iterator<string> Parse (class<?> service, URL u) throws serviceconfigurationerror{InputStream I        n = null;        BufferedReader r = null;        The full class name of all implementation classes in the file, each line being an element arraylist<string> names = new arraylist<> ();            try {in = U.openstream ();            R = new BufferedReader (new InputStreamReader (in, "Utf-8"));            int LC = 1;        while (LC = ParseLine (Service, U, R, LC, names)) >= 0);        } catch (IOException x) {Fail (service, "Error reading configuration file", x);                } finally {try {if (r! = null) r.close ();            if (in = null) In.close ();            } catch (IOException y) {fail (service, "Error closing configuration file", y); }}//Returns an iterator instance of ArrayList return Names.iterator ();} Parse the contents of each row in the resource file private int parseline (class<?> service, URL u, bufferedreader r, int LC, LIST&LT                         String> names) throws IOException, serviceconfigurationerror{//Next row no content, return-1, for the upper layer can jump out of the loop        String ln = r.readline ();        if (ln = = null) {return-1;        }//If there is a ' # ' character, the content before the first ' # ' string is intercepted, the ' # ' character is followed by the comment content int ci = ln.indexof (' # ');        if (CI >= 0) ln = ln.substring (0, CI);        ln = Ln.trim ();        int n = ln.length (); if (n! = 0) {//cannot exist space characters ' and special characters ' \ t ' if ((Ln.indexof (') ') >= 0) | |                (Ln.indexof (' \ t ') >= 0))            Fail (service, U, LC, "illegal configuration-file syntax");            int cp = LN.CODEPOINTAT (0); Determines whether the first char is a valid Java start identifier if (!            Character.isjavaidentifierstart (CP)) fail (service, U, LC, "Illegal provider-class name:" + ln);                Determines whether all other strings belong to a valid Java identifier for (int i = Character.charcount (CP); i < n; i + = Character.charcount (cp)) {                CP = Ln.codepointat (i); if (! Character.isjavaIdentifierpart (CP) && (cp! = '))            Fail (service, U, LC, "Illegal provider-class name:" + ln); }//If the full class name is not present in the cache or the loaded list does not exist, the full class name is added to the loaded full-class list if (!providers.containskey (LN) &&!n        Ames.contains (LN)) names.add (LN);    } return LC + 1; }

The entire resource file parsing process is not complex, mainly including the file content of the word conforms to the legal judgment and cache to avoid repeated loading judgment.

Summary

SPI is widely used in the loading of third-party plug-in class libraries, most commonly such as JDBC, JNDI, JCE (Java Encryption Module extension) and other class libraries. Understanding how Serviceloader works helps you write scalable, pluggable class libraries.

(The end of this article c-1-d e-20181014)

Analysis of the source code of Serviceloader in JDK

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.