Use scenarios
In program development, there may be different package names (such as: pom depends on many jars). How to solve the problem that Spring Boot cannot be scanned by default paths?
- Method 1: Use the @Import annotation on the Spring Boot Application main class.
- Method 2: Use the file
The method is relatively simple, so I won’t introduce it too much here, and mainly talk about use.
effect
The function is to use external jars without writing configurations, and the configured configuration will be automatically added.
Internal principles and mechanisms
This mechanism is actually implemented in the SPI extension mechanism in java.
SPI mechanism
SPI full name Service Provider Interface, is a set of interfaces provided by Java for implementation or extension by third parties. Its significance lies in finding services for a certain interface. Its main application is used in the framework to find components and improve scalability.
In object-oriented design, we generally recommend that modules be programmed based on interfaces, and the implementation classes are not hard-coded between modules. Once the code involves specific implementation classes, it violates the principle of pluggability. If you need to replace an implementation, you need to modify the code. In order to achieve dynamic indication in the program when module assembly, a service discovery mechanism is required.
- SPI mechanism in Java:The mechanism for finding a service implementation for an interface is somewhat similar to the idea of IOC, which is to move the control of the assembly out of the program. This mechanism is very important in modular design.
- SPI mechanisms in Spring Boot:Configure the implementation class names of the interfaces in the META-INFO/ file, and then read the configuration files in the program and instantiate them. This customized SPI mechanism is the basis of Spring Boot Starter implementation.
Spring Factory implementation principle
The implementation depends on the SpringFactoriesLoader class in the spring-core package. This class implements the function of retrieving META-INF/ files and obtaining the configuration of the specified interface.
This class defines two external methods:
-
loadFactories
: Obtain an instance of its implementation class based on the interface class. This method returns an object list -
loadFactoryNames
: Get the name of the interface class based on the interface. This method returns a list of class names
The key to the above two methods is to obtain the file from the specified ClassLoader and parse the class name list. The specific code is as follows:
public final class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/"; private static final Log logger = (); private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap(); private SpringFactoriesLoader() { } public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) { (factoryType, "'factoryType' must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = (); } List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); if (()) { ("Loaded [" + () + "] names: " + factoryImplementationNames); } List<T> result = new ArrayList(()); Iterator var5 = (); while(()) { String factoryImplementationName = (String)(); (instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } (result); return result; } public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = (); return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, ()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)(classLoader); if (result != null) { return result; } else { try { Enumeration<URL> urls = classLoader != null ? ("META-INF/") : ("META-INF/"); LinkedMultiValueMap result = new LinkedMultiValueMap(); while(()) { URL url = (URL)(); UrlResource resource = new UrlResource(url); Properties properties = (resource); Iterator var6 = ().iterator(); while(()) { Entry<?, ?> entry = (Entry)(); String factoryTypeName = ((String)()).trim(); String[] var9 = ((String)()); int var10 = ; for(int var11 = 0; var11 < var10; ++var11) { String factoryImplementationName = var9[var11]; (factoryTypeName, ()); } } } (classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/]", var13); } } } private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) { try { Class<?> factoryImplementationClass = (factoryImplementationName, classLoader); if (!(factoryImplementationClass)) { throw new IllegalArgumentException("Class [" + factoryImplementationName + "] is not assignable to factory type [" + () + "]"); } else { return (factoryImplementationClass, new Class[0]).newInstance(); } } catch (Throwable var4) { throw new IllegalArgumentException("Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + () + "]", var4); } } }
From the above code, we can see that in this method, all files under the jar package in the entire ClassLoader will be traversed. The files will not affect each other's configuration and will not be overwritten by other people's configuration.
It is obtained through Properties parsing, so the contents in the writing file are configured in the following way.
Usage and configuration
The file must be placed in the META-INF directory in the resources directory, otherwise it will not take effect. If an interface wants to configure multiple implementation classes, you can use "," to split it.
The specific meaning of various configurations
ApplicationContextInitializer
- This configuration item is used to configure classes that implement the ApplicationContextInitializer interface, which are used to implement context initialization.
- The configuration is as follows:
=\
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { ("() " + applicationContext); } }
ApplicationListener
- Configure an application listener that must implement the ApplicationListener interface. It can be used to listen for ApplicationEvent events.
- The configuration is as follows:
=\
@Slf4j public class EmailApplicationListener implements ApplicationListener<EmailMessageEvent> { @Override public void onApplicationEvent(EmailMessageEvent event) { ("Simulate sending email..."); ("EmailApplicationListener Received messages:{}", ()); } }
AutoConfigurationImportListener
- This configuration item is used to configure the automatic configuration import listener, which must implement the AutoConfigurationImportListener interface.
- This listener can listen for AutoConfigurationImportEvent event.
- The configuration is as follows:
=\
public class MyAutoConfigurationImportListener implements AutoConfigurationImportListener { @Override public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) { ("() " + event); } }
AutoConfigurationImportFilter
- Configure the automatic configuration import filter, which must implement the AutoConfigurationImportFilter interface.
- This filter is used to filter those automatic configuration classes available.
- The configuration is as follows:
=\
public class MyConfigurationCondition implements AutoConfigurationImportFilter { @Override public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { ("() autoConfigurationClasses=" + (autoConfigurationClasses) + ", autoConfigurationMetadata=" + autoConfigurationMetadata); return new boolean[0]; } }
EnableAutoConfiguration
- Configure the automatic configuration class. These configuration classes require the @Configuration annotation.
- The configuration is as follows:
=\
@Configuration public class MyConfiguration { public MyConfiguration() { ("MyConfiguration()"); } }
FailureAnalyzer
- Configure a custom error analysis class, which requires the FailureAnalyzer interface to be implemented.
- The configuration is as follows:
=\
/** * Custom Error Analyzer */ public class MyFailureAnalyzer implements FailureAnalyzer { @Override public FailureAnalysis analyze(Throwable failure) { ("() failure=" + failure); return new FailureAnalysis("MyFailureAnalyzer execute", "test ", failure); } }
TemplateAvailabilityProvider
- Configure the availability provider of templates, and the provider needs to implement the TemplateAvailabilityProvider interface.
- The configuration is as follows:
=\
/** * Verify that the specified template supports it */ public class MyTemplateAvailabilityProvider implements TemplateAvailabilityProvider { @Override public boolean isTemplateAvailable(String view, Environment environment, ClassLoader classLoader, ResourceLoader resourceLoader) { ("() view=" + view + ", environment=" + environment + ", classLoader=" + classLoader + "resourceLoader=" + resourceLoader); return false; } }
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.