This is my 20th day of the Gwen Challenge

Spring source code analysis

1. Simulate spring source code

Get an overview of how Spring works

Familiar with BeanDefinition, BeanFactory, Bean and other basic concepts

Familiar with basic concepts such as Bean life cycle, Bean post-processor, etc

1. Ask the question: How do you automatically help us create a bean instance in the Spring container

  1. The first thing you need to know is which beans are going to be implemented. You can’t just scan them and instantiate them all. Spring doesn’t do that.

  2. When is it scanned? It must be scanned when the Spring container is created. So how do you scan? You specify a path.

    So the implementation is, we all use our own containers

    // Start Spring, or at least help us create bean objects,
    //1. How to scan @ComponentScan and Appconfig
    //2. If the path is specified, all beans will be scanned. No, only classes with Component annotations will be created
    //3. Are these classes created at startup, or are they created at use, or are they singletons? Spring is implemented by starting the creation of non-lazy-loaded beans
    // For prototype beans, only get classes are created (classes decorated with Scope),
    // For singleton beans: lazy-loaded beans are created when getBean is created, and non-lazy-loaded beans are created when Spring container is created
    AnZhiApplicationContext applicationContext = new AnZhiApplicationContext(AppConfig.class); / / container
    Copy the code

    AppConfig specifies a path scan class.

    @ComponentScan("com.anzhi.service") // Specify the path to scan, then there should be a question: where did this comment come from
    public class AppConfig {}Copy the code

    ComponentScan annotation implementation:

    @Retention(RetentionPolicy.RUNTIME)  // This is a custom annotation
    @Target(ElementType.TYPE)
    public @interface ComponentScan {
        String value(a) default "";  // The default value is null.
    }
    Copy the code

    Ok, now that the basic implementation, so start implementing, this code contains some later implementation, the original version OF the creation I have changed, can not complete, look at the understanding

    public void scan(Class configClass) {
        // Scan implementation
        //1. First check whether the annotation exists
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            //1.2 Exist to obtain
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            1.3 Obtaining the scan path
            String path = componentScan.value();
            path = path.replace("."."/");
            System.out.println(path);
            // finally we need to go to the target path to find the class we need
            ClassLoader classLoader = AnZhiApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            //1.5 converts to File and iterates to find all classes
            File files = new File(resource.getFile());
            for(File f:files.listFiles()){
                // Use bytecode in Spring to determine if there are annotations in a class
                // Here's a simpler way:
                //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 {
                        // Get the class object based on the path
                        Class clazz = classLoader.loadClass(s);
                        System.out.println(clazz);
                        // What is the current bean
                        if(clazz.isAnnotationPresent(Component.class)){
                            // Scan the back processor and instantiate it to see if an interface is implemented
                            if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                                // After traversal, these beans need to be stored for invocation
                                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 the annotation
                            Component component = (Component) clazz.getAnnotation(Component.class);
                            String beanName = component.value();
    
                            if(clazz.isAnnotationPresent(Lazy.class)){
                                // For lazy loading, define true
                                beanDefintion.setLazy(true);
                            }
                            if(clazz.isAnnotationPresent(Scope.class)){
                                Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefintion.setScope(value);
                            }else{
                                / / the singleton
                                beanDefintion.setScope("singleton");
                            }
    
                            // So we will only look up the beans we want from the BeanDefinteion map based on the beanDefinteion definitionbeanDefintionMap.put(beanName,beanDefintion); }}catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }
    }
    Copy the code

    How do you know which class is being scanned? The Component annotation, as indicated in the code above, implements the Component annotation, and instantiates anything to which the annotation is added

    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(a) default "";
    }
    Copy the code

    The annotations are scanned and the bean is created. Then, use it or create it

    public Object getBean(String beanName) {
        // Problem: getBean gets only the name of the bean. How do you know which class it belongs to?
        // If this class has annotations, singletons, lazy loading, etc.
        // Spring definitely does not implement this. BeanDefintion: represents the definition of the bean, caches the bean,
        // Beans are generated from BeanDefintion
        // The bean object is placed in the beanDefintionMap, so you just need to judge by name
        if(! beanDefintionMap.containsKey(beanName)){throw new NullPointerException();
        }else {
            BeanDefintion beanDefintion = beanDefintionMap.get(beanName);
            if (beanDefintion.getScope().equals("singleton")) {
                / / the 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 a bean
                Object bean = createBean(beanDefintion, beanName);
                returnbean; }}return null;
    }
    Copy the code

    Now, if we think about it, there’s something missing, what’s missing, because we know that there’s more than one object scanning for annotations like this, right

    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 {
    
        // Implement automatic injection
        @Autowired
        private OrderService orderService;
    
        private String beanName;
    
        @Override
        public void setBeanName(String name) {
            this.beanName = name;
        }
    
        public void test(a){
            System.out.println(orderService);
            System.out.println(beanName);
        }
    
        @Override
        public void afterPopertiesSet(a) {
            // Determine whether the created bean conforms to the rule. This happens after the bean is created and its properties are set
            if(orderService == null) {}}}Copy the code

    In Spring, bean objects can be divided into many different types, such as native bean types, singleton beans, and lazy loading beans. These different types of beans can not be mixed at the same time

    // As mentioned below, the getBean problem has already been constrained by BeanDefintion to identify,
    // But you also need to store it with map for invocation
    private Map<String, BeanDefintion> beanDefintionMap = new HashMap<>();
    private Map<String, Object> singletoObjects = new HashMap<>();  // Singleton pool, singleton mode
    Copy the code

    So here, some of the variables that I talked about can be found here.

    Here we see singleton pooling, singleton pattern, and for this type of singleton that is not lazyloaded, it is required to be started when the Spring container is created, so the implementation needs to be called in the constructor of the startup container. Did you think sacN method needs to be called in the constructor as well

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

    Lazy bean creation:

    private void createNonLazySingleto(a) {
        for(String beanName : beanDefintionMap.keySet()){
            BeanDefintion beanDefintion = beanDefintionMap.get(beanName);
            if(beanDefintion.getScope().equals("singleton") && !beanDefintion.isLazy()){
                // If the beans are not lazily loaded, they need to be stored after they are created. Create a singleton pool. Singleton beans are a little different from singleton pattern
                // How to create a beanObject bean = createBean(beanDefintion,beanName); singletoObjects.put(beanName,bean); }}}Copy the code

    How is it to identify lazy loaders, also need to add annotations, and annotations also have our implementation

    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(a) default "";
    }
    Copy the code

    So a basic framework function is implemented. The problems in the code are also helpful to look at.

    So how do I recognize the properties of this annotated class, how do I know that this is lazily loaded, singleton? Hence the existence of BeanDefintion, which contains the bean’s attribute definitions. There are just a few here, but there are many more attributes in Spring

    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 the code

    Spring also provides a post-processor to further process the bean object that has been created. For example, there are other annotations in the bean object, such as Resources, AnZhiAutoWired, which we define ourselves. These classes need to implement the same interface and, after creating the bean, put it into a pool and iterate over the matching execution

    package com.anzhi.service;
    
    import com.anzhi.framework.BeanPostProcessor;
    import com.anzhi.framework.Component;
    
    @Component
    public class AnZhiAutoWiredBeanPostProcessor implements BeanPostProcessor {
        @Override
        public void autowired(a) {
            System.out.println("Handle AnZhiAutoWired annotation"); }} -- -- -- -- -- -- -- -- -- -- -- -- -- -package com.anzhi.service;
    
    import com.anzhi.framework.BeanPostProcessor;
    import com.anzhi.framework.Component;
    
    @Component
    public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor {
        @Override
        public void autowired(a) {
            System.out.println("Handling 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(a) {
            System.out.println("Handling Common annotations"); }}Copy the code

    The interface definition

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

    These classes also need to be loaded with instantiations, so each class has a Component annotation that creates an instance while scanning the package.

    At this point, a fledgling Spring is complete. (May be a little uncomfortable to watch, please understand, after watching the video, I understand according to the train of thought, because I have to find a job, the time is relatively tight, so I probably convinced myself to understand, haha)

    See gitee.com/anzhihong10…