Spring source code implementation

Spring source code implementation

Become a more qualified spring engineer by implementing the mini version of the spring framework

  1. Implement the entire scanning process of the spring framework,@ComponentScan
  2. Realize parsing the full-matching class path, and use the class loader to load (parental delegation principle) sweeping class, generateBeanDefinitionMap
  3. Throughout the familiar Reflection reflection call, including:getClass,annotation,field,constructor,method
  4. Implement Awarecontainer-aware technology (set method + reflection application)
  5. The implementation uses InitializingBeanthe way of implementing to initialize the Bean object
  6. Realize usage Java-jdk 动态代理to realize AOP aspectBeanPostProcessor
  7. Familiar with Bean naming definition, creation process, and life cycle
  8. implement problem 三级缓存依赖solving循环依赖
    insert image description here

Description of the running process of the spring framework

  • com.source.content application code
  • com.source.spring framework source code

The step description of the framework running process includes two parts: the default is the spring framework, which is marked with [application], and this step description belongs to the application

  1. Create a management container SpringApplicationContext: the container is used to load the configuration file and provide the method getBean() to get the Bean
  2. (Application) Create an application configuration class AppConfig: used to identify the package path that the spring framework needs to scan
  3. (Application) Create two classes UserService, XxUtils: used to hand over to the spring framework for scanning later, one is a Bean object, one is a common class, only the classes marked with annotations will be parsed
  4. Create annotations @ComponentScan, @Component: Mark by annotation: which packages need to be scanned, and @Componentthe class marked with in the scanned package directory is the Bean object
  5. Pass 类加载器to 获取注解标记的所有类对象, and prepare to convert the class object to the specified data type of the Bean objectBeanDefinition
  6. At this point, we design the Bean into two types: 单例 singleton, 原型 prototype. So create an annotation @Scopeto mark which type of Bean this object is
  7. During the scanning process, all beans are stored in <beanName, beanDefinition> key-value pairs for beanDefinitionMapmanagement.
  8. After scanning, create corresponding instances for all bean objects. Here: the bean whose scope is a singleton first beanDefinitionMapchecks whether it has been created, if not, it will be created, and if it is, it will return the created one, and the bean whose scope is a prototype will directly create the corresponding instance

  1. Dependency injection implementation. All beans have been scanned and stored in beanDefinitionMap, and instances are created for all singleton beans, and all prototype beans just store their bean data structuresbeanDefinition
  2. Container-aware beanNameAware: For a certain Bean itself (userService), the user does not know the information about the container. Implement beanNameAware container awareness
  3. Bean initialization method: Two bean initialization methods are provided in the spring source code, and one of them is implemented hereinitializingBean
  4. AOP implementation: through JAVA-JDK dynamic proxy implementation, that is, to realize the AOP aspect, and the implementation location is located BeanPostProcessor. It should be noted that the proxy is a proxy for a certain behavior of a specific object, so the object that must be proxied must implement at least one interface

difficulties and challenges

In the process of writing the source code, if you encounter unclear knowledge points, record them in the source-exfolder

Source Code Award

  1. spring container
    public SpringApplicationContext(Class<?> primarySource, Class<?> configClass) {
    
    
        this.configClass = configClass;
        // 扫描
        scanBeanDefinition(configClass);
        // AOP
        registerBeanPostProcessors();
        // 获取Bean
        preInstantiateSingletons();
    }

  1. scan scanBeanDefinition
/**
     * 扫描.解析配置类
     * 过程:通过传入的配置信息 ComponentScan 获取扫描路径  --> 执行扫描 @Component
     *
     * @param configClass 配置类
     */
    private void scanBeanDefinition(Class<?> configClass) {
    
    

        // 1. 传递来的类,是都被扫描注解标记
        ComponentScan componentScanAnnotation = configClass.getAnnotation(ComponentScan.class);
        String scanPath = componentScanAnnotation.value();

        // 2. 获取扫扫描路径后,准备扫描。一个包下有许多的类,我们框架关心的是被指定注解标记的类(@Component),才会被扫描
        // 如何获取一个包下面的java类?使用类加载器

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL resource = classLoader.getResource("com/sourcecode/content/service");
        assert resource != null;
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
    
    
            for (File listFile : Objects.requireNonNull(file.listFiles())) {
    
    

                // 2.1 由于 classLoader.loadClass(quotePath) 需要获取的是 com.xxx.xxx这样的引用地址,所以需要转换一下
                String absolutePath = listFile.getAbsolutePath();
                if (absolutePath.endsWith(".class")) {
    
    
                    String quotePath = resolveClassAbsolutePath(absolutePath);
                    try {
    
    
                        Class<?> aClass = classLoader.loadClass(quotePath);

                        if (aClass.isAnnotationPresent(Component.class)) {
    
    

                            // 2.2 使用 @Component 注解装饰类:就表示希望将它交给Spring容器托管,它是一个bean对象
                            //  class  ---??--->  Bean
                            // 2.3 在将class转换为我们制定的Bean类型时,由于Bean有两种类型:单例和原型。需要使用单例模式来确保Bean对象的唯一性
                            // 因此,如何实现单例呢?
                            // 可以创建一个@Scope注解来标识它是单例还是原型Bean类型,同时创建一个Map来保存所有的Bean。
                            // Map {BeanName,BeanObject}  也就是常说的单例池

                            // 2.4 判断是单例Bean还是原型Bean
                            Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();


                            if ("".equals(beanName)) {
    
    
                                beanName = Introspector.decapitalize(aClass.getSimpleName());
                            }

                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(aClass);

                            if (aClass.isAnnotationPresent(Scope.class)) {
    
    
                                Scope scopeAnnotation = aClass.getDeclaredAnnotation((Scope.class));
                                String scope = scopeAnnotation.value();
                                beanDefinition.setScope(scope);
                            } else {
    
    
                                beanDefinition.setScope("singleton");
                            }
                            // 存储Bean
                            beanDefinitionMap.put(beanName, beanDefinition);

                        }

                    } catch (ClassNotFoundException e) {
    
    
                        e.printStackTrace();
                    }
                }

            }
        }
    }
  1. Get Bean:preInstantiateSingletons
/**
     * 初始化:实例化所有的单例Bean
     */
    private void preInstantiateSingletons() {
    
    
        beanDefinitionMap.forEach((beanName, beanDefinition) -> {
    
    
            if (beanDefinition.isSingleton()) {
    
    
                getBean(beanName);
            }
        });
    }

    public Object getBean(String beanName) {
    
    
        Asset.notNull(beanName);
        if (!beanDefinitionMap.containsKey(beanName)) {
    
    
            throw new NullPointerException("没有找到bean:" + beanName);
        } else {
    
    
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

            if (beanDefinition.isSingleton()) {
    
    
                // 实现3级缓存或者使用最简单的一级缓存,只需在方法中 getSingleton 决定
                Object singletonObject = getSingleton(beanName, true);

                // 三级缓存中都没有,那么就只能 create
                if (singletonObject == null) {
    
    
                    singletonObject = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, singletonObject);
                    earlySingletonObjects.remove(beanName);
                    singletonFactories.remove(beanName);
                }

                return singletonObject;
            } else {
    
    
                // prototype.每次创建新对象
                return createBean(beanName, beanDefinition);
            }
        }
    }

Guess you like

Origin blog.csdn.net/win7583362/article/details/126949455