1, Loading mechanism of class
The loader loading of classes in Java comes from files, networks, source files, etc. By default, the class loader of the jvm uses the parent delegation mechanism, Bootstrap ClassLoader, Extension ClassLoader, and attachment classloader (system classloader). Each class loader loads files in the specified location. It can inherit java.lang.classloader custom class loader.
Bootstrap ClassLoader: responsible for loading the class files in the rt.jar package of JDK, which is the parent class of all classes
Extension ClassLoader: the extension class library responsible for loading Java loads classes from the jre/lib/ect directory or the directory specified by the java.ext.dirs system property. It is the parent class loader of System ClassLoader
System ClassLoader(Application ClassLoader): responsible for loading class file from classpath environment variable
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // Verify loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { //Parent delegation mechanism if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); //Play by yourself c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } -------------------------------------NB Dividing line----------------------------------- public Enumeration<URL> getResources(String name) throws IOException { @SuppressWarnings("unchecked") Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2]; //Get resources through classLoader if (parent != null) { tmp[0] = parent.getResources(name); } else { tmp[0] = getBootstrapResources(name); } tmp[1] = findResources(name); return new CompoundEnumeration<>(tmp); }
The thread context class loader can be obtained through Thread.currentThread().getClassLoader() and Thread.currentThread().getContextClassLoader(). The default thread is AppClassLoader.
Two, SPI
Breaking the parental delegation model, the most basic class of jdk is the user's api call, which is also called by api users, such as spi.
SPI Brief introduction of mechanism SPI The full name is Service Provider Interface,It is mainly used in manufacturers' custom components or plug-ins. stay java.util.ServiceLoader There is a more detailed introduction in the document of. Simple summary java SPI Mechanism idea: the abstract modules in our system often have many different implementation schemes, such as the log module xml Analysis module jdbc Module, etc. In the object-oriented design, we generally recommend the interface programming between modules, and there is no hard coding between modules. Once a specific implementation class is involved in the code, it violates the pluggable principle. If you need to replace an implementation, you need to modify the code. In order to realize the dynamic specification of module assembly, a service discovery mechanism is needed. Java SPI It is to provide a mechanism for finding a service implementation for an interface. A bit similar. IOC The idea is to move the assembly control right out of the program, which is particularly important in modular design.
1.JDK implementation
Create a new META-INF/services / configuration file under classpath. The file name is the reference of the interface class, and the content is the reference of the implementation class of the interface.
@Test public void testSpi() throws IOException { //Get through ServiceLoader ServiceLoader<SpiDemo> loader=ServiceLoader.load(SpiDemo.class); for(SpiDemo demo:loader){ System.out.println(demo.getName()); } } ------------------------------NB Dividing line----------------------------------------- public final class ServiceLoader<S> implements Iterable<S> { //Default specified path META-INF/services/ private static final String PREFIX = "META-INF/services/"; ......NB Ellipsis............... 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); } ..................NB Ellipsis............. while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } //Read the contents of the configuration file and then use reference to instance it through class.forname(). pending = parse(service, configs.nextElement()); } nextName = pending.next(); ..................NB Ellipsis.............
2. spi in spring
In the process of spring boot auto loading, all jar s under classpath will be loaded to search META-INF/spring.factories. Then load through spring factors loader, and enter the process of loading bean s after loading.
public final class SpringFactoriesLoader { //Specified path public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap(); ..........................NB Ellipsis............................. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); //Parsing the spring.factories file (in the form of key/value) Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryTypeName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryImplementationName = var9[var11]; //Put the reference in the file into the map result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } } >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) { Assert.notNull(factoryType, "'factoryType' must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames); } List<T> result = new ArrayList(factoryImplementationNames.size()); Iterator var5 = factoryImplementationNames.iterator(); while(var5.hasNext()) { String factoryImplementationName = (String)var5.next(); result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result; }
It will eventually be loaded in the AutoConfigurationImportSelector class.
/** * AutoConfigurationImportSelector Implemented beanclassloaderaware / resourceloaderaware / beanfactorylaware / environmentaware / deferredimportselector * These things. * DeferredImportSelector */ public class AutoConfigurationImportSelector implements , BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry(); private static final String[] NO_IMPORTS = {}; private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class); private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude"; //beanFactory can be obtained by implementing BeanFactoryAware private ConfigurableListableBeanFactory beanFactory; //Environment can be obtained by implementing EnvironmentAware private Environment environment; //Implement BeanClassLoaderAware to obtain ClassLoader private ClassLoader beanClassLoader; //ResourceLoaderAware can get ResourceLoader private ResourceLoader resourceLoader; @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } //Load "meta-inf /" + "spring autoconfigure metadata. Properties" AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); //Load AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } ....................NB................................. public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName())); AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }