It doesn't matter if you don't understand the Spring source code. Today, let's understand the underlying logic from the perspective of architecture design.

foreword

Why do you need Spring? What is Spring?

For such a problem, most people are in a hazy state, saying it, but not completely saying it. Today we will try to unravel the mystery of Spring from the perspective of architecture design.

This article is introduced in a simple-to-deep way. You don't have to panic. I can guarantee that as long as you know programming, you can understand it.

This article is based on Spring 5.2.8 and takes about 20 minutes to read

case

Let's look at a case first: there is a guy who has a Geely car, and usually drives a Geely car to work

Code:

public class GeelyCar {

    public void run(){
        System.out.println("geely running");
    }
}
复制代码
public class Boy {
		// 依赖GeelyCar
    private final GeelyCar geelyCar = new GeelyCar();

    public void drive(){
        geelyCar.run();
    }
}
复制代码

One day, the guy made money, bought another Hongqi, and wanted to drive a new car.

Simple, replace the dependency with HongQiCar

Code:

public class HongQiCar {

    public void run(){
        System.out.println("hongqi running");
    }
}
复制代码
public class Boy {
    // 修改依赖为HongQiCar
    private final HongQiCar hongQiCar = new HongQiCar();

    public void drive(){
        hongQiCar.run();
    }
}
复制代码

I am tired of driving a new car and want to change back to the old car. At this time, there will be a problem: the code has been changed over and over again.

Clearly, this case violates our Dependency Inversion Principle (DIP) : programs should not depend on implementations, but on abstractions

Optimized

Now we optimize the code as follows:

Boy relies on the Car interface, while the previous GeelyCar and HongQiCar are implemented for the Car interface

Code:

Define the Car interface

public interface Car {

    void run();
}
复制代码

Change the previous GeelyCar and HongQiCar to the implementation class of Car

public class GeelyCar implements Car {

    @Override
    public void run(){
        System.out.println("geely running");
    }
}
复制代码

Same as HongQiCar

Person relies on the Car interface at this time

public class Boy {
		// 依赖于接口
    private final Car car;
		
    public Person(Car car){
        this.car = car;
    }

    public void drive(){
        car.run();
    }
}
复制代码

At this time, the guy wants to change the car to drive, just pass in any parameters, and the code will no longer change.

limitation

The above case does not seem to have any problems after the transformation, but there are still certain limitations. If a new scene is added at this time:

One day, the guy couldn't drive after drinking, so he needed to find a chauffeur. The chauffeur doesn't care which guy he drives, nor what kind of car he drives. The guy suddenly becomes an abstraction. At this time, the code needs to be changed again. The code that the chauffeur depends on the guy may look like this:

private final Boy boy = new YoungBoy(new HongQiCar());
复制代码

As the complexity of the system increases, such problems will become more and more difficult to maintain, so how should we solve this problem?

think

First of all, we can be sure: there is no problem using the Dependency Inversion Principle , it solves our problem to a certain extent.

We feel that the problem is in the process of passing in parameters: we pass in whatever the program needs. Once there are multiple dependent class relationships in the system, the incoming parameters will become extremely complex.

Maybe we can reverse the idea : what we have, what the program uses!

When we only implement HongQiCar and YoungBoy, the driver uses YoungBoy driving HongQiCar!

When we only implement GeelyCar and OldBoy, the chauffeur will naturally change to OldBoy driving GeelyCar!

And how to reverse is a big problem that Spring solves.

Introduction to Spring

Spring is a one-stop lightweight and heavyweight development framework, which aims to solve the complexity of enterprise application development. It provides comprehensive infrastructure support for developing Java applications, so that Java developers no longer need to care about class and Dependencies between classes, you can focus on developing applications (crud).

Spring provides rich functionality for enterprise-level development, and the bottom layer of these functions relies on its two core features: Dependency Injection (DI) and Aspect Oriented Programming (AOP).

The core concepts of Spring

IoC container

The full name of IoC is Inversion of Control, which means inversion of control. IoC is also known as Dependency Injection (DI), which is a process of injecting objects through dependencies: objects are only passed through constructors, factory methods, or when the object is instantiated. properties set on it to define its dependencies (i.e. other objects with which they are combined), and the container then injects these needed dependencies when the bean is created. This process is fundamentally the inverse of the bean itself controlling the instantiation or location of its dependencies by using either a direct build class or a mechanism such as the service location pattern (hence the name Inversion of Control).

The Dependency Inversion Principle is the design principle of IoC, and Dependency Injection is the implementation of IoC.

container

In Spring, we can use XML, Java annotations or Java code to write configuration information, and through configuration information, get instructions about instantiating, configuring and assembling objects, and instantiating, configuring and assembling application objects are called container.

In general, we only need to add a few annotations, so that after the container is created and initialized, we can get a configurable, executable system or application.

Bean

In Spring, the objects that are instantiated by the Spring IOC container -> assembly management -> constitute the skeleton of the program are called Beans. Beans are one of many objects in an application.

The above three points are linked together: Spring is an IoC container that places beans, and handles dependencies between beans by means of dependency injection.

AOP

Aspect-oriented programming is a functional supplement to object-oriented programming (OOP). The main object of OOP is class, while AOP is aspect. It plays a very important role in processing logs, security management, and transaction management. AOP is an important component of the Spring framework. Although the IOC container does not depend on AOP, AOP provides very powerful functions to supplement IOC.

AOP allows us to enhance our business functions without modifying the original code: cutting a piece of function into a position we specify, such as printing logs between method call chains.

Advantages of Spring

1. Spring simplifies enterprise-level Java development through DI and AOP

2. Spring's low-intrusive design makes code pollution extremely low

3. Spring's IoC container reduces the complexity between business objects and decouples components from each other

4. Spring's AOP support allows centralized processing of some common tasks such as security, transactions, logging, etc., thereby improving better reusability

5. The high openness of Spring does not force the application to completely depend on Spring. Developers can freely choose part or all of the Spring framework.

6. Spring's high extensibility allows developers to easily integrate their own frameworks on Spring

7. Spring's ecology is extremely complete, integrating various excellent frameworks, allowing developers to easily use them

We can live without Java, but we can't live without Spring~

Retrofit the case with Spring

We now know what Spring is, and now try to use Spring to transform the case

The original structure has not changed, just add @Component annotation on GeelyCar or HongQiCar, and Boy add @Autowired annotation when using

Code style:

@Component
public class GeelyCar implements Car {

	@Override
	public void run() {
		System.out.println("geely car running");
	}
}
复制代码

Same as HongQiCar

In Spring, when a class is marked with the @Component annotation, it means that this is a Bean that can be managed by the IoC container

@Component
public class Boy {
	
	// 使用Autowired注解表示car需要进行依赖注入
	@Autowired
	private Car car;

	public void driver(){
		car.run();
	}
}
复制代码

What we said before: what we implement, what the program uses, here is equivalent to which class we marked the Component annotation, which class will be a Bean, and Spring will use it to inject into the Boy's attribute Car

So when we annotate GeelyCar with Component, Boy's car is GeelyCar, and when we annotate HongQiCar with Component, Boy's car is HongQiCar

Of course, we can't mark Component annotations on GeelyCar and HongQiCar at the same time, because then Spring doesn't know which Car to use for injection - Spring also has difficulty in choosing (or can't one boy drive two cars?)

Using Spring Bootstrap

// 告诉Spring从哪个包下扫描Bean,不写就是当前包路径@ComponentScan(basePackages = "com.my.spring.test.demo")public class Main {	public static void main(String[] args) {		// 将Main(配置信息)传入到ApplicationContext(IoC容器)中		ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);		// 从(IoC容器)中获取到我们的boy		Boy boy = (Boy) context.getBean("boy");		// 开车		boy.driver();	}}
复制代码

Here you can interpret the knowledge we just introduced about Spring.

Passing the Main class with the ComponentScan annotation (configuration information) to the AnnotationConfigApplicationContext (IoC container) for initialization is equivalent to: the IoC container instantiates, manages and assembles beans by obtaining the configuration information.

How to perform dependency injection is done inside the IoC container, which is also the focus of this article.

think

We have a complete understanding of the basic functions of Spring through a transformation case, and have a concrete experience of the previous concepts, but we still do not know how the internal action of Spring's dependency injection is completed. More to know why, combined with our existing knowledge and understanding of Spring, let’s make bold guesses (this is a very important ability)

In fact, guessing means: if we let us implement it ourselves, how would we implement this process?

First, we need to be clear about what we need to do: scan the classes under the specified package, instantiate them, and combine them according to dependencies.

Step breakdown:

Scan the class under the specified package -> if this class identifies the Component annotation (a Bean) -> save the information of this class

Instantiate -> traverse the stored class information -> instantiate these classes through reflection

According to the combination of dependencies -> parse the class information -> determine whether there are fields in the class that need to be injected -> inject the fields

Scheme realization

We now have a solution that looks like the same thing, let's try to implement it now

define annotations

First we need to define the annotations we need to use: ComponentScan, Component, Autowired

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {

    String basePackages() default "";
}
复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

    String value() default "";
}
复制代码
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface Autowired {}
复制代码

Scan the classes under the specified package

Scanning all classes under a specified package sounds like a moment of confusion, but it is actually equivalent to another problem: how to traverse the file directory?

So what should be used to store class information? Let's take a look at the getBean method in the above example. Is it like getting the value by key in Map? There are many implementations in Map, but there is only one thread-safe, that is ConcurrentHashMap (don't tell me HashTable)

Define a map to store class information

private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>(16);
复制代码

For the specific process, the code implementation is also attached below:

Code implementation, you can watch it in conjunction with the flowchart:

Scan class information

private void scan(Class<?> configClass) {
  // 解析配置类,获取到扫描包路径
  String basePackages = this.getBasePackages(configClass);
  // 使用扫描包路径进行文件遍历操作
  this.doScan(basePackages);
}
复制代码
private String getBasePackages(Class<?> configClass) {
  // 从ComponentScan注解中获取扫描包路径
  ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
  return componentScan.basePackages();
}
复制代码
private void doScan(String basePackages) {
  // 获取资源信息
  URI resource = this.getResource(basePackages);

  File dir = new File(resource.getPath());
  for (File file : dir.listFiles()) {
    if (file.isDirectory()) {
      // 递归扫描
      doScan(basePackages + "." + file.getName());
    }
    else {
      // com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy
      String className = basePackages + "." + file.getName().replace(".class", "");
      // 将class存放到classMap中
      this.registerClass(className);
    }
  }
}
复制代码
private void registerClass(String className){
  try {
    // 加载类信息
    Class<?> clazz = classLoader.loadClass(className);
    // 判断是否标识Component注解
    if(clazz.isAnnotationPresent(Component.class)){
      // 生成beanName com.my.spring.example.Boy -> boy
      String beanName = this.generateBeanName(clazz);
      // car: com.my.spring.example.Car
      classMap.put(beanName, clazz);
    }
  } catch (ClassNotFoundException ignore) {}
}
复制代码

instantiate

Now that all suitable classes have been parsed, the next step is the instantiation process

Define the Map that stores the Bean

private final Map<String, Object> beanMap = new ConcurrentHashMap<>(16);
复制代码

The specific process, the code implementation is also given below:

Code implementation, you can watch it in conjunction with the flowchart:

Traverse classMap to instantiate beans

public void instantiateBean() {
  for (String beanName : classMap.keySet()) {
    getBean(beanName);
  }
}
复制代码
public Object getBean(String beanName){
  // 先从缓存中获取
  Object bean = beanMap.get(beanName);
  if(bean != null){
    return bean;
  }
  return this.createBean(beanName);
}
复制代码
private Object createBean(String beanName){
  Class<?> clazz = classMap.get(beanName);
  try {
    // 创建bean
    Object bean = this.doCreateBean(clazz);
    // 将bean存到容器中
    beanMap.put(beanName, bean);
    return bean;
  } catch (IllegalAccessException e) {
    throw new RuntimeException(e);
  }
}
复制代码
private Object doCreateBean(Class<?> clazz) throws IllegalAccessException {
  // 实例化bean
  Object bean = this.newInstance(clazz);
  // 填充字段,将字段设值
  this.populateBean(bean, clazz);
  return bean;
}
复制代码
private Object newInstance(Class<?> clazz){  try {    // 这里只支持默认构造器    return clazz.getDeclaredConstructor().newInstance();  } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {    throw new RuntimeException(e);  }}
复制代码
private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {  // 解析class信息,判断类中是否有需要进行依赖注入的字段  final Field[] fields = clazz.getDeclaredFields();  for (Field field : fields) {    Autowired autowired = field.getAnnotation(Autowired.class);    if(autowired != null){      // 获取bean      Object value = this.resolveBean(field.getType());      field.setAccessible(true);      field.set(bean, value);    }  }}
复制代码
private Object resolveBean(Class<?> clazz){
  // 先判断clazz是否为一个接口,是则判断classMap中是否存在子类
  if(clazz.isInterface()){
    // 暂时只支持classMap只有一个子类的情况
    for (Map.Entry<String, Class<?>> entry : classMap.entrySet()) {
      if (clazz.isAssignableFrom(entry.getValue())) {
        return getBean(entry.getValue());
      }
    }
    throw new RuntimeException("找不到可以进行依赖注入的bean");
  }else {
    return getBean(clazz);
  }
}
复制代码
public Object getBean(Class<?> clazz){
  // 生成bean的名称
  String beanName = this.generateBeanName(clazz);
  // 此处对应最开始的getBean方法
  return this.getBean(beanName);
}
复制代码

combination

The two core methods have been written. Next, combine them. I implement them in a custom ApplicationContext class. The construction method is as follows:

public ApplicationContext(Class<?> configClass) {
  // 1.扫描配置信息中指定包下的类
  this.scan(configClass);
  // 2.实例化扫描到的类
  this.instantiateBean();
}
复制代码

UML class diagram:

test

The code structure is the same as the case, here we show whether our own Spring can run normally

It works fine, the Chinese don't lie to the Chinese

The source code will be given at the end of the article

review

Now, we have implemented according to the envisaged scheme, and the operation has achieved the expected effect. But if you study it carefully, combined with our usual use of Spring, you will find many problems with this code:

1. Constructor injection cannot be supported, and of course method injection is not supported, which is a functional lack.

2. The problem of loading class information, we use the method of classLoader.loadClass when loading the class. Although this avoids the initialization of the class (don't use the method of Class.forName), it is inevitable to use the class meta information. Loaded into metaspace, which wastes our memory when we scan packages for unwanted classes.

3. The circular dependency between beans cannot be resolved. For example, an object A depends on object B, and object B depends on object A. At this time, if we look at the code logic again, we will find that it will fall into an infinite loop.

4. The scalability is very poor. We write all the functions in one class. When we want to improve the functions (such as the above 3 problems), we need to modify this class frequently, and this class will become more and more bloated. , not to mention iterating on new features, maintenance will be a headache.

Optimization

The first three problems are similar to functional problems, and the functions can be changed.

What we need to focus on is the fourth question. If a framework wants to become excellent, its iterative ability must be good, so that the functions can become rich. There are many factors that affect the iterative ability, one of which is its Extensibility.

So how should we improve the scalability of our solution? The six design principles give us a good guide.

In the scheme, ApplicationContext does a lot of things, which can be mainly divided into two parts

1. Scan the classes under the specified package

2. Instantiate Bean

With the idea of ​​single responsibility principle: a class does only one thing, a method only does one thing.

We use a processor to process the scanning of the classes under the specified package , because the scanning configuration comes from the configuration class, then we call it the configuration class processor: ConfigurationCalssProcessor

The same is true for instantiating beans. Instantiating beans is divided into two things: instantiation and dependency injection.

Instantiating a bean is equivalent to a process of producing a bean. We use a factory class to handle this matter, which is called: BeanFactory. Since it is producing a bean, it needs raw materials (Class), so we put classMap and beanMap is defined here

The process of dependency injection is actually processing Autowired annotations, so it is called: AutowiredAnnotationBeanProcessor

We still know that in Spring, there is not only this way of use, but also xml, mvc, SpringBoot, so we abstract the ApplicationContext and only implement the main process. The original annotation method is implemented by AnnotationApplicationContext.

With the help of the Dependency Inversion Principle: Programs should depend on abstractions

In the future, class information can not only come from class information, but also from configuration files, so we abstract ConfigurationCalssProcessor

The way of dependency injection does not necessarily have to be marked with Autowired annotations, but can also be marked with other annotations, such as Resource, so we abstract AutowiredAnnotationBeanProcessor

There can also be many types of beans, which can be singleton, multi-instance, or a factory bean, so we abstract BeanFactory

Now, we have optimized our solution with the help of two design principles, which can be described as "reborn" compared to before.

Design of Spring

In the previous step, we implemented our own solution and optimized the scalability based on some assumptions. Now, let's take a look at the actual design of Spring.

So, what are the "roles" in Spring?

1. Bean: Spring as an IoC container, the most important thing is of course Bean.

2. BeanFactory: A factory that produces and manages beans

3. BeanDefinition: The definition of Bean, which is the Class in our scheme, Spring encapsulates it

4. BeanDefinitionRegistry: Similar to the relationship between Bean and BeanFactory, BeanDefinitionRegistry is used to manage BeanDefinition

5. BeanDefinitionRegistryPostProcessor: A processor for parsing configuration classes, similar to the ClassProcessor in our scheme

6. BeanFactoryPostProcessor: BeanDefinitionRegistryPostProcessor parent class, so that we can perform post-processing after parsing the configuration class

7. BeanPostProcessor: Bean's post-processor, which is used to perform some processing in the process of bean production, such as dependency injection, similar to our AutowiredAnnotationBeanProcessor

8. ApplicationContext: If the above roles are all workers who produce beans in the factory, then ApplicationContext is the facade of our Spring. ApplicationContext and BeanFactory are in a combined relationship, so it completely extends the function of BeanFactory and builds on its foundation. Added more enterprise-specific features, such as the well-known ApplicationListener (event listener)

The above mentioned similarities actually put the cart before the horse, because in fact, the implementation in our solution should be similar to the implementation in Spring. This is just for everyone to understand better.

After we have experienced the design and optimization of our own solutions, these roles are actually very easy to understand

Next, let's take a detailed look at one by one

BeanFactory

BeanFactory is a top-level interface in Spring, which defines the way to obtain beans. There is another interface in Spring called SingletonBeanRegistry, which defines the way to operate singleton beans. Here I will introduce these two together. Because they are roughly the same, the annotation of SingletonBeanRegistry also writes that it can be implemented together with the BeanFactory interface to facilitate unified management.

BeanFactory

1. ListableBeanFactory: Interface, which defines methods related to obtaining the Bean/BeanDefinition list, such as getBeansOfType(Class type)

2. AutowireCapableBeanFactory: Interface, which defines methods related to Bean life cycle, such as bean creation, dependency injection, initialization

3. AbstractBeanFactory: Abstract class, which basically implements all methods related to Bean operations, and defines abstract methods related to the Bean life cycle

4. AbstractAutowireCapableBeanFactory: Abstract class, which inherits AbstractBeanFactory and implements the content related to the Bean life cycle. Although it is an abstract class, it has no abstract methods.

5. DefaultListableBeanFactory: inherits and implements all the above classes and interfaces. It is the bottom BeanFactory in Spring, and implements the ListableBeanFactory interface itself.

6, ApplicationContext: is also an interface, we will have a special introduction to it below

SingletonBeanRegistry

1. DefaultSingletonBeanRegistry: Defines the cache pool of beans, similar to our BeanMap, and implements operations related to singletons, such as getSingleton (the three-level cache frequently asked in interviews is here)

2. FactoryBeanRegistrySupport: Provides support for FactoryBean, such as obtaining beans from FactoryBean

BeanDefinition

BeanDefinition is actually an interface (unexpectedly), which defines many operation methods related to class information, which is convenient to use directly when producing beans, such as getBeanClassName

Its approximate structure is as follows (here is an example of a RootBeanDefinition subclass):

The various attributes in it must be familiar to everyone.

Likewise, it has many implementation classes:

1. AnnotatedGenericBeanDefinition: When parsing the configuration class and parsing the class brought by the Import annotation, it will be used for encapsulation

2. ScannedGenericBeanDefinition: Encapsulates the class information obtained by scanning the package through @ComponentScan

3. ConfigurationClassBeanDefinition: Encapsulates the class information obtained through the @Bean annotation

4. RootBeanDefinition: The parent class of ConfigurationClassBeanDefinition, which is generally used inside Spring to convert other BeanDefitions into this class

BeanDefinitionRegistry

Defines operations related to BeanDefinition, such as registerBeanDefinition, getBeanDefinition, in BeanFactory, the implementation class is DefaultListableBeanFactory

BeanDefinitionRegistryPostProcessor

Interjection: Speaking of this, have you found that the naming of Spring is extremely standardized? The Spring team once said that the class names in Spring are confirmed after repeated deliberation, which is really worthy of the name, so it is really a very comfortable thing to look at the Spring source code. You can guess what they do by looking at the class name and method name.

This interface only defines one function: processing BeanDefintonRegistry, that is, parsing the Import, Component, ComponentScan and other annotations in the configuration class for corresponding processing, and registering these classes as the corresponding BeanDefinition after processing

Inside Spring, there is only one implementation: ConfigurationClassPostProcessor

BeanFactoryPostProcessor

The so-called post-processor of BeanFactory, which defines the processing logic that can be called after parsing the configuration class, is similar to a slot. If we want to do something after the configuration class is parsed, we can implement this interface.

Inside Spring, only ConfigurationClassPostProcessor implements it: it is used to specifically handle classes annotated with Configuration

There is a small problem in the field here, if you know the following code:

@Configuraiton
public class MyConfiguration{
  @Bean
  public Car car(){
      return new Car(wheel());
  }
  @Bean
  public Wheel wheel(){
      return new Wheel();
  }
}
复制代码

Q: How many times is the Wheel object new when Spring is started? Why?

BeanPostProcessor

Jianghu translation: Bean's post-processor

The post-processor runs through the entire life cycle of the bean. During the creation of the bean, it is called a total of 9 times. As for which 9 times, we will explore it next time. The following describes its implementation class and function.

1. AutowiredAnnotationBeanPostProcessor: used to infer the constructor for instantiation, and to process Autowired and Value annotations

2. CommonAnnotationBeanPostProcessor: Process annotations in the Java specification, such as Resource, PostConstruct

3. ApplicationListenerDetector: Used after the initialization of the bean, adding the bean that implements the ApplicationListener interface to the list of event listeners

4. ApplicationContextAwareProcessor: used to call back Beans that implement the Aware interface

5. ImportAwareBeanPostProcessor: Used to call back the Bean that implements the ImportAware interface

ApplicationContext

As the core of Spring, ApplicationContext isolates the BeanFactory in the facade mode, defines the skeleton of the Spring startup process in the template method mode, and invokes various Processors in the strategy mode... It is really intricate and exquisite!

Its implementation class is as follows:

1. ConfigurableApplicationContext: interface, which defines configuration and life cycle related operations, such as refresh

2. AbstractApplicationContext: An abstract class that implements the refresh method. The refresh method is the core of the Spring core. It can be said that the entire Spring is in the refresh. All subclasses are started through the refresh method. After calling this method, all singleton

3. AnnotationConfigApplicationContext: Use the relevant annotation readers and scanners at startup, register the required processors in the Spring container, and then call the refresh method by the main process.

4. AnnotationConfigWebApplicationContext: Implement the loadBeanDefinitions method, in order to be called in the refresh process to load BeanDefintion

5、ClassPathXmlApplicationContext: 同上

It can be seen from the situation of subclasses that the difference between subclasses is how to load BeanDefinitons. AnnotationConfigApplicationContext is loaded through the configuration class processor (ConfigurationClassPostProcessor), while AnnotationConfigWebApplicationContext and ClassPathXmlApplicationContext implement the loadBeanDefinitions method by themselves, and the other processes are exactly the same

Spring's process

Above, we have clear the main roles and roles in Spring, now we try to combine them to build a Spring startup process

Also take our commonly used AnnotationConfigApplicationContext as an example

The figure only draws a part of the general process in Spring, and we will expand the details in the following chapters.

summary

The so-called beginning of everything is difficult. The original intention of this article is to allow everyone to understand Spring from the shallower to the deeper, to initially establish Spring's cognitive system, understand Spring's internal structure, and to understand Spring's knowledge no longer superficially.

Now that the head has been opened, I believe that the learning of the following content will come naturally.

This article is not only about the architectural design of Spring, but also hopes to become a manual for us to review the overall content of Spring in the future.

Finally, after reading the article, I believe it is easy to answer the following frequently asked interview questions.

1. What is BeanDefinition?

2. What is the relationship between BeanFactory and ApplicationContext?

3. Classification and role of post-processors?

4. What is the main process of Spring?

If you feel that you can't answer it well, please read the article again, or leave your own opinions in the comment area.


Author: Ao Bing
Link: https://juejin.cn/post/7052116392979464205
 

Guess you like

Origin blog.csdn.net/wdjnb/article/details/124477569