Writing in the front
Today we continue to talk about the principle of SpringBoot automatic assembly, interested in the boy can see the first introductory articleCopy the code
Analysis of SpringBoot autowiring principles (Chapter 1) using Spring. factories files for cross-module instantiation
On the basis of this process, we first introduce two problems, starting from the problems of automatic assembly. 1. When we introduce third-party JAR packages, such as the unified architecture package of the company, unified exception handling and unified permission blocking are generally encapsulated in the package. However, sometimes for our business, some functions need to be customized separately. For example, there are unified permission interception in the three-party Jar and all interception requests are processed. However, the business wants to open the black and white list, so the business can handle unified exception handling in the three-party Jar by itself. The business wants to change to please contact the owner and so on. That is, we do not want to enable some beans in the third-party Jar. We usually do this in the startup class, which is to remove the loading of this Bean. How was it removed? @SpringBootApplication(exclude = BaseInterpreterAutoConfiguration.class) 2 In the first article, we explained that spring.factories are used to instantiate across modules. If I have 1000 beans configured with spring.factories, should Spring load them all? Or how do I load on demand? The spring-factories (ES,Redis,Mongo,Mysql) Jar package contains the Bean configuration for ES,Redis,Mongo,Mysql. But for our business, I only need to operate Redis, no other fancy, that is, Spring only needs to load the beans related to Redis, no need to load the beans of ES,Mongo,Mysql So when we look at the documentation, it's like, if you want to use Redis, you just put @enableredis on the startup class, if you want to use Mongo, you just load @enablemongo on the startup class ConditionalOnBean (@conditionalonBean) ConditionalOnBean (ConditionalOnBean) ConditionalOnBean (ConditionalOnBean) Or what does it do? Any other notes like that? @ConditionalOnBean(annotation = EnableAuthAutoConfiguration.class) public class AuthAutoConfiguration {}Copy the code
@springBootApplication (exclude = *.class) implementation mechanism
Direct access to the core source AutoConfigurationImportSelector. SelectImports ()Copy the code
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (! isEnabled(annotationMetadata)) { return NO_IMPORTS; } try { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); / / load spring. Factories configuration List < String > configurations = getCandidateConfigurations (annotationMetadata, attributes); configurations = removeDuplicates(configurations); configurations = sort(configurations, autoConfigurationMetadata); Set<String> Exclusions = getExclusions(annotationMetadata, Attributes); checkExcludedClasses(configurations, exclusions); // Remove Bean configurations. RemoveAll (Exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); }}Copy the code
@ ConditionOnBean role
This configuration takes effect only when the specified class instance exists in the Spring container. Suppose we set up a starter Jar package to provide Redis service to the business side, requiring the business to add @enableredis to the startup class, so as to indicate the start of providing Redis service. In other words, the startup class adds @enbaleredis to load the Bean configuration related to RedisCopy the code
@target ({elementtype.type}) @Retention(RetentionPolicy.runtime) @Documented Public @Interface EnableRedis {} // Start class @EnableRedis public class SpringEasyMain { public static void main(String[] args) { SpringApplication.run(SpringEasyMain.class); } } @ConditionalOnBean(annotation = EnableRedis.class) @Bean public ConditionalOnExpressionDemo GetConditionalOnExpressionDemo () {System. Out. Println (" added @ EnableRedis start class, related to load the Redis Bean configuration "); return ConditionalOnExpressionDemo.builder().name("test").age(20).build(); }Copy the code
ConditionalOnExpression(value = "1<11") When the expression is valid, the configuration takes effect pseudo-codeCopy the code
@ConditionalOnExpression(value = "1<11") @Bean public ConditionalOnExpressionDemo getConditionalOnExpressionDemo() { System.out.println(" Verify that a Bean 1 is instantiated when the expression is true "); return ConditionalOnExpressionDemo.builder().name("test").age(20).build(); } @ConditionalOnExpression(value = "50>10") @Bean public ConditionalOnExpressionDemo getConditionalOnExpressionDemo2() { System.out.println(" Verify that a Bean 2 is instantiated when the expression is true "); return ConditionalOnExpressionDemo.builder().name("test").age(20).build(); }Copy the code
@ ConditionOn * * * the role of the simple is to meet the conditions of * * *, * * * * to load configuration this part I also write some of the demo, interested in handsome can directly download the validation That is to summarize: We use @ ConditionOnBean this kind of operation, can realize the Bean's on-demand load, load conditions We use in the use of a third party, @ EnableRedis said open Redis function, @enablees indicates that the Es function is enabled for dynamic Bean loading. There are also a lot of fancy things like @condition ***Copy the code
ConditionalOn ConditionalOn
ConditionOnBean source code parsing
Directly on the dry goods, into the core source code, the entry is still AutoConfigurationImportSelector. SelectImports (), after the end of this method, we actually get to the current spring. The Bean in the factories ConditionOnBean (exclude = *.class); / / ConditionOnBean (exclude = *.class) Look at the source ConfigurationClassParserCopy the code
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false); Method in finding processConfigurationClass (candidate. AsConfigClass (configClass));Copy the code
And as WE go on, Until this. ConditionEvaluator. ShouldSkip (configClass for getMetadata (), Configurationphase. PARSE_CONFIGURATION) core source code, for @condition ** adaptationCopy the code
The Debug directly to the core matching source SpringBootCondition. Matches ()Copy the code
Interested students can continue to down, this method is @ Condition * * judgment logic public final Boolean matches (ConditionContext context. AnnotatedTypeMetadata metadata) { String classOrMethodName = getClassOrMethodName(metadata); try { ConditionOutcome outcome = getMatchOutcome(context, metadata); logOutcome(classOrMethodName, outcome); recordEvaluation(context, classOrMethodName, outcome); return outcome.isMatch(); }Copy the code
Before, of course, the author also read an article, describe the @ Condition * *, matching the timing of the validation is AutoConfigurationImportSelector. SelectImports () the entry method, also is the place of my circle; However, when I debug, the conclusion is really inconsistent, which is still doubtful, if you have good ideas, please do not hesitate to commentCopy the code
The article summarizes
When we talked about SpringBoot autowiring in the last article, we were left with two questions; One is SpringBoot how to eliminate unwanted Bean, directly using the @ SpringBootApplication (exclude = BaseInterpreterAutoConfiguration. Class) this configuration interface; ConditionalOn/ConditionalOn/ConditionalOn/ConditionalOn/ConditionalOn/ConditionalOn/ConditionalOn/ConditionalOn/ConditionalOn/ConditionalOn With the Spring. factories file and the @conditionalon ** annotation, the principle of SpringBoot auto-assembly is clearCopy the code
It should be a future
The principle of automatic assembly is understood, and then you can do hands-on combat, for example, you can write a middleware starter by yourself, Springboot-mymq-starter, for example, provides data services, out of the box, muggle-style API services for companies Just tell him, you just need to import my POM package, add @enableMQ annotation to mq, send message, retry message, dead letter, I have done it for you, support QPS 2W+, feel free to use it, don't re-create the wheel of course this will be the next article SpringBoot automatic assembly principle analysis (chapter 3) teach you handwriting middleware Springboot-Demo-StaterCopy the code