One, foreword

Four features of Springboot

What is the relationship between SpringBoot and microservices? Answer: All microservices components are integrated into SpringBoot, as is Spring Cloud, which should be version-compatible with SpringBoot when used.

1. The first feature, EnableAutoConfiguration, translates to automatic assembly.

The second feature, the Starter dependency, relies on requests for auto-assembly. 3. The third feature is that the Actuator monitors and provides some endpoints, which can access health information and metrics information based on HTTP and JMX.

4, the fourth feature, Spring Boot CLI, which is the springboot command line operation function provided by SpringCloud, it quickly builds springboot applications through Groovy scripts, rarely used, generally based on IDEA applications to build Springboot applications, Skip.

Summary: Autowage and startup dependencies are different. Startup dependencies depend on autowage, and monitoring provides some form of access.

Springboot automatic assembly

3.1 Springboot test: integrate Redis and introduce automatic assembly

Question: @AutoWired dependency injection can only be implemented if an instance of the bean exists in IOC. Who loaded the bean instance?

Answer: There are three ways to load bean instances in Spring: XML file configuration class Enable annotation, now we do not do these three ways, but why springBoot can implement bean assembly, this is springBoot automatic assembly, complete the automatic loading of beans, just write @autowired, How is the principle of automatic assembly realized?

3.2 Thinking of automatic assembly

3.2.1 Thinking about automatic assembly



Why is SpringBoot capable of auto-assembly? In short, the dependencies introduced in POM.xml (i.e., jar packages) are all agreed to have a uniform format, and all have XxxConfiguration classes, which are scanned directly by SpringBoot when used to complete the assembly, but are not visible to the developer, so they are called autoroassembly. Each third-party starter has a configuration class called XxxConfiguration, which is a convention.

Question 1: How does the Spring engine know where the specific configuration classes are in the third-party component starter?

Springboot automatic loading problem 2: how to implement batch scan configuration class XxxConfiguration Redis Mybatis dubble?

3.2.2 From static assembly bean instances to dynamic assembly bean instances

There are three ways to static assembly of Spring: XML file Configuration Configuration class @enable module assembly There are two ways to dynamic assembly of Spring: ImportSelector interface Registator interface

Each third-party starter has a configuration class XxxConfiguration. Springboot uses multiple third-party components. How do I scan multiple configuration classes XxxConfiguration in batches

Answer: Batch bean loading mechanism. There are two dynamic bean loading mechanisms in Spring, one is ImportSelector interface and the other is Registator interface, both are interfaces. The ImportSelector interface is introduced in this paper.

Dynamic loading: The configuration class XxxConfiguration is loaded in state according to the specific running conditions of the runtime context, as opposed to @Conditional.

3.3 Simulation of automatic assembly

This section presents a project to simulate Springboot automatic assembly. The project structure is as follows:

3.3.1 Two Beans and two configuration classes

This paper presents a mybatis bean class, MyBatis simulated configuration class, Redis bean class, redis simulated configuration class



3.3.2 rainfall distribution on 10-12 MyImportSelector class

Emulates the ImportSelector implementation class, implements the ImportSelector interface, and returns the configuration class

public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata AnnotationMetadata) {// Load all annotationMetadata classes in annotationMetadata. // Spring just needs to know package.class.classname.returnnew String[]{MybatisConfiguration.class.getName(), RedisConfiguration.class.getName()}; }}Copy the code

3.3.3 @ MyEnableConfiguration annotation

How do I let SpringbootApplication know about the configuration class returned by MyImportSelector? All you need to do is declare an annotation.

Define an annotation, @import (ImportSelector interface implementation class.class)



The use of the annotations SpringApplicationdemoApplication class

3.3.4 Detailed operation process explanation

In this project, actually SpringbootApplication don’t use @ MyEnableConfiguration can also, because SpringbootdemopageApplication in bag for package com. Example. The demo; However, where the two beans bag for package com. The example. The demo. Mybatis and package com. Example. The demo. Redis, so start SpringbootdemopageApplication, The package and its children are scanned, and two beans are scanned.

If the SpringbootdemopageApplication com. Example. Demo. Springbootdemo, you must configure @ MyEnableConfiguration annotations, startup, Scan @myEnableconFiguration and know @import (MyImportSelector. Class). In MyImportSelector. Class, Spring knows the path to the two configuration classes. We have the following load bean and print it out.

3.3.5 Add-on: Either a configuration class or a bean class directly

It seems that putting the bean class directly in the MyImportSelector return value would duplicate the assembly. In general, it is better to put the configuration class.

Summary: Mybatis and com.example.demo.redis are used to simulate third-party components. Each component includes configuration classes and entity beans), which can be found using the ImportSelector interface and annotated to SpringBoot, solving the first problem. For n configuration classes (mybatis and Redis simulate two configuration classes in the project), batch configuration is not a problem.

Note 1: @ComponentScan or Base-Package does not work. It is a static assembly that requires the path of the scan to be written out. However, when using a third-party component, you do not know where its beans or configuration classes are, which is the limitation of static assembly. Note 2: using @contional also not only, just one gives a judgment on whether to assemble or not, of course below our conditional configuration.

3.4 Analyze the underlying principle of automatic assembly from simulation to practice

3.4.1 Dynamic assembly is realized at the bottom of Springboot from simulation to practice

** You can see that the @SpringBootApplication annotation is the same as the @MyEnableconFiguration annotation in our simulation. Both integrate the ImportSelector interface to implement their selectImports return an array of configuration classes. ** No wonder the incipient redisTemplate can be assembled automatically.

3.4.2 Redis configures classes from simulation to reality

3.4.3 Springboot automatically loaded core source code

@Autowird private RedisTemplate redisTemplate; // Automatic loadingCopy the code

First, AutoConfigurationImportSelector automatic configuration ImportSelector: getAutoConfigurationEntry automatic configuration/automatic loading of entity



Second, the SpringFactoriesLoader says: Spring.factories

3.5 Springboot automatic assembly summary

When developers use it, just use it directly

@Autowired private RedisTemplate<string,string> redisTemplate; // If you are automatically configured, you can use it directlyCopy the code

Question: How does the Spring engine know where the specific starter configuration class is in practice?

First, all starter components follow a common convention. All starter components need a meta-INF /spring.factories file like this:



Second, springboot direct scan configuration file (org. Redis. RedisConfiguration org. Mybatis. MybatisConfiguration), the following figure:



Third, autoloading is done.

3.6 SPI design idea

3.6.1 What is SPI?

SPI stands for Service Provider Interface. The Service provides the interface. Here, different database drivers are used as an example, as shown in the figure below:

SPI means that we provide the interface, and the specific implementation of the interface is done by a third party (in this case, different database drivers are different, which is done by the database vendor). Java programs can be executed by importing different JAR packages, which is the extension of SPI, which is the extension of the service providing interface.

Spring. factories are based on key-value pairs (the key is the same Configuration and the value is implemented by a third party). Because each starter JAR has a Configuration annotation that provides an interface to communicate with SpringBoot, its internal implementation is done by a third party (Redis Mybatis Dubbo), Springboot simply imports a different JAR package (Redis depends on Mybatis depends on Dubbo).

To sum up SPI in one sentence:

SPI itself contains default implementations, but also provides extension points that can be replaced if needed to improve scalability and flexibility.

3.6.2 DEMO of SPI design ideas

SPI definition: SPI (Service Provider Interface), refers to the JDK provides a standard Interface, the specific implementation is determined by the vendor. For example, SQL), the above JDBC Java SPI implementation: “interface-based programming + policy mode + configuration file” combination of dynamic loading mechanism explains three projects: ServiceLoader

loadedDrivers = Serviceloader.load (Xxx interface.class); . By the way, because the Java. Util. ServiceLoader load (Xxx interface. Class) the underlying call nextService () to iterate through all depend on the meta-inf/services/directory of the specified file, for each variable content, is to get the bytecode, Providers of ServiceLoader implements Iterable. Providers of ServiceLoader implements Iterable. KnownProviders = providers.entryset ().iterator(); KnownProviders/meta-INF /services/, iterates over the contents of the file loaded from the meta-INF /services/ path, and then calls the specified method. For (DatabaseDriver DatabaseDriver: serviceLoader) {System. Out. Println (DatabaseDriver. BuildConnect (” Test “)); // First, iterate; Second, call; } or Iterator searchs = s.iterator(); if(searchs.hasNext()){ DatabaseDriver databaseDriver= searchs.next(); // First, iterate; System. The out. Println (databaseDriver. BuildConnect (” Test “)); // Second, call; } so, later See this sentence in the third party jar package, see the Java. Util. ServiceLoader. Load (Xxx interface. The class), can know that it is the use of SPI, such as commonloging and jdbc4.0 later.

The SPI design can be implemented only if:

1. Create meta-INF /services in the classpath directory

2. Create a full pathname file for the interface in this directory. Fill the file with the implementation of this extension point

ServiceLoader to load

SPI for service providers, after providing an implementation of the service interface, create a file named after the service interface in the META-INF/services/ directory of the JAR package. This file contains the concrete implementation class that implements the service interface. For service users, when an external application assembs the module, it can find the implementation class name in the meta-INF /services/ jar file and load the instantiation to complete the module injection. Based on such a convention, it is easy to find the implementation class of the service interface without having to specify it in the code.

Common logging is one of the earliest logging surface interfaces provided by Apache. Only interface, no implementation. Specific schemes are implemented by each provider. For users, by scanning META-INF/services/org.apache.com mons. Logging. LogFactory find log provider configuration files, by reading the contents of the file to find log provider implementation class. Second, before JDBC JDBC 4.0, programmers had to load drivers based on class.forname (” XXX “). Programmers had to hard-code specific database drivers in their code. Jdbc4.0 later, to find the Driver provider, based on the mechanism of spi can through the meta-inf/services/Java SQL. The Driver file specifies the implementation Class to expose drive providers, don’t have to write a Class. Class.forname (” XXX “) hard coded. static { loadInitialDrivers(); Println (” JDBC DriverManager the initialized “); } loadInitialDrivers method of the AccessController. DoPrivileged (new PrivilegedAction () {public Void the run () {/ / typical SPI here, ServiceLoader loadedDrivers = Serviceloader.load (driver.class); Iterator driversIterator = loadeddrivers.iterator (); Iterator driversIterator = loadeddrivers.iterator (); // JDBC source code just like we did in appDemo in the above three projects using SPI GOOD from simulation to real first, Try {while(driversiterator.hasnext ()) {// JDBC source code as we did in appDemo above three projects using SPI Good from simulation to actual first, Driversiterator.next (); }} catch(Throwable t) {// Do nothing} return null; }});

Question: Why, with SPI, do programmers not hardcode class.forname (” XXX “) to specify specific database drivers? Answer: loadInitialDrivers() for the user; To traverse all the content in the configuration file, automatically load. Class.forname (” XXX “) code is encapsulated to Java. The util. ServiceLoader. NextService () method, this method, the PREFIX + service. The getName (); / PREFIX/meta-inf /services/ service.getName(); / PREFIX/meta-inf /services/ service.getName(); Then S p = service.cast(c.newinstance ()); Providers. Put (cn, p); Providers= providers.entryset ().iterator(); // Providers= providers.entryset ().iterator(); KnownProviders/meta-INF /services/, and then iterate through the knownProviders file to get the contents of the file loaded from the meta-INF /services/ path

Note: The ServiceLoader is JavaSE built-in classes, Java. Util. ServiceLoader, parsing Java. Util. ServiceLoader Class, from Java. Util. ServiceLoader. Load (Class < >? > < p style = “max-width: 100%; clear: both; min-height: 1em;

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)
{
    returnnew ServiceLoader<>(service, loader); } Here we get the ClassLoader for the thread context, because the upper ClassLoader cannot load the lower class in parent-delegate mode, initializing the ServiceLoader object. public final class ServiceLoader<S> implements Iterable<S> { 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 voidreload() { providers.clear(); lookupIterator = new LazyIterator(service, loader); } hasNext calls hasNextService private static Final String PREFIX ="META-INF/services/";

        private boolean hasNextService() {
            if(nextName ! = null) {return true;
            }
            if (configs == null) {
                try {
//fullName = META-INF/services/java.sql.Driver
                    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;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true; } get the fully qualified name of your implementation class from the meta-INF /services/ XXX configuration file. Here's the answer to the original question. The next method is to call class.forname with the nextName from hasNext and load the interface implementation Class with the passed ClassLoader. public Snext() {
    if (acc == null) {
        return nextService();
    } else {
        PrivilegedAction<S> action = new PrivilegedAction<S>() {
            public S run() { returnnextService(); }};returnAccessController.doPrivileged(action, acc); NextService is called to load and initialize the implementation class. private SnextService() {
    if(! hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<? > c = null; try { c = Class.forName(cn,false, loader); . try { S p = service.cast(c.newInstance()); providers.put(cn, p);Copy the code

Note: The meta-INF and services directories must be created separately

Maven quickStart project, maven Clean Maven install after the jar package; Maven WebApp project, Maven Clean Maven install after the war package.

Fourth, Springboot automatic assembly is further advanced

The starter is closely related to the autowage because all third-party starter components follow the autowage convention and provide spring.factories files and configuration classes for SpringBoot to scan for autowage.

4.1 Principle of the Starter component: The official package depends on the condition trigger

4.1.1 Introduction of conditional annotations

Spring.factories do not exist in 4.1.2 official dependency packages

Spring. factories does not exist for the official Springboot starter, so why does it not exist and how does it load? It turns out there are two types of starter packs, The official spring-boot-starter-xxx package does not have spring.factories such as spring-boot-starter-data-redis spring-boot-starter-thymeleaf Xxx-spring-boot-starter must have spring.factories

Take Redis as an example, its configuration class RedisAutoConfiguration does not exist in spring-boot-starter-data-redis, but does not exist in spring-boot-Autoconfigure package.

How does SpringBoot find the RedisAutoConfiguration class? The answer is conditional annotations.

4.1.3 Conditional annotations Ensure that the configuration class is configured

In summary, the official package relies on conditional triggers.

Spring-factories records the configuration class location of the official package



You only need to import dependencies, and only third-party packages need to write spring.Factories themselves.

4.2 Demo Spring. factories configuration classes for custom components

Create a New Maven QuickStart project: AutoConfigureDemo

Specify the configuration classpath in the spring.factories file

Maven Clean Maven install becomes a JAR package for the SpringBoot project to use

Create a New Maven QuickStart project, SpringBootCore2, and import the dependencies

4.3 Demo@conditionalonclass and other annotations to achieve conditional configuration

ConditionalOnClass({redisoperations.class}); ConditionalOnClass({redisoperations.class});

Lead-in dependence

4.4 Demo spring-autoconfigure-metadata.properties Conditional configuration

There is a spring-autoconfigure-metadata.properties file in Springboot, which is also a configuration file.

This configuration file is just, the user needs to configure this as required, in fact, our own project can also implement this configuration file and jar package.

The source of engineering

4.5 The two configuration modes are used together

Not all conditional configurations are written in class annotations. They can also be written in the spring-autoconfigure-metadata.properties configuration file, see Mybatis

Here is the conditional configuration file spring-autoconfigure-metadata.properties for Mybatis

Here is mybatis conditional note

Five, the summary

This paper introduces the characteristics of Springboot automatic assembly, from the use of Redis introduced automatic assembly, and then understand the automatic assembly, simulation implementation, reading Springboot source code, and finally introduce conditional trigger and demo implementation.

Play code every day, progress every day!!