To undertake the previous text springboot sentiment edify [email protected] annotation analysis, this article will be on the basis of the previous article @AutoConfigureAfter
and explain @Conditional
the role of annotation and analysis
[Email protected]
According to the word, it is the meaning of the condition. We can look at the internal source code before the analysis.
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Conditional { /** * All {@link Condition}s that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value();}
It acts on a class, method, and the specified value must be an implementation class of Org.springframework.context.annotation.Condition for the condition to be judged.
The annotations that are expanded on this basis include,,, and @ConditionalBean
@ConditionalOnWebApplication
@ConditionalOnClass
@ConditionalOnMissingBean
so on.
@Conditional annotations are parsed into the portal
Then we would like to know how the annotations are parsed. In fact, in the preceding ConfigurationClassParser
class, the following code is executed before the real Doprocessconfigurationclass () method is executed
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { // 条件判断,不满足则直接返回,不进行后续的解析 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } .... // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
That is, the above ConditionEvaluator#shouldSkip()
method is executed, and the true annotation parsing is performed only if the condition is met @Configuration
.
Conditionevaluator#shouldskip ()
Don't say much nonsense, directly on the source
Metadata is the annotated class element, and a return value of TRUE indicates that the condition does not meet the public boolean shouldskip (@Nullable annotatedtypemetadata metadata, which should be ignored, @Nulla BLE configurationphase phase) {//1. Determine if the class contains @conditional annotations, otherwise return directly if (metadata = = NULL | |!metadata.isannota Ted (Conditional.class.getName ())) {return false; } if (phase = = null) {if (metadata instanceof annotationmetadata && Configur Ationclassutils.isconfigurationcandidate ((annotationmetadata) metadata) {return Shouldskip (metadata, Conf Igurationphase.parse_configuration); } return Shouldskip (metadata, Configurationphase.register_bean); }//2. Gets all the value collections on the class that contain @conditional annotations (which recursively look for annotations) list<condition> conditions = new Arraylist<> ( ); For (string[] conditionclasses:getconditionclasses (metadata)) {for (String conditionclass:conditionclasses ) {Condition Condition = getcondition (CondiTionclass, This.context.getClassLoader ()); Conditions.add (condition); }}//3. Sort by order annotationawareordercomparator.sort (conditions); 4. Condition unified Call to the matches () method in the collection, once return false and the required phase is consistent, the condition does not satisfy for (Condition condition:conditions) { Configurationphase requiredphase = null; if (condition instanceof configurationcondition) {requiredphase = ((configurationcondition) condition). Get Configurationphase (); } if ((Requiredphase = = NULL | | requiredphase = = phase) &&!condition.matches (This.context, metadata)) {return true; }} return false; }
The specific code explanation has been given in accordance with the comments, in fact, it is very simple, the reader can understand a little reading. So additional annotations such as @ConditionalOnMissingBean
readers can read the code analysis on their own, the author does not expand here
[Email protected]
@AutoConfigureBefore
In contrast, the meaning of the representation is that it is automatically injected before or after the class is loaded. First look at its internal source code
@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE })@Documentedpublic @interface AutoConfigureAfter { /** * The auto-configure classes that should have already been applied. * @return the classes */ Class<?>[] value() default {}; /** * The names of the auto-configure classes that should have already been applied. * @return the class names * @since 1.2.2 */ String[] name() default {};}
Used only on classes, internal properties name
indicate Beandefinition's class full path, and internal properties value
indicate beandefinition classes. So how is it parsed, and also based on the previous ConfigurationClassParser#parse()
method, as follows
public void parse(Set<BeanDefinitionHolder> configCandidates) { this.deferredImportSelectors = new LinkedList<>(); // 解析@Configuration注解 .... // 解析DeferredImportSelector接口类,表面上也就是延迟解析的意思 processDeferredImportSelectors(); }
The author only focuses on the processdeferredimportselectors () method, which can be used to detect @AutoConfigureAfter
traces of such annotations.
Configurationclassparser#processdeferredimportselectors ()
Read the source directly
private void Processdeferredimportselectors () {//1. Processimport () method to get the Deferredimportselector interface set, no direct return list<deferredimportselectorholder> deferredimports = this.deferredimportselectors; This.deferredimportselectors = null; if (deferredimports = = null) {return; }//2. Sort Deferredimports.sort (Deferred_import_comparator); 3. Traverse the Deferredimportselector interface collection, get the Group collection class, default to Defaultdeferredimportselectorgroup Map<object, deferredimportselectorgrouping> groupings = new linkedhashmap<> (); Map<annotationmetadata, configurationclass> configurationclasses = new hashmap<> (); for (Deferredimportselectorholder deferredimport:deferredimports) {//notice this ..... class<? Extends group> Group = Deferredimport.getimportselector (). Getimportgroup (); deferredimportselectorgrouping grouping = groupings.computeifabsent ( (group = null? group:deferredimport), Key-New deferredimportselectorgrouping (Creategro Up (group)); Grouping.add (Deferredimport); Configurationclasses.put (Deferredimport.getconfigurationclass (). GetMetaData (), Deferredimport.getconfig Urationclass ()); }//4. Traversing the group collection, the function is to call the Processimport () method for parsing @import for (deferredimportselectorgrouping grouping:groupings.values ()) { Grouping.getimports (). ForEach (entry, {configurationclass ConfigurationClass = Configuration Classes.get (Entry.getmetadata ()); try {processimports (ConfigurationClass, Assourceclass (ConfigurationClass), Assourceclasses (Entry.getimportclassname ()), false); } catch (Beandefinitionstoreexception ex) {throw ex; } catch (Throwable ex) {throw nEW beandefinitionstoreexception ("Failed to process import candidates for configuration class [ "+ Configurationclass.getmetadata (). GetClassName () +"] ", ex); } }); } }
The author and reader here only need to pay attention to deferredImport.getImportSelector().getImportGroup()
this method, here as an AutoConfigurationImportSelector.class
example
Autoconfigurationimportselector
First look at its getimportgroup () method
public Class<? extends Group> getImportGroup() { return AutoConfigurationGroup.class; }
Then observe the AutoConfigurationGroup
selectimports () method of this class
public Iterable<Entry> selectImports() { return sortAutoConfigurations().stream() .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) .collect(Collectors.toList()); }
The key is here, just in the sortautoconfigurations () method, which sorts the imported class classes through the Autoconfigurationsorter class, as to how to sort we keep looking down.
Autoconfigurationsorter
Sorting method Getinpriorityorder (), let's look at the source code
public List<String> getInPriorityOrder(Collection<String> classNames) { AutoConfigurationClasses classes = new AutoConfigurationClasses( this.metadataReaderFactory, this.autoConfigurationMetadata, classNames); List<String> orderedClassNames = new ArrayList<>(classNames); // Initially sort alphabetically.首先根据ASCII来进行排序 Collections.sort(orderedClassNames); // Then sort by order,再根据Order来进行排序 orderedClassNames.sort((o1, o2) -> { int i1 = classes.get(o1).getOrder(); int i2 = classes.get(o2).getOrder(); return Integer.compare(i1, i2); }); // Then respect @AutoConfigureBefore @AutoConfigureAfter orderedClassNames = sortByAnnotation(classes, orderedClassNames); return orderedClassNames; }
It can be concluded that the most critical sort from the sortbyannotation () method, specifically not looked at, is nothing more than according to Before/after, to the importclassname to sort out an ordered set.
Finally go back to the last paragraph of the Configurationclassparser#processdeferredimportselectors () method, which will have an ordered set traversal operation processImports()
Method, If the corresponding class class does not exist, it will be an error, also satisfies the meaning of autoconfigurebefore/autoconfigureafter .
Summary
For @Conditional and @AutoConfigureAfter specific analysis can be seen above, this article is also a supplement to the previous article. Readers are expected to read this article and be sure to read the preceding article to understand the meaning of the code above. At the same time, because these two annotations are conditional, it is very important for the springboot to use the two annotations to match the dependent deployment to build different conditions. WebMvcAutoConfiguration
End With class
@Configuration@ConditionalOnWebApplication(type = Type.SERVLET)@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class })public class WebMvcAutoConfiguration {}
Springboot sentiment edify [email protected] and @autoconfigureafter annotation parsing