Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

[SpringBoot] profound and simple Analysis of Spring Factories Mechanism in SpringBoot

2025-02-22 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/03 Report--

WeChat account: GitShare

Official account of Wechat: a straw that loves to toss.

If you have any questions or suggestions, please leave a message on the official account [1]

Previous continuation

In order to help the majority of SpringBoot users to "know what it is, but also need to know why", the author will deeply analyze the SpringBoot2.0.0.RELEASE version through a series of SpringBoot articles, so that you can have a deep understanding of its internal working principle.

1. [SpringBoot] use SpringBoot to quickly build and start the project

2. [SpringBoot] explain the startup process of SpringBoot application in detail

3. [SpringBoot] in-depth analysis of the application type identification mechanism of SpringBoot

Java SPI mechanism

What is Java SPI?

The full name of SPI is Service Provider Interface. Most developers may not be familiar with it because this is for vendors or plug-ins.

There are often many different implementation schemes for the abstract modules in our system, such as the scheme of log module, xml parsing module, jdbc module and so on.

In the object-oriented design, we generally recommend the interface-based programming between modules, and the implementation classes are not hard-coded between modules. Once a specific implementation class is involved in the code, it violates the principle of pluggability

If you need to replace an implementation, you need to modify the code. In order to realize that it can not be dynamically specified in the program when the module is assembled, a service discovery mechanism is needed.

Java SPI provides such a mechanism: a mechanism to find an implementation for an interface.

Java SPI's agreement

When the service provider provides an implementation of the service interface, a file named after the service interface is also created in the META-INF/services/ directory of the jar package.

In this file is the specific implementation class that implements the service interface. When the external program assembles this module, it can find the specific implementation class name through the configuration file in the jar package META-INF/services/, and load the instantiation to complete the injection of the module.

Based on such a convention, it is good to find the implementation class of the service interface without having to make it in the code.

Jdk provides a utility class for service implementation lookups: java.util.ServiceLoader.

SPI Mechanism in SpringBoot

There is also a loading mechanism similar to Java SPI in Spring. It configures the implementation class names of the interfaces in the META-INF/spring.factories file, then reads these configuration files in the program and instantiates them.

This custom SPI mechanism is the basis of the Spring Boot Starter implementation.

Principle of Spring Factories implementation

The SpringFactoriesLoader class is defined in the spring-core package, which implements the function of retrieving the META-INF/spring.factories file and getting the configuration of the specified interface.

Two external methods are defined in this class:

1 、 loadFactories

Get an instance of its implementation class based on the interface class, and this method returns a list of objects with its source code:

Public static List loadFactories (Class factoryClass, @ Nullable ClassLoader classLoader) {

Assert.notNull (factoryClass, "'factoryClass' must not be null")

ClassLoader classLoaderToUse = classLoader

If (classLoaderToUse = = null) {

ClassLoaderToUse = SpringFactoriesLoader.class.getClassLoader ()

}

List factoryNames = loadFactoryNames (factoryClass, classLoaderToUse)

If (logger.isTraceEnabled ()) {

Logger.trace ("Loaded [" + factoryClass.getName () + "] names:" + factoryNames)

}

List result = new ArrayList (factoryNames.size ())

For (String factoryName: factoryNames) {

Result.add (instantiateFactory (factoryName, factoryClass, classLoaderToUse))

}

AnnotationAwareOrderComparator.sort (result)

Return result

}

Get ClassLoader first

Call the loadFacotoryNames () method to get the factoryNames. The source code is as follows:

Public static List loadFactoryNames (Class factoryClass, @ Nullable ClassLoader classLoader) {

/ / get the class name

String factoryClassName = factoryClass.getName ()

/ / get the value of the specified factory in the Map of the configuration information of all META-INF/spring.factories files under the current ClassLoader. If it does not exist, an empty list is returned.

Return loadSpringFactories (classLoader) .getOrDefault (factoryClassName, Collections.emptyList ())

}

Call the loadSpringFactories method to get the configuration information of all META-INF/spring.factories files under the current ClassLoader

Private static Map loadSpringFactories (@ Nullable ClassLoader classLoader) {

/ / get the corresponding from the cache

MultiValueMap result = cache.get (classLoader)

If (result! = null)

Return result

Try {

/ / get all the URL paths that contain META-INF/spring.factories files under the current ClassLoader

Enumeration urls = (classLoader! = null?

ClassLoader.getResources (FACTORIES_RESOURCE_LOCATION):

ClassLoader.getSystemResources (FACTORIES_RESOURCE_LOCATION))

/ / initial return object

Result = new LinkedMultiValueMap ()

/ / iterate through all URL collections containing META-INF/spring.factories files

While (urls.hasMoreElements ()) {

URL url = urls.nextElement ()

UrlResource resource = new UrlResource (url)

/ / convert to Properties object

Properties properties = PropertiesLoaderUtils.loadProperties (resource)

/ / iterate through all the attributes in the META-INF/spring.factories file

For (Map.Entry entry: properties.entrySet ()) {

/ / if an API wants to configure multiple implementation classes, you can split it with','to convert the value corresponding to the current Key to List

List factoryClassNames = Arrays.asList (StringUtils.commaDelimitedListToStringArray ((String) entry.getValue ()

/ / add to the returned object

Result.addAll ((String) entry.getKey (), factoryClassNames)

}

}

/ / add to the cache

Cache.put (classLoader, result)

/ / return the result

Return result

}

Catch (IOException ex) {

Throw new IllegalArgumentException ("Unable to load factories from location [" +)

FACTORIES_RESOURCE_LOCATION + "]", ex)

}

}

We can configure the spring.factories file in our own jar without affecting the configuration elsewhere or being overwritten by someone else's configuration.

This is very helpful for us to customize the extension later.

Call instantiateFactory to instantiate the corresponding Factory object obtained, and its source code:

Private static T instantiateFactory (String instanceClassName, Class factoryClass, ClassLoader classLoader) {

Try {

/ / get an instance of the class based on the class name, which was parsed in the previous article.

Class instanceClass = ClassUtils.forName (instanceClassName, classLoader)

/ / isAssignableFrom () determines whether the object of instanceClassName is a subclass of FactoryClass or the same class, and returns true if so.

If (! factoryClass.isAssignableFrom (instanceClass)) {

/ / throw an exception if the instantiated target class is not a subclass of the Factory class or is not a Factory class

Throw new IllegalArgumentException ("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName () + "]")

}

/ / instantiate the target class through the java reflection mechanism

Return (T) ReflectionUtils.accessibleConstructor (instanceClass). NewInstance ()

}

Catch (Throwable ex) {

Throw new IllegalArgumentException ("Unable to instantiate factory class:" + factoryClass.getName (), ex)

}

}

We can see from this code that it only supports constructors with no parameters.

2 、 loadFactoryNames

To get the name of its interface class based on the interface, this method returns a list of class names, whose source code has been analyzed above.

Application of SpringBoot when starting up

During SpringBoot startup, the SpringApplication object is created with the following two lines of code:

SetInitializers ((Collection) getSpringFactoriesInstances (ApplicationContextInitializer.class))

SetListeners ((Collection) getSpringFactoriesInstances (ApplicationListener.class))

Use SpringFactoriesLoader to find and load all available ApplicationContextInitializer in the application's classpath

Use SpringFactoriesLoader to find and load all available ApplicationListener in the application's classpath

Application of Spring Factories Mechanism

In our daily work, we may need to implement some SDK or Spring Boot Starter to be used by others, and with this we can use the Factories mechanism.

The Factories mechanism allows the use of SDK or Starter to require little or no configuration, just to introduce our jar package into the service.

Knowledge point

IsAssignableFrom () parsing of Class

Public native boolean isAssignableFrom (Class cls)

It can be seen from the method signature that it is written by a local method, that is, C code.

Its function is:

There are two classes of type Class, one is the class object that calls the isAssignableFrom method (later called object a), and the class object in the method as a parameter (called object b). These two objects return true if the following conditions are met, otherwise false:

The class information corresponding to an object is the parent class or interface of the class information corresponding to b object. It is simply understood that an is the parent class or interface of b

The class information corresponding to an object is the same as that corresponding to b object. It is simply understood that an and b are the same class or the same interface.

Postscript

In order to help the majority of SpringBoot users to "know what it is, but also need to know why", the author will deeply analyze the SpringBoot2.0.0.RELEASE version through a series of SpringBoot articles, so that you can have a deep understanding of its internal working principle. Please follow the official account of GitShare to provide you with more and more high-quality technical tutorials.

Picture note: love to toss about the straw

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report