catalogue
2.1 three types of import configuration
2.2 source code interpretation
3.2 import Configuration class with @ Configuration
3.3 classes imported through ImportSelector
3.4 classes imported through ImportBeanDefinitionRegistrar
1. Introduction
The @ Import annotation will appear on the source code or many configuration classes. Its function is the same as that in Spring XML@ The Import annotation is used to Import configuration classes or some classes that need to be preloaded
2. Source code analysis
2.1 three types of import configuration
@Import supports three methods
1. Configuration class with @ configuration (only configuration classes can be imported before version 4.2, and ordinary classes can also be imported after version 4.2)
2. Implementation of importselector
3. Implementation of importbeandefinitionregistrar
2.2 source code interpretation
/** * Indicates one or more {@link Configuration @Configuration} classes to import. * *The function is similar to < import / > in XML. You can import @ Configuration configuration classes, ImportSelector * ImportBeanDefinitionRegistrar After version 4.2, you can import ordinary classes (similar to AnnotationConfigApplicationContext#register * ) * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML. * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}). * * It can be declared at the class level or as a meta annotation * <p>May be declared at the class level or as a meta-annotation. * If you need to import XML or other types of files, use @ ImportResource annotation * <p>If XML or other non-{@code @Configuration} bean definition resources need to be * imported, use the {@link ImportResource @ImportResource} annotation instead. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} * or regular component classes to import. */ Class<?>[] value(); }
3. Test example
3.1 import general class
- Create a new TestA
public class TestA { public void fun(String str) { System.out.println(str); } public void printName() { System.out.println("Class name:" + Thread.currentThread().getStackTrace()[1].getClassName()); } }
- Create a new ImportConfig and add @ Configuration on the class to enable Spring to scan the class and directly Import the TestA class through @ Import
@Import({TestA.class}) @Configuration public class ImportConfig { }
3. Test results
TestA is a generic class. It can now be annotated and invoked by @Autowired, which directly indicates that it has been injected and managed by Spring, and ordinary classes need to be instantiated first.
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = ApplicationMain.class) public class ImportAnnotionTest { @Autowired TestA testA; @Test public void TestA() { testA.printName(); } }
Print:
Class name: com.test.importdemo.TestA
3.2 import Configuration class with @ Configuration
- New TestB
@Configuration public class TestB { public void fun(String str) { System.out.println(str); } public void printName() { System.out.println("Class name:" + Thread.currentThread().getStackTrace()[1].getClassName()); } }
- In importconfig Class directly introduces TestB
@Import({TestA.class,TestB.class}) @Configuration public class ImportConfig { }
3. Test results
TestB.class has @ Configuration annotation on it, so it will be configured with spring to scan and instance, @ import introduces the Configuration file with @ Configuration, which needs to be instantiated before relevant operations
@Autowired TestB testB; @Test public void TestB(){ testB.printName(); }
Print:
ImportAnnotionTest in 8.149 seconds (JVM running for 10.104) Class name: com.test.importdemo.TestB 2019-01-31 14:12:05.737 INFO 23760 --- [ Thread-2]
3.3 classes imported through ImportSelector
- New testc class
public class TestC { public void fun(String str) { System.out.println(str); } public void printName() { System.out.println("Class name:" + Thread.currentThread().getStackTrace()[1].getClassName()); } }
2. Create a selfimportselector Class implements the ImportSelector interface and injects testc class
//TODO ImportSelector related explanations
public class SelfImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.test.importdemo.TestC"}; } }
3. Selfimportselector. Is introduced into importconfig class
@Import({TestA.class,TestB.class,SelfImportSelector.class}) @Configuration public class ImportConfig { }
4. Test results
@Autowired TestC testC; @Test public void TestC() { testC.printName(); }
Print:
ImportAnnotionTest in 7.23 seconds (JVM running for 9.065) Class name: com.test.importdemo.TestC 2019-01-31 14:23:15.330 INFO 1196 --- [
3.4 classes imported through ImportBeanDefinitionRegistrar
1. Create a new testd class
public class TestD { public void fun(String str) { System.out.println(str); } public void printName() { System.out.println("Class name:" + Thread.currentThread().getStackTrace()[1].getClassName()); } }
2. Create a selfimportbeandefinitionregistrar Class, implement the interface ImportBeanDefinitionRegistrar, and inject testd class
public class SelfImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition root = new RootBeanDefinition(TestD.class); registry.registerBeanDefinition("testD", root); } }
3. Add selfimportbeandefinitionregistrar to importconfig class class
@Import({TestA.class,TestB.class,SelfImportSelector.class, SelfImportBeanDefinitionRegistrar.class}) @Configuration public class ImportConfig { }
4. Test results
@Autowired TestD testD; @Test public void TestD() { testD.printName(); }
Print:
ImportAnnotionTest in 7.817 seconds (JVM running for 9.874) Class name: com.test.importdemo.TestD 2019-01-31 14:30:05.781 INFO 23476 --- [
Through the above methods, you can successfully inject Spring
4. Detailed process analysis
Here we mainly look at configurationclassparser Java inside
Doprocessconfigurationclass (configurationclass, configclass, sourceclass) this method Locate the 302 lines of code of the source code
Why not comb from the beginning? The Spring startup process here is complex. If you comb from the beginning, there are many things involved. Many people will be tired and give up. We will be familiar with each other and summarize them at last
4.1 getImports method
Before analyzing this method, let's take a look at the getImports method, which is to get all the classes in @ import
Here is to get the classes in @ import. The general process is as follows:
1. Define a visited set, which is used as a flag to determine whether it has been judged
2. Here is to get all the annotations above sourceClass and judge one by one. If it is not @ import, then call the corresponding annotations recursively until they are all finished
3. Load the corresponding class name in @ Import annotation in sourceClass, and finally return
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { Set<SourceClass> imports = new LinkedHashSet<>(); Set<SourceClass> visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited); return imports; } // Here is to get all the annotations above sourceClass. If it is not @ import, then call the corresponding annotations recursively until they are all finished private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException { if (visited.add(sourceClass)) { for (SourceClass annotation : sourceClass.getAnnotations()) { String annName = annotation.getMetadata().getClassName(); if (!annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited); } } imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } }
4.2 processImports method
The code logic of processImports method is also very clear. The flow chart is as follows:
The general process is as follows:
Judge whether importCandidates is empty and exit if it is empty
Judge isChainedImportOnStack. If it is true, add error in the problemReporter and exit
Add the current configClass to ImportStack, which inherits ArrayDeque // TODO and implements ImportRegistry// TODO
Traverse the classes that need to be imported obtained from getImports
4.1 if it is an ImportSelector type, first instance an ImportSelector object, and then extend it with Aware (if Aware interface is implemented)
4.1.2 further judge whether it is the DeferredImportSelector type. If so, add it to deferredImportSelectors for final processing. Here you can see the method parse(Set configCandidates), which is called only in the last line. That is, sometimes, if you want to inject finally, you can define it as deferredImportSelectors type
4.1.2 if it is not the DeferredImportSelector type, call the selectImports method to get all the classes to be injected. Then call the processImports method again. Calling the processImports method here actually treats these classes to be injected as ordinary @ Configuration
If it is an ImportBeanDefinitionRegistrar type, an object is first instantiated here, and then added to importBeanDefinitionRegistrars. Later, it will be processed in the loadBeanDefinitionsFromRegistrars method of the ConfigurationClassBeanDefinitionReader class
6. If neither of the above two types is true, it is the ordinary class with @ Configuration
--------
Copyright notice: This is the original article of CSDN blogger "always forging iron", which follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this notice for reprint.
Original link: https://blog.csdn.net/mamamalululu00000000/article/details/86711079