[Analysis of Spring’s underlying core architecture concepts]

Article directory

  • 1. BeanDefinition
  • 2. BeanDefinitionReader
    • 2.1. AnnotatedBeanDefinitionReader
    • 2.2.XmlBeanDefinitionReader
  • 5. ClassPathBeanDefinitionScanner
  • 6. BeanFactory
  • 7. ApplicationContext
    • 7.1. AnnotationConfigApplicationContext
    • 7.2. ClassPathXmlApplicationContext
  • 8. Type conversion
    • 8.1.PropertyEditor
    • 8.2.ConversionService
    • 8.3. TypeConverter
  • 9. FactoryBean
  • 10. ExcludeFilter and IncludeFilter
  • 11. MetadataReader, ClassMetadata, AnnotationMetadata

Prepare the test object User in advance:

public class User {<!-- -->

    public User(int flag, String uuid){<!-- -->
        System.out.println(flag + "----------" + uuid);
    }

    public User(){<!-- -->
        System.out.println("User no parameter construction");
    }

}

1. BeanDefinition

BeanDefinition represents the definition of a Bean, which can record the characteristics of the Bean, for example:

  1. Bean type: beanClass attribute
  2. Bean scope: Scope attribute
  3. Whether the bean is lazy loaded: lazyinit attribute

In Spring, the main ways to define a bean are: using bean tags in XML files to define its bean properties. In addition, you can use @Bean annotations and @Component annotations to define annotations. Of course, you can also use programmatic definitions. Bean, the following code:

//Spring core concept test class
public class TestSpringDemo {<!-- -->
    public static void main(String[] args) {<!-- -->
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);


        //Define a BeanDefinition object
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        //Add object information to the BeanDefinition object
        beanDefinition.setBeanClass(User.class);
        beanDefinition.setScope("prototype");
        //Add the BeanDefinition object to the Spring container
        context.registerBeanDefinition("user",beanDefinition);

        System.out.println("The user object created by Spring BeanDefinition is: " + context.getBean("user"));

        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }
}

2. BeanDefinitionReader

It represents the BeanDefinition reader. There are two main reading methods:

2.1, AnnotatedBeanDefinitionReader

This component can directly convert certain types into BeanDefinition, and will parse the annotations on the class. The test code is as follows:

//Spring core concept test class
public class TestSpringDemo {<!-- -->
    public static void main(String[] args) {<!-- -->
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        //Declare a BeanDefinition reader and pass in the Spring container in this reader
        AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(context);

        annotatedBeanDefinitionReader.register(User.class);
        System.out.println("BeanDefinition reader AnnotatedBeanDefinitionReader creates a bean as: " + context.getBean("user"));

        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }
}

2.2, XmlBeanDefinitionReader

This interface mainly performs BeanDefinition conversion by parsing the Bean tags in the XML file. The code is as follows:

XML configuration file information:

//Spring core concept test class
public class TestSpringDemo {<!-- -->
    public static void main(String[] args) {<!-- -->
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        //Declare a BeanDefinition reader and pass in the Spring container in this reader
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);

        int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");
        System.out.println("Number of bean tags scanned: " + i);
        System.out.println("BeanDefinition readerXmlBeanDefinitionReader creates a bean as: " + context.getBean("user"));

        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }
}

5. ClassPathBeanDefinitionScanner

It is a BeanDefinition resource scanner. Its function is similar to that of BeanDefinitionReader. The only difference is that it scans class resources in a specified path and parses the scanned classes to see if they contain relevant annotation information. For example: if the class contains @Component annotation, the class will be put into the Spring container as a BeanDefinition object. The code is as follows:

@Component
public class UserService {<!-- -->
    public void test(){<!-- -->
        System.out.println("test--->Spring creates object");
    }
}
//Spring core concept test class
public class TestSpringDemo {<!-- -->
    public static void main(String[] args) {<!-- -->
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.refresh();
        //Declare a BeanDefinition reader and pass in the Spring container in this reader
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);

        int scan = scanner.scan("com.practice.service");
        System.out.println("The number of bean resources scanned under the specified path: " + scan);
        System.out.println("ClassPathBeanDefinitionScanner scanner creates bean: " + context.getBean("userService"));

    }
}

6. BeanFactory

BeanFactory will be responsible for creating beans and providing APIs for obtaining beans.

7. ApplicationContext

ApplicationContext is a type of BeanFactory, which is defined in Spring’s source code as:

ListableBeanFactory and HierarchicalBeanFactory in the figure both inherit from BeanFactory, so ApplicationContext also has the features and functions of BeanFactory. However, ApplicationContext also has additional functions, such as supporting internationalization, supporting obtaining environment information, supporting event publishing and related resource loading. It has two implementation classes, as introduced below

7.1, AnnotationConfigApplicationContext

Its source code is as follows:

AnnotationConfigApplicationContext inherits GenericApplicationContext, implements the AnnotationConfigRegistry interface, and has all the functions of its parent class, parent interface, and even higher-level interfaces inherited from the parent interface.

7.2, ClassPathXmlApplicationContext


It inherits AbstractApplicationContext, but compared to AnnotationConfigApplicationContext, its functions are not as powerful as AnnotationConfigApplicationContext. For example, BeanDefinition cannot be registered.

8. Type conversion

In Spring, there may be some type conversion problems. Spring provides some technologies to facilitate type conversion operations. In the Spring source code, there are many type conversion operations. Type conversion mainly has the following interfaces.

8.1, PropertyEditor

This is the type conversion tool class that comes with JDK. The specific usage is as follows:

public class StringToUserPropertyEditor extends PropertyEditorSupport implements PropertyEditor {<!-- -->
    @Override
    public void setAsText(String text) throws IllegalArgumentException {<!-- -->
        User user = new User();
        user.setName(text);
        this.setValue(user);
    }
}
//Spring core concept test class
public class TestSpringDemo {<!-- -->
    public static void main(String[] args) {<!-- -->

        StringToUserPropertyEditor propertyEditor = new StringToUserPropertyEditor();
        propertyEditor.setAsText("1");
        User value = (User) propertyEditor.getValue();
        System.out.println(value);
    }
}

8.2, ConversionService

The type conversion service provided in Spring is more powerful than PropertyEditor. Its code is as follows:

public class StringToUserConverter implements ConditionalGenericConverter {<!-- -->
    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {<!-- -->
        return sourceType.getType().equals(String.class) & amp; & amp;
                targetType.getType().equals(User.class);
    }
    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {<!-- -->
        return Collections.singleton(new ConvertiblePair(String.class, User.class));
    }
    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor
            targetType) {<!-- -->
        User user = new User();
        user.setName((String)source);
        return user;
    }
}
//Spring core concept test class
public class TestSpringDemo {<!-- -->
    public static void main(String[] args) {<!-- -->

        DefaultConversionService conversionService = new DefaultConversionService();
        conversionService.addConverter(new StringToUserConverter());
        User value = conversionService.convert("1", User.class);
        System.out.println(value);

    }
}

8.3, TypeConverter

It integrates the functions of PropertyEditor and ConversionService and is used internally by Spring, as follows:

//Spring core concept test class
public class TestSpringDemo {<!-- -->
    public static void main(String[] args) {<!-- -->

        SimpleTypeConverter typeConverter = new SimpleTypeConverter();
        typeConverter.registerCustomEditor(User.class, new StringToUserPropertyEditor());
        User value = typeConverter.convertIfNecessary("1", User.class);
        System.out.println(value);

    }
}

9. FactoryBean

We want to create a Bean completely by us, which can be done through FactoryBean, as follows:

public class factoryBeanTest implements FactoryBean {<!-- -->
    @Override
    public Object getObject() throws Exception {<!-- -->
        UserService userService = new UserService();
        return userService;
    }
    @Override
    public Class<?> getObjectType() {<!-- -->
        return UserService.class;
    }

}

Through the above code, we created a UserService object ourselves, and it will become a Bean. However, the UserService Bean created in this way will only be initialized and will not go through other Spring life cycle steps, such as dependency injection.

10. ExcludeFilter and IncludeFilter

These two Filters are used for filtering during Spring scanning. ExcludeFilter means to exclude the filter, and IncludeFilter means to include the filter, as follows:



FilterType is divided into:
1. ANNOTATION: Indicates whether an annotation is included
2. ASSIGNABLE_TYPE: Indicates whether it is a certain class
3. ASPECTJ: Indicates whether it conforms to an Aspectj expression
4. REGEX: Indicates whether it matches a certain regular expression
5. CUSTOM: Customization

11. MetadataReader, ClassMetadata, AnnotationMetadata

In Spring, it is necessary to parse class information, such as class name, methods in the class, and annotations on the class. These can be called metadata of the class, so Spring abstracts the metadata of the class and provides some tools
MetadataReader represents the metadata reader of the class, and the default implementation class is SimpleMetadataReader. The code is as follows:

package com.practice.service;

import org.springframework.stereotype.Component;

@Component
public class UserService {<!-- -->
    public void test(){<!-- -->
        System.out.println("test--->Spring creates object");
    }
}

//Spring core concept test class
public class TestSpringDemo {<!-- -->
    public static void main(String[] args) throws IOException {<!-- -->

        SimpleMetadataReaderFactory simpleMetadataReaderFactory = new
                SimpleMetadataReaderFactory();
        //Construct a MetadataReader
        MetadataReader metadataReader =
                simpleMetadataReaderFactory.getMetadataReader("com.practice.service.UserService");
        // Get a ClassMetadata and get the class name
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        System.out.println(classMetadata.getClassName());
        // Get an AnnotationMetadata and get the annotation information on the class
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        for (String annotationType : annotationMetadata.getAnnotationTypes()) {<!-- -->
            System.out.println(annotationType);
        }
    }
}


Note: SimpleMetadataReader uses ASM technology when parsing classes. Because Spring needs to scan when it starts, if the specified package path is relatively broad, then there will be a lot of scanned classes. If all these classes are loaded into the JVM when Spring starts, this is not good, so use ASM technology.