Spring-bean registration simulation

Spring-bean registration simulation

This is the 20th day I participated in the more essay challenge

Spring source code analysis

1. Simulate spring source code

Understand the general process of Spring work

Familiar with the basic concepts of BeanDefinition, BeanFactory, Bean, etc.

Familiar with the basic concepts of Bean life cycle, Bean post processor and other basic concepts

1. Ask the question: how to automatically create a bean instance for us in the spring container

  1. First of all, you must know those beans to be implemented. I can t say that I scan them all at once. I don t care about them. All of them are instantiated. Spring obviously doesn t do this?

  2. When is it scanned? It must be scanned when the spring container is created? How do you scan? Specify the path (this produces a series of stories)

    So to achieve this, we use our own container

    //Start spring, don t say goodbye, at least help us create bean objects, //1. How to scan, I must tell me the path so that I can work, realize the @ComponentScan annotation, and the Appconfig configuration class //2. Then specify Is it necessary for all beans to be scanned and created? No, only the classes annotated with Component will be created //3. Then these classes are created when they are started, or are they created when they are used? Is it a singleton? Spring is implemented like this: start to create non-lazy loaded beans //For prototype beans, they will only be created when get (classes decorated with Scope), //For singleton beans: lazy loaded beans are also created when getBean , Non-lazy loading is created when the spring container is created AnZhiApplicationContext applicationContext = new AnZhiApplicationContext(AppConfig.class); //container copy code

    What class is AppConfig? It is a class that specifies path scanning. Take a look at the implementation:

    @ComponentScan("com.anzhi.service") //Specify the path to be scanned. At this time, there should be questions: Where does this comment come from, we implement it ourselves, after all, we have to simulate it by ourselves public class AppConfig { } Copy code

    ComponentScan annotation implementation:

    @Retention(RetentionPolicy.RUNTIME) //This is a custom annotation, so you don t need to implement it yourself @Target(ElementType.TYPE) public @interface ComponentScan { String value () default "" ; //Default empty value, What is the specific role, the editor did not elaborate, (My commentary idiot passed by) } Copy code

    Okay, now that the basics are implemented, let s start to implement it. This code contains some of the later implementations. I changed the version I created at the beginning, and I can t fix it. Look and understand.

    public void scan (Class configClass) { //Scan implementation //1. First determine whether the annotation exists if (configClass.isAnnotationPresent(ComponentScan.class)) { //1.2 Only if it exists, get it ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class); //1.3 Get scan path String path = componentScan.value(); path = path.replace( "." , "/" ); System.out.println(path); //1.4 Finally, we need to find the class we need under the target path ClassLoader classLoader = AnZhiApplicationContext.class.getClassLoader(); URL resource = classLoader.getResource(path); //1.5 Convert to File type, traverse to find all class File files = new File(resource.getFile()); for (File f:files.listFiles()){ //Use bytecode to determine the class Is there any comment? //Here is another simple method: //1. Get the path first String s = f.getAbsolutePath(); if (s.endsWith( ".class" )){ s = s.substring(s.indexOf( "com" ), s.indexOf( ".class" )); s = s.replace( "\\" , "." ); try { //Acquire the class object according to the path Class clazz = classLoader.loadClass(s); System.out.println(clazz); //Determine what the current bean is if (clazz.isAnnotationPresent(Component.class)){ //Scan the post processor and instantiate it, that is, determine whether to implement an interface if (BeanPostProcessor.class.isAssignableFrom(clazz)){ //After traversing, these beans need to be stored for calling try { BeanPostProcessor o = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance(); beanPostProcessors.add(o); //store } catch (Exception e){ e.printStackTrace(); } } //bean definition BeanDefintion BeanDefintion beanDefintion = new BeanDefintion(); beanDefintion.setBeanClass(clazz); //component gets annotations Component component = (Component) clazz.getAnnotation(Component.class); String beanName = component.value(); if (clazz.isAnnotationPresent(Lazy.class)){ //If it is lazy loading, define it as true beanDefintion.setLazy( true ); } if (clazz.isAnnotationPresent(Scope.class)){ Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class); String value = scopeAnnotation.value(); beanDefintion.setScope(value); } else { //Singleton beanDefintion.setScope( "singleton" ); } //So in the future, we will only find the required bean from beanDefintionMap based on the definition of beandefinteion beanDefintionMap.put(beanName,beanDefintion); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } } Copy code

    How do you know which class is to be scanned when scanning? Component annotation, the code above is also explained, Component annotation is implemented, and all the annotations must be instantiated

    package com.anzhi.framework; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Component { String value () default "" ; } Copy code

    The annotation scanning and the creation of the bean are completed above, what to do next, use it, or create it

    public Object getBean (String beanName) { //Question: getBean only gets a bean name, how do you know which class it corresponds to? Even if you know this class, //how do you know whether this class has annotations and whether it is a singleton? Whether it s lazy loading, etc., you have to judge it again like before. //Spring is definitely not implemented like this. BeanDefintion: Represents the definition of the bean and caches the bean. //The bean is generated according to BeanDefintion. //Put the bean object on it. Into the beanDefintionMap, so you only need to judge according to the name at this time if (!beanDefintionMap.containsKey(beanName)){ throw new NullPointerException(); } else { BeanDefintion beanDefintion = beanDefintionMap.get(beanName); if (beanDefintion.getScope().equals( "singleton" )) { //Singleton Object o = singletoObjects.get(beanName); if (o == null ){ Object bean = createBean(beanDefintion, beanName); singletoObjects.put(beanName, bean); } return o; } if (beanDefintion.getScope().equals( "prototype" )) { //Create bean Object bean = createBean(beanDefintion, beanName); return bean; } } return null ; } Copy code

    In these two steps, we actually think about it carefully. What is the difference? We know that there must be more than one object to be annotated when scanning.

    package com.anzhi.service; import com.anzhi.framework.Component; @Component("orderService") public class OrderService { } package com.anzhi.service; import com.anzhi.framework.Autowired; import com.anzhi.framework.BeanNameAware; import com.anzhi.framework.Component; import com.anzhi.framework.InitializingBean; @Component("userService") public class UserService implements BeanNameAware , InitializingBean { //Realize automatic injection @Autowired private OrderService orderService; private String beanName; @Override public void setBeanName (String name) { this .beanName = name; } public void test () { System.out.println(orderService); System.out.println(beanName); } @Override public void afterPopertiesSet () { //Judging whether the created bean conforms to the rules, occurs when the bean is created, and the property setting is completed if (orderService == null ){ } } } Copy code

    So we need to build pools to store separately, why are they stored separately, because in spring, bean objects are also divided into many situations, such as native bean types, singleton beans, and when they are created and divided into lazy loading. These different beans Of course it can t be cooked in one pot, so create two pools for storage

    //As mentioned below, the problem of getBean has been created BeanDefintion to constrain the bean so that it can be identified, //but it also needs to be stored with a map for calling private Map<String, BeanDefintion> beanDefintionMap = new the HashMap <> (); Private the Map <String, Object> = singletoObjects new new the HashMap <> (); //singleton pool singleton duplicated code

    Up to this point, some of the variables mentioned above can be found here (that is, the hard readers have flipped up and down)

    Here we see the singleton pool, singleton mode, and for this non-lazy loaded singleton, the spring container needs to be started when it is created, so it needs to be implemented in the construction method of the startup container. Did the sacn method think about it? Also need to be called in the constructor

    public AnZhiApplicationContext (Class configClass) { this .configClass = configClass; //1. Scan beanDefintion scan(configClass); //2. Create a non-lazy bean createNonLazySingleto(); } Copy code

    Lazy loaded bean creation:

    private void createNonLazySingleto () { for (String beanName: beanDefintionMap.keySet()){ BeanDefintion beanDefintion = beanDefintionMap.get(beanName); if (beanDefintion.getScope().equals( "singleton" ) && !beanDefintion.isLazy()){ //If it is a non-lazy bean, after it is created, it also needs to be stored to create a singleton pool, singleton bean , Is a little different from the singleton pattern.//So how to create a bean? Object bean = createBean(beanDefintion,beanName); singletoObjects.put(beanName,bean); } } } Copy code

    Then how to recognize lazy loading, it also needs to be annotated, and the annotations must be implemented by us

    package com.anzhi.framework; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Lazy { String value () default "" ; } Copy code

    Such a basic framework function is realized. The problem in the code is also helpful to understand by looking at it.

    So how to identify the attributes of the annotated class? How do I know that this is lazy loading, singleton? That's why BeanDefintion exists. It contains the attribute definition of the bean. Here are just a few of them. The attributes in spring are much more than these.

    package com.anzhi.framework; public class BeanDefintion { private String scope; private boolean isLazy; private Class beanClass; public BeanDefintion() { } public BeanDefintion(String scope, boolean isLazy, Class beanClass) { this.scope = scope; this.isLazy = isLazy; this.beanClass = beanClass; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public boolean isLazy() { return isLazy; } public void setLazy(boolean lazy) { isLazy = lazy; } public Class getBeanClass() { return beanClass; } public void setBeanClass(Class beanClass) { this.beanClass = beanClass; } } Copy code

    Spring also provides a post processor to further process the created bean object. For example, there are other annotations in the object, such as Resources, our own AnZhiAutoWired, how to implement it, corresponding to different annotations. Different classes are processed, and these classes need to implement the same interface. After the bean is created, put it into a pool, and traverse matching

    package com.anzhi.service; import com.anzhi.framework.BeanPostProcessor; import com.anzhi.framework.Component; @Component public class AnZhiAutoWiredBeanPostProcessor implements BeanPostProcessor { @Override public void autowired () { System.out.println( "Processing AnZhiAutoWired annotations" ); } } -------------- package com.anzhi.service; import com.anzhi.framework.BeanPostProcessor; import com.anzhi.framework.Component; @Component public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor { @Override public void autowired () { System.out.println( "Processing Autowired annotations" ); } } --------------- package com.anzhi.service; import com.anzhi.framework.BeanPostProcessor; import com.anzhi.framework.Component; @Component public class CommonAnnotationBeanPostProcessor implements BeanPostProcessor { @Override public void autowired () { System.out.println( "Processing Common Annotations" ); } } Copy code

    Interface definition

    package com.anzhi.framework; public interface BeanPostProcessor { void autowired () ; } Copy code

    And these classes also need us to load and instantiate, otherwise how to use Le, so each class is annotated with Component, and an instance is created when the package is scanned.

    So far, a spring with initial functions has been completed. (It may be a little uncomfortable to watch, please understand. After watching the video, I understood it according to my ideas. Because I am looking for a job and the time is relatively tight, I probably convinced myself to understand, haha)

    For detailed code, please refer to gitee.com/anzhihong10...