Preface
It's a good idea to understand the java spi(service provider interface) mechanism before going into dubbo source code. Simply put, spi can help us load the interface implementation classes described in the specified file. Hmm... That's it? Isn't that easy, even though I'm a vegetable melon, I know it Class.forName Oops~Let's study it then~
java spi
demo
Despite the uniformity, a working demo is given
// First you need an external interface public interface GreetOrBye { String say(String name); } // And two implementation classes public class Bye implements GreetOrBye { @Override public String say(String name) { return "bye " + name; } } public class Greet implements GreetOrBye { @Override public String say(String name) { return "hi " + name; } } //Then the execution class public class Launcher { public static void say(String name) { ServiceLoader<GreetOrBye> greetOrByeServiceLoader = ServiceLoader.load(GreetOrBye.class); Iterator<GreetOrBye> iterator = greetOrByeServiceLoader.iterator(); while (iterator.hasNext()) { GreetOrBye greetOrBye = iterator.next(); System.out.println(greetOrBye.say(name)); } } public static void main(String[] args) { say("wahahah"); } }
Following is the file you need to specify, with the directory name fixed to services under META-INF and the file name fully qualified for the interface File content is the fully qualified name of the implementation class Run Results
hi wahahah bye wahahah
Ordinary ~ (Ancient Music?) Although I haven't seen ServiceLoader's code yet, from my code experience in the 1940s, it must be a process of reading files, reflecting the creation of objects. Okay ~Now prove yourself
ServiceLoader
Watch the notes before the attributes for the vegetable and melon conventions.
Comment Summary
Two nouns are defined in the comment
A service is an exposed interface or class, usually an abstract class or a concrete class, but is not recommended. A service provider is an implementation class, usually a proxy class, whose content determines the specific implementation class Then a few requests were made
**The service provider must provide a parameterless construct** The service provider is defined in META-INF/services, and the file name is the fully qualified name of the service, such asCom.togo.spi.Helloworld.GreetOrByeThe file content is the fully qualified name of the service provider for each line, such asCom.togo.spi.Helloworld.impl.Greet If a service provider appears in more than one file or more times in a single file, the service loader will deweight. Service providers and configuration files do not necessarily need to be in one jar, but service provider s must be accessible by the loader that loads the configuration file (that's what we'll look at then) Service providers are lazy to load (load on demand) ServiceLoader is thread insecure
attribute
ServiceLoader implements the Iterable interface with the following properties.
// File Path private static final String PREFIX = "META-INF/services/"; // Exposed interface types private final Class<S> service; // classloader private final ClassLoader loader; // Security-related private final AccessControlContext acc; // Cache Loaded Classes private LinkedHashMap<String,S> providers = new LinkedHashMap<>(); // Lazy Load Iterator private LazyIterator lookupIterator;
Looking at the names and comments, you should be able to guess what each attribute does. We'll look at the source code to get a better idea.
Source code
public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); } public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){ return new ServiceLoader<>(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(); } public void reload() { providers.clear(); lookupIterator = new LazyIterator(service, loader); } private LazyIterator(Class<S> service, ClassLoader loader) { this.service = service; this.loader = loader; }
The load method actually creates a new ServiceLoader object (because the construction method is private ~), uses the class loader of the current thread, and the entire construction process is some assignment operations. In the reload method, the cached object in the map is cleared and a LazyIterator is re-created, which is all assignment in the LazyIterator construction method. Since it is lazy to load,Of course all the important operations are in use.
public Iterator<S> iterator() { return new Iterator<S>() { // Loaded Objects Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator(); // Find from loaded objects first, not lookupIterator public boolean hasNext() { if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); } // Logical Icon public S next() { if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); } public void remove() { throw new UnsupportedOperationException(); } }; }
public boolean hasNext() { if (acc == null) { // By default, the AccessControlContext authors are not concerned about ~~ return hasNextService(); } else { PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() { public Boolean run() { return hasNextService(); } }; return AccessController.doPrivileged(action, acc); } } private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { // Read File Operation String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } // Parse the file, store the string in the file in the list, and return the iterator of the list pending = parse(service, configs.nextElement()); } nextName = pending.next(); return true; }
The next() method is also simpler, reflecting the creation of the object after getting the fully qualified name of the class, just as we started predicting.
summary
This java spi analysis shows that the code is simple, but the focus is on learning thought-oriented programming. For example, we often use different database-driven code and have ServiceLoader in DriverManager.