I blog at bugstack.cn

Precipitate, share, grow, let oneself and others can have a harvest! 😄

👨💻 can’t even read the code written by my colleagues, but still read Spring? Why, Spring is hard to read!

Spring, who lives with us day and night, is just like your daughter-in-law who sleeps beside you. You know to ask her for food, drink, pocket money and skin. But you don’t know how much grain is stored in her warehouse, or whether she’s buying money or saving it in the bank. 🍑 just kidding, next I want to be serious!


Why is Spring difficult to read?

Why Spring every day to use, but want to go to read the source code, how so difficult! Because the Spring framework, created in 2002 by Rod Johnson, an expert in Java and J2EE development, has grown in size as JDK versions and market needs have evolved.

When you read the source code you get the feeling:

  1. Why does the code jump around like it’s not writing your own codepure
  2. Why is it that so many interfaces and interface inheritance, class A inherits from class B that also implements interface X implemented by class A
  3. Simple factory, factory method, proxy pattern, observer pattern, how can there be so many design patterns used
  4. Resource loading, application context, IOC, AOP, and the Bean declaration cycle run through, where the piece of code starts

So, is this a list of problems you’ve encountered while reading about Spring? In fact, not only you can even say that as long as you are engaged in this industry code farmers, want to read Spring source code will have a feeling of don’t know where to start. Therefore, I came up with a solution. Since Spring is too big to understand, I will try to start with a small Spring. It is not understandable to implement a Spring by hand, not to mention the effect is really good. After implementing a simple version of Spring, the understanding of Spring has been greatly improved, and the source code of Spring can also be read.

Two, share hand Spring

Learn about the core principles of Spring by writing a simplified version of the Spring framework like this. In the process of handwriting will simplify the Spring source code, extract the core logic in the overall framework, simplify the code implementation process, retain the core functions, such as IOC, AOP, Bean lifecycle, context, scope, resource processing and other content implementation.

The source code:Github.com/fuzhengwei/…

Implement a simple Bean container

Any concrete implementation of a data structure that can hold data is called a container. Examples are ArrayList, LinkedList, HashSet, etc. But in the case of Spring Bean containers, we need a data structure that can be used to store and index names, so HashMap is the best choice.

Here’s a quick introduction to HashMap. HashMap is a zipper addressing data structure based on perturbation function, load factor, red-black tree transformation and other technical content. It enables data to be distributed more hashed in hash buckets and in linked lists and red-black trees formed when collisions occur. Its data structure will maximize the complexity of the entire data read between O(1) ~O(Logn) ~O(n), of course, in extreme cases, there will be O(n) linked list lookup data. However, we did a perturbation function re-addressing verification test of 100,000 data, and the data is uniformly hashed across the hash bucket indexes, so HashMap is a good fit for the container implementation of Spring Beans.

Another simple Spring Bean container implementation, but also need Bean definition, registration, obtain three basic steps, simplified design as follows;

  • Definition: BeanDefinition, which is probably one of the most common classes you see when you look at Spring source code, such as Singleton, Prototype, BeanClassName, etc. But for now, our initial implementation will be much simpler. We will only define an Object type to hold objects.
  • Registration: This process is equivalent to storing data in a HashMap, except that the HashMap now holds the object information for the defined Bean.
  • The Bean name is key. After the Spring container initializes the Bean, it can be fetched directly.

2. Use the design pattern to realize the definition, registration and acquisition of beans

To complete the Spring Bean container, it is important to register only one class information when the Bean is registered, rather than registering the instantiation information directly into the Spring container. Then you need to change the property Object in BeanDefinition to Class. The next thing you need to do is to handle the Bean Object instantiation operation and determine whether the current singleton has been cached in the container. Figure 3-1 shows the overall design

  • First we need to define a Bean factory called BeanFactory, which provides a method for fetching beansgetBean(String name)The Bean factory interface is then implemented by the abstract class AbstractBeanFactory. Such useTemplate patternThe design method can unify the invocation logic and standard definition of the common core method, and also well control the subsequent implementors do not care about the invocation logic, in accordance with the unified way to execute. Then the inheritors of the class need only care about the logical implementation of the specific method.
  • So after the inherited abstract classes AbstractBeanFactory AbstractAutowireCapableBeanFactory can implement corresponding abstract method, Because AbstractAutowireCapableBeanFactory itself is an abstract class, so it can only realize their own abstract methods, other abstract method by inheritance AbstractAutowireCapableBeanFactory class implements. This is where the implementation of the class comes in. You only need to care about the content that belongs to you. Do not participate in the content that is not yours.
  • In addition there is also very important piece of knowledge, it is about the singleton SingletonBeanRegistry interface definitions, and DefaultSingletonBeanRegistry after the implementation, is an abstract class AbstractBeanFactory inheritance. AbstractBeanFactory is now a very complete and powerful abstract class that demonstrates its abstract definition of template patterns.

3. Implement class instantiation strategy with constructor based on Cglib

The technical design for filling this pit is concerned with two parts. One is where the string flow passes the constructor input information to the instantiation operation properly, and the other is how to instantiate the object containing the constructor.

  • Refer to the implementation of Spring Bean container source code, add in BeanFactoryObject getBean(String name, Object... args)Interface so that we can pass in the constructor input information when we get the Bean.
  • Another core issue is how to create a Bean object with a constructor? There are two options. One is based on Java’s own methodsDeclaredConstructorThe other is to use Cglib to dynamically create Bean objects.Cglib is based on the bytecode framework ASM, so you can also create objects directly by manipulating instructions using ASM

4. Implement the function of injecting properties and dependent beans for Bean objects

Given the attribute is used in Bean filling newInstance or additional created, began to completion attribute information, you can in the class AbstractAutowireCapableBeanFactory createBean method completion attribute is added. This part of everyone in the process of internship can also be compared to Spring source learning, here is the implementation of Spring simplified version, the follow-up study will be more easy to understand

  • Property padding is required after class instantiation is createdAbstractAutowireCapableBeanFactoryIn the createBean method ofapplyPropertyValuesOperation.
  • Since we need to populate the properties when we create the Bean, we need to add PropertyValues information to the Bean definition BeanDefinition class.
  • In addition, the padding property information also includes the object type of the Bean, that is, we need to define another BeanReference, which is actually a simple Bean name, which is created and filled recursively when the concrete instantiation operation is carried out, just like the Spring source implementation.BeanReference is an interface in Spring source code

5. Design and implement a resource loader to parse and register Bean objects from Spring.xml

Following the requirements background in this chapter, we need to add a resource resolver to the existing Spring framework prototype, which can read the configuration content of the CLASspath, local files, and cloud files. The configuration content is the same as the spring.xml configuration for Spring, which includes the description and property information for the Bean object. After reading the configuration file information, the next step is to register the Bean object into the Spring container after parsing the Bean description in the configuration file. The overall design structure is as follows:

  • The resource loader is a relatively separate part of the IO implementation content under the Core package of the Spring framework and is used to process file information in Class, local, and cloud environments.
  • When resources can be loaded, then there’s parsing and registered Bean to the operation, in the Spring the partial implementation needs and DefaultListableBeanFactory core class together, because you all registered the resolved action, will define the Bean information into to this class.
  • Then design the implementation hierarchy of the interface at implementation time, including the need to define the Bean definition’s read interfaceBeanDefinitionReaderAnd do a good job of the corresponding implementation class, in the implementation class to complete the Bean object parsing and registration.

6. Design and implement a resource loader that parses and registers Bean objects from Spring.xml

In order to be satisfied with performing user-defined operations on the Bean object from registration to instantiation, you need to insert an interface class into the Bean definition and initialization process, and then externally implement the required services. When combined with the ability to handle the Spring framework context, we should be able to meet our target requirements. The overall design structure is as follows:

  • The two interfaces that are content to extend Bean objects are, in fact, two very heavyweight interfaces in the Spring framework:BeanFactoryPostProcessBeanPostProcessor, are almost two additional must-have interfaces that people add to develop their own build requirements using the Spring framework.
  • BeanFactoryPostProcessor, a container extension mechanism provided by the Spring framework, allows for defining information about a Bean after the Bean object has been registered but before it has been instantiatedBeanDefinitionPerform modification operations.
  • BeanPostProcessor is also an extension mechanism provided by Spring, but BeanPostProcessor modifiers Bean objects after they are instantiated or can replace Bean objects. This part is closely related to AOP, which will be implemented later.
  • At the same time, if you just add these two interfaces, without any packaging, then it is very troublesome for the user. We want to develop Spring’s contextual operations classes that incorporate the corresponding XML loading, registration, instantiation, and new modifications and extensions so that Spring can automatically scan our new services for users to use.

7. Implement application context, automatic identification, resource loading and extension mechanisms

Perhaps in the face of a framework as large as Spring, the use of exposed interface definitions or XML configuration, complete with a series of extensibility operations, can make the Spring framework seem mysterious. The additional processing that is added to the Bean container initialization is simply a pre-execution of a defined interface method or a method configured in the XML of the invoke class, and you will have the Spring container to invoke it in the process. The overall design structure is as follows:

  • In the spring.xml configurationThe init method, destroy methodTwo annotations. During the loading of the configuration file, the annotation configuration is defined in the property of BeanDefinition. In this way, the method information configured in the Bean definition properties can be invoked by reflection in the project that initializeBean initializes. In the case of an interface implementation, you can call the method defined by the corresponding interface directly from the Bean object.((InitializingBean) bean).afterPropertiesSet(), the effect is the same.
  • In addition to what we did at initialization,destroy-methodDisposableBeanThe definition of the interface, will be completed in Bean object initialization phase, perform registration information destroy method to DefaultSingletonBeanRegistry disposableBeans attribute of class, this is in order for unity to be follow-up.There is also a use of the adapter, because there are two ways to call reflectively and call directly through the interface. So you need to use the adapter to wrap it up. Refer to DisposableBeanAdapter for the implementation of this code below

The destruct method needs to be executed before the virtual machine is shut down, so there needs to be a registered hook operation, such as: Runtime.getruntime ().addShutdownhook (new Thread() -> system.out.println (“close! “)) ))); With this code you can run the test, and you can close the container by manually calling the applicationContext.close method.

8. Register the hook with the VM to implement the initialization and destruction methods of the Bean object

Perhaps in the face of a framework as large as Spring, the use of exposed interface definitions or XML configuration, complete with a series of extensibility operations, can make the Spring framework seem mysterious. The additional processing that is added to the Bean container initialization is simply a pre-execution of a defined interface method or a method configured in the XML of the invoke class, and you will have the Spring container to invoke it in the process. The overall design structure is as follows:

  • In the spring.xml configurationThe init method, destroy methodTwo annotations. During the loading of the configuration file, the annotation configuration is defined in the property of BeanDefinition. In this way, the method information configured in the Bean definition properties can be invoked by reflection in the project that initializeBean initializes. In the case of an interface implementation, you can call the method defined by the corresponding interface directly from the Bean object.((InitializingBean) bean).afterPropertiesSet(), the effect is the same.
  • In addition to what we did at initialization,destroy-methodDisposableBeanThe definition of the interface, will be completed in Bean object initialization phase, perform registration information destroy method to DefaultSingletonBeanRegistry disposableBeans attribute of class, this is in order for unity to be follow-up.There is also a use of the adapter, because there are two ways to call reflectively and call directly through the interface. So you need to use the adapter to wrap it up. Refer to DisposableBeanAdapter for the implementation of this code below

The destruct method needs to be executed before the virtual machine is shut down, so there needs to be a registered hook operation, such as: Runtime.getruntime ().addShutdownhook (new Thread() -> system.out.println (“close! “)) ))); With this code you can run the test, and you can close the container by manually calling the applicationContext.close method.

9. Define the tag type Aware interface to realize container object awareness

If I want to get some of the resources provided by the Spring framework, I first need to consider a way to get them, and then you define the way to get them, and how to get them in the Spring framework. By implementing these two things, you can extend some of the capabilities that belong to the Spring framework itself.

In the Bean object instantiation phase, we have some additional definitions, properties, initialization, and destruction. In fact, we can also do this when we want to obtain Spring objects such as BeanFactory and ApplicationContext. Then we need to define a token interface, which does not need to have a method, but only serves as a token, and the specific functions are defined by other functional interfaces inheriting this interface, which can be determined and called by instanceof. The overall design structure is as follows:

  • Define the interface Aware, which in the Spring framework is a token-aware interface where specific subclass definitions and implementations are Aware of related objects in the container.This bridge is used to provide container services to concrete implementation classes
  • The interfaces to inherited Aware include: BeanFactoryAware, BeanClassLoaderAware, BeanNameAware, and ApplicationContextAware. Of course, there are a few other annotations in the Spring source code, but we won’t use them yet.
  • In the actual interface implementation you can see that part of the (BeanFactoryAware, BeanClassLoaderAware, BeanNameAwareIn addition, ApplicationContextAware is in the support of context, because different content fetching needs to be provided in different packages. So, in the concrete implementation of AbstractApplicationContext to add the beanFactory BeanPostProcessor content may be usedApplicationContextAwareProcessorWhen the operation, and finally founded by AbstractAutowireCapableBeanFactory createBean processing corresponding call operation.About applyBeanPostProcessorsBeforeInitialization has been implemented in the previous section, if you forget to forward pace

10. About Bean object scope and the implementation and use of FactoryBean

In terms of providing a user with the ability to define complex Bean objects, the feature point is great and makes a lot of sense, because by doing so Spring’s ecological seed incubator provides a standard on which any framework can access its own services.

However, the design of this function logic is not complicated, because the entire Spring framework has provided various extension capabilities in the development process, you just need to provide a processing interface call and the corresponding function logic implementation in the appropriate place. The goal implementation, like this one, is to provide a way to get objects from FactoryBean’s getObject method twice, so that all object classes that implement this interface can extend their own object functionality. MyBatis implements a MapperFactoryBean class that provides SqlSession operations in the getObject method to execute CRUD methods.

  • There are two parts to the implementation, one dealing with singletons or prototype objects, and the other dealing with getting specific call objects during the creation of FactoryBean type objectsgetObjectOperation.
  • SCOPE_SINGLETON,SCOPE_PROTOTYPE, the creation and acquisition of object types, the main differences areAbstractAutowireCapableBeanFactory#createBeanWhether to put the object into memory after it is created. If not, it will be recreated every time it is obtained.
  • After createBean performs object creation, property filling, dependency loading, pre – and post – processing, and initialization, it is time to determine whether the entire object is a FactoryBean object. If so, it is time to go ahead and obtain a FactoryBean concrete objectgetObjectThe object. A singleton type judgment is added throughout the getBean processfactory.isSingleton()Is used to determine whether to use memory to store object information.

11. Observer based implementation, container events and event listeners

In fact, the design of the event itself is a kind of implementation of the observer pattern, it is to solve the problem of one object’s state changes to other objects, but also to consider ease of use and low coupling, to ensure a high degree of collaboration.

On the functions we need to define events, event listeners, release, and the function of these classes requires a combination of the Spring AbstractApplicationContext# refresh (), in order to handle events initialization, and the operation of the registered event listeners. The overall design structure is as follows:

  • Throughout the implementation of the function, it still needs to be in the context of the application facing the userAbstractApplicationContextAdds related event content, including initializing event publisher, registering event listener, and publishing container refresh completion event.
  • The observer pattern is used to define the event class, listener class, and publication class. At the same time, it needs to complete the function of a broadcaster. When receiving the event push, it will analyze and process the events that match the listener’s interest, that is, it uses isAssignableFrom to judge.
  • IsAssignableFrom is similar to instanceof, but isAssignableFrom is used to determine the relationship between a subclass and its parent class, or an interface’s implementation class and its interface. By default, all classes have Object as their ultimate parent class. If a. assignablefrom (B) is true, then B can be converted to A, i.e. A can be converted from B.

12. Implement AOP core functions based on JDK and Cglib dynamic proxy

Before we can integrate the entire AOP aspect design into Spring, we need to solve two problems: how to proxy methods that comply with the rules, and how to split the responsibilities of the classes after we have done the proxy method case. And the realization of these two function points is designed and developed with the idea of section. If it’s not clear what AOP is, you can think of cutting as cutting a leek with a knife, one by one is always a bit slow, then pinch the leek by hand (proxy) and handle it with different intercepting operations such as a kitchen knife or an axe. In fact, the program is the same, but the leek into a method, the kitchen knife into an interception method. The overall design structure is as follows:

  • Just as you would with Spring’S AOP, handle only methods that need to be intercepted. After intercepting the method, perform your extension to the method.
  • So we first need to implement a Proxy that can Proxy methods, which use the method interceptor class to handle method callsMethodInterceptor#invokeInstead of using the invoke Method input Methodmethod.invoke(targetObj, args)This is the overall difference in usage.
  • In addition to the above core functionality implementation, also need to useorg.aspectj.weaver.tools.PointcutParserHandling interception expressions"execution(* cn.bugstack.springframework.test.bean.IUserService.*(..) )"With method proxies and handling interceptors, we are ready to design an AOP prototype.

Integrate AOP dynamic proxies into the Bean lifecycle

In fact, with the implementation of the core FUNCTIONALITY of AOP, it is not difficult to integrate these functions and services into Spring, but to solve several problems, including: How to integrate dynamic proxies into the Bean lifecycle through BeanPostProcessor, and how to assemble pointcuts, interceptors, and prefixes, and adapt the corresponding proxies. The overall design structure is as follows:

  • In order for the proxy objects configured in XML to be instantiated during object creation, i.e., some class objects of the aspect, we need to use the methods provided by BeanPostProcessor, because the methods in this class can be used to modify the extension information of the Bean object before and after the Bean object is initialized. But the new interface and implementation class need to be implemented in BeanPostProcessor so that the corresponding class information can be directed.
  • Before but because create a proxy object is not the ordinary objects in the process, so we need before on other objects to create, so in the actual development process, the need to AbstractAutowireCapableBeanFactory# createBean prioritize Bean object judgment, If a proxy is required, return the proxy object directly.The createBean and doCreateBean methods are split in the Spring source code
  • This section also includes the specific functions to resolve the interceptors, providing some BeforeAdvice and AfterAdvice implementations to make it easier for users to use the aspect functions. This includes the need to wrap aspect expressions and the integration of interception methods, as well as agent factories that provide different types of proxy methods to wrap our aspect services.

3. Study instructions

This code repository github.com/fuzhengwei/… For the purpose of learning Spring source code, by writing a simplified version of the Spring framework, understand the core principles of Spring.

In the process of handwriting will simplify the Spring source code, extract the core logic in the overall framework, simplify the code implementation process, retain the core functions, such as IOC, AOP, Bean lifecycle, context, scope, resource processing and other content implementation.


  1. This column is actual coding materials, in the process of learning, we need to combine the objectives to be solved in each chapter of the paper, the design of ideas, into the practical coding process. While learning the code, it is also a good idea to understand why this part of the implementation is the way it is, what design patterns are used, and what means are used to do what separation of responsibilities. Only through such learning can we better understand and master the implementation process of Spring source code, but also can help you in the future in-depth learning and practical application of the process to lay a solid foundation.

  2. In addition, the content of this column is combined with the design pattern, which corresponds to the design and development of SpringBoot middleware. Therefore, readers can browse the relevant materials if they don’t understand the design pattern during the learning process, and can also practice with the content of middleware after learning Spring.

  3. Source code: the source code involved in this column has all been integrated into the current project, and can be matched with the corresponding case source code in the chapter. You can get a complete set of projects to run directly, you can also open each chapter corresponding to the source code project to run separately.

  4. If you encounter any problems in the process of learning, including: can’t run, optimization comments, text errors, anything can submit an issue

  5. Each section of the column provides clear design drawings and corresponding class diagrams, so it’s important not just to learn how the code was written, but to understand where the content came from.


😁 ok, hope you can learn happily!