【Spring第一篇】手写模拟Spring框架核心逻辑

前言

Spring技术天天用,但是理论上很欠缺功夫,对于一个Java研发来说,这是不应该的,后面会计划的将技术这块每周三篇博客的频率整理一下,巩固自己的理论知识,博客这块都是自己对技术的理解和认识,如有不足的地方或者错误的地方,欢迎大佬们指出…

项目目录 【Spring第一篇】手写模拟Spring框架核心逻辑_第1张图片

注:此为maven项目,项目中额pom.xml不依赖任何jar包,所有的注解实例都是自己定义写

相关注解类:

package com.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target({
     ElementType.FIELD,ElementType.METHOD})
public @interface Autowired {
      //此注解为Spring 自动注入

    boolean required() default true;
}

------------------------------------------------------------------------------------
package com.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)
public @interface Component {
       //类上添加此注解,Spring在启动时,创建bean并放入Spring容器中

    String value() default "";
}

---------------------------------------------------------------------------------------
package com.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
/**
 * @Target 注解表示使用的作用域范围,也就说这个注解可以放在哪些地方
 * ElementType.TYPE            : 接口、类、枚举
 * ElementType.FIELD           : 字段、枚举的常量
 * ElementType.METHOD          : 方法
 * ElementType.PARAMETER       : 方法参数
 * ElementType.CONSTRUCTOR     : 构造函数
 * ElementType.LOCAL_VARIABLE  : 局部变量
 * ElementType.ANNOTATION_TYPE : 注解
 * ElementType.PACKAGE         : 包
 */
@Target(ElementType.TYPE)
public @interface ComponentScan {
     

    String value() default "";
}
-------------------------------------------------------------------------------------
package com.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)
public @interface Lazy {
       //懒加载

}
---------------------------------------------------------------------------------------
package com.spring;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target(ElementType.TYPE)
public @interface Scope {
     

    String value() default "";
}

相关接口类:

package com.spring;

/**
 * 用于实例化后的回调
 */
public interface BeanNameAware {
     
    public void setBeanName(String name);
}
---------------------------------------------------------------------------------------
package com.spring;

/**
 * @Description:Bean后置处理器,Spring容器在初始化bean的时候,会回调BeanPostProcessor中的两个方法
 * @Author: Maker
 * @Date: 2020/11/5 14:39
 */
public interface BeanPostProcessor {
     
    //在每一个bean对象的初始化方法调用之前回调
    void postProcessBeforeInitialization(String beanName,Object bean);
    
    //会在每个bean对象的初始化方法调用之后被回调
    void postProcessAfterInitialization(String beanName,Object bean);
}
------------------------------------------------------------------------------------
package com.spring;

/**
 * @Description: 原型、单例
 * @Author: Maker
 * @Date: 2020/11/5 14:13
 */
public enum ScopeEnum {
     
    singleton,  //单例
    prototype  //原型
}

1.Spring 启动的时候都做了什么?

【Spring第一篇】手写模拟Spring框架核心逻辑_第2张图片

(1)扫描
Spring在启动的时候,会去加载配置类AppConfig是否有通过@ComponentScan注解指定扫描路径范围,并且扫描的是target目录下的.class文件

package com.maker;

import com.spring.MakerApplicationContext;

/**
 * @Description:测试模拟手写Spring启动加载过程
 * @Author: Maker
 * @Date: 2020/11/5 9:33
 */
public class Test {
     
    public static void main(String[] args) {
     
        //模拟Spring启动时,先去查看AppConfig是否被@ComponentScan注解标识,并且获取注解中指定的扫描路径范围
        MakerApplicationContext applicationContext = new MakerApplicationContext(AppConfig.class);
        //扫描 + 实例 流程走完后,Spring容器中就会存在相应的实例bean,此时就可以通过每个实例取得别名来从容器中获取响应的实例了
        System.out.println(applicationContext.getBean("userService"));
        System.out.println(applicationContext.getBean("userService"));
        System.out.println(applicationContext.getBean("userService"));
    }
}

package com.maker;

import com.spring.ComponentScan;

/**
 * @Description: 配置类
 * @Author: Maker
 * @Date: 2020/11/5 9:37
 */
@ComponentScan("com.maker.service")  //spring 启动时会去扫描的包路径
public class AppConfig {
     
}

【Spring第一篇】手写模拟Spring框架核心逻辑_第3张图片
(2)实例化
实例化就是创建实例的bean,并不是目录下所有的.class文件都会实例化,只有非懒加载的单例Bean才会被实例化,如果一个类上添加了@Scope(“prototype”)或者@Lazy 注解时,那么Spring启动时是不会实例化该对象的,只有在使用该对象的时候才会实例化

  • 1.实例化
    表示从target目录下获取所有非懒加载的单例Bean,并且为了后续流程不在走同样的实例流程,每次实例一个,就放入一个ConcurrentHashMap集合中存起来,key为bean的名称,value为bean的定义类,里面描述了这个bean的一些信息,如是否为单例,是否为懒加载,bean的实例类型等等信息

  • 2.属性填充
    补充BeanDefinition信息,并且此处涉及注入依赖的问题,如果OrderService通过注解@Autowired注入到UserService 中,是先通过ByType类型去匹配,在通过ByName去寻找
    问:@Autowired 为什么是先ByType后ByName?
    解:因为OrderService上被注解@Component(“orderService”),并且配置的别名是orderService,而别名是随意取得,其他的类也可以叫这个,所有在ConcurrentHashMap容器中可能存在实例名称相同,但是对应的实例Bean对象不一样,如果先通过ByName去寻找,可能会匹配到多个实例Bean对象
    补充:@Resource注解,是直接通过ByName匹配的(留着后续文章详解)

  • 3.Aware回调
    实现BeanNameAware接口中的setBeanName方法,获取Spring实例bean对象的别名

  • 4.初始化
    实现InitializingBean接口的afterPropertiesSet方法,表示在一个bean对象实例完成时,校验一下是否创建成功,例如:Spring创建UserService时,不希望注入的属性对象orderService为空,否则抛异常
    补充Bean的后置处理器:BeanPostProcessor接口,实现postProcessBeforeInitialization和postProcessAfterInitialization也就是在bean初始化之前或者之后做增强的处理

  • 5.添加到单例池
    如果实例的对象是单例,则添加到创建的单例池中,以便后面使用的时候不需要再次实例化以便,直接从单例池中获取(这里比源码中的要简单一些,便于理解)

package com.maker.service;

import com.spring.*;

@Component("userService")  //@Component 添加了这个注解,Spring在启动时,实例bean的时候默认为单例
@Scope("prototype") //原型bean,此注解不加时,Spring 默认为singleton单例
@Lazy //懒加载,spring启动时不会实例化的对象,只有在使用的时候才回去实例化对象
public class UserService implements BeanNameAware,InitializingBean {
     

    @Autowired
    private OrderService orderService;

    private String name;

    public OrderService getOrderService() {
     
        return orderService;
    }

    public void setOrderService(OrderService orderService) {
     
        this.orderService = orderService;
    }

    public String getName() {
     
        return name;
    }

    public void setName(String name) {
     
        this.name = name;
    }

    @Override
    public void setBeanName(String name) {
     
        //实现BeanNameAware接口,回调,就是想知道Spring在实例化时,对象的别名,也就是@Component("userService")注解中userService          
        this.name = name;
    }

    @Override
    public void afterPropertiesSet() {
     
        //实现InitializingBean 接口,初始化,校验spring创建的bean是否创建成功
        //例如:Spring创建UserService时,不希望注入的属性对象orderService为空,否则抛异常
        if(orderService == null){
     
            System.out.println("属性orderService为空,对象UserService创建失败");
        }
    }
}
package com.maker.service;

import com.spring.Component;

@Component("orderService")
public class OrderService {
     
}
package com.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: 手写模拟Spring框架核心逻辑
 * @Author: Maker
 * @Date: 2020/11/5 9:34
 */
public class MakerApplicationContext {
     

    //每次创建好一个bean的定义信息,就存起来,一个文件路径下可能会有多个.class文件,key为bean的名称,value为bean的定义信息
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //就是一个list,存放bean的后置处理器
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    //单例池,存放所有的单例bean
    private ConcurrentHashMap<String, Object> singletonObjectList = new ConcurrentHashMap<>();


    /**
     * Spring 启动,两大核心步骤
     * 1.扫描指定路径下的所有类(扫描的事target下的.class文件)
     * 2.创建实例bean(自动注入)--Spring启动的时候只会实例化非懒加载的单例bean
     *
     * @param configClass
     */
    public MakerApplicationContext(Class configClass) {
     
        /**
         *  扫描类,得到BeanDefinition(里面封装的bean的属性)
         *  依据@ComponentScan("com.maker.service") 注解配置的路径扫描,路径可以为多个
         */
        scan(configClass);

        /**
         *  实例化非懒加载单例bean,分五步执行
         *  1.实例化
         *  2.属性填充
         *  3.Aware回调
         *  4.初始化
         *  5.添加到单例池
         */
        instanceSingletonBean();
    }

    /**
     * Spring在启动的时候,扫描给定路径下的所有.class文件
     *
     * @param configClass
     */
    public void scan(Class configClass) {
     
        /**
         *  1.扫描指定路径下的所有类(扫描的事target下的.class文件)
         *  转化为BeanDefinition对象,最后添加到beanDefinitionMap中
         */
        //先得到扫描路径
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
       //判断是否存在@ComponentScan注解
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String packagePath = componentScanAnnotation.value();
            System.out.println("Spring启动扫描的包路径地址:" + packagePath);
            //扫描包路径得到路径下所有的.class文件
            List<Class> beanClasses = getBeanClasses(packagePath);

            //遍历beanClasses,得到bean的定义BeanDefinition,将bean的部分属性信息封装在BeanDefinition,
            //以便在之后获取的bean的时候,不要再次走一遍扫描 -->实例化的流程了
            for (Class clazz : beanClasses) {
     
                //判断当前bean有没有被@Component注解标识
                if (clazz.isAnnotationPresent(Component.class)) {
     
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setBeanClass(clazz);

                    //一个实例bean,要么是被Spring自动生成,要么是从注解@Component上获取(注解不唯一,这里只举一个例子)
                    Component componentAnnotation = (Component) clazz.getAnnotation(Component.class);
                    //获取注解@Component中标识的bean的名称,例如@Component("userService")形式
                    String beanName = componentAnnotation.value();

                    //添加Bean的后置处理逻辑,Spring在扫描时,将实现BeanPostProcessor接口全部添加到后置处理器集合中
                    /*if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                        try {
                            BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                            beanPostProcessorList.add(instance);
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        }
                    }*/

                    //判断是否有@Scope注解
                    if (clazz.isAnnotationPresent(Scope.class)) {
     
                        Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class);
                        //获取单例或原型的值
                        String scopeValue = scopeAnnotation.value();
                        beanDefinition.setScopeValue(scopeValue);
                    } else {
     
                        //没有则默认是单例
                        beanDefinition.setScopeValue(ScopeEnum.singleton.name());
                    }

                    //判断是否有@Lazy懒加载注解
                    if (clazz.isAnnotationPresent(Lazy.class)) {
     
                        beanDefinition.setLazy(true);
                    }

                    beanDefinitionMap.put(beanName, beanDefinition);
                }
            }

        }
    }

    /**
     * 从指定的路径中获取bean
     * 此类为自己单独模拟获取的,写的比较简单,方便理解
     */
    private List<Class> getBeanClasses(String packagePath) {
     
        List<Class> beanClasses = new ArrayList<>();
        //获取一个类加载器
        ClassLoader classLoader = MakerApplicationContext.class.getClassLoader();
        //通过类加载器获取一个资源(此时是一个文件夹),例如:file:/E:/code/maker/study/maker-spring/target/classes/com/maker/service
        URL resource = classLoader.getResource(packagePath.replace(".", "/"));
        System.out.println("Spring扫描的路径地址:" + resource);
        File file = new File(resource.getFile());
        //判断当前文件是否为一个文件夹
        if (file.isDirectory()) {
     
            for (File f : file.listFiles()) {
     
                //获取.class文件名称
                String fileName = f.getAbsolutePath();
                //由于此文件夹下可能存在其他非.class类型的文件,所以需要判断
                if (fileName.endsWith(".class")) {
     
                    //获取.class文件的对应的类名
                    String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                    //替换
                    className = className.replace("\\", ".");
                    try {
     
                        //通过类加载器加载获取一个对象
                        Class<?> clazz = classLoader.loadClass(className);
                        beanClasses.add(clazz);
                    } catch (ClassNotFoundException e) {
     
                        e.printStackTrace();
                    }
                }
            }
        }
        return beanClasses;
    }

    /**
     * 实例化非懒加载单例bean
     */
    public void instanceSingletonBean() {
     
        //从bean的定义map中获取
        for (String beanName : beanDefinitionMap.keySet()) {
     
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //判断是否为单例
            if (beanDefinition.getScopeValue().equals(ScopeEnum.singleton.name())) {
     
                //是单例,创建单例的bean,并放入单例池中
                Object bean = doCreateBean(beanName, beanDefinition);
                singletonObjectList.put(beanName, bean);
            }
        }
    }

    /**
     * 创建bean
     */
    private Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
     
        //基于bean的定义,也就是BeanDefinition创建bean
        Class beanClass = beanDefinition.getBeanClass();

        try {
     
            /**
             * 1.实例化bean
             */
            Object instance = beanClass.getDeclaredConstructor().newInstance();

            /**
             *  2.属性填充
             *  使用Bean的后置处理器
             */
            //获取实例bean中的所有属性
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
     
                //判断属性中是否有@Autowired 注解注入的
                if (field.isAnnotationPresent(Autowired.class)) {
     
                    //Todo:此处后面在补充先通过byType寻找,在通过byName寻找
                    String fieldName = field.getName();
                    Object bean = getBean(fieldName); //直接通过bean的名字获得bean

                    field.setAccessible(true); //如果取得的field属性使用private的,则必须设置true才能访问,否则会报错
                    field.set(instance, bean);
                }
            }
            /**
             *  Bean后置处理器
             *  例如:UserService中有用@Autowired和@Resource注解注入的属性对象
             *       那么UserService bean实例化好之后,分别处理@Autowired和@Resource
             *       的内容
             *  Spring源码中处理@Autowired是AutowiredAnnotationBeanPostProcessor
             *                @Resource是CommonAnnotationBeanPostProcessor
             *
             */
            /*for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessAfterInitialization(beanName, instance);
            }*/


            /**
             *  3.Aware回调--->判断当前创建的实例bean是否实现了BeanNameAware回调接口
             *  Spring在扫描带@Component注解的类时,会给类赋值一个名称(或者@Component中配置),
             *  此时需要知道bean对应的名称是什么,所以回调获取bean的名称
             */
            if (instance instanceof BeanNameAware) {
     
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            /**
             *  4.初始化,校验spring创建的bean是否创建成功
             *  执行顺序放在实例bean、属性填充、Aware回调之后
             */
            if (instance instanceof InitializingBean) {
     
                ((InitializingBean) instance).afterPropertiesSet();
            }

            return instance;
        } catch (InstantiationException e) {
     
            e.printStackTrace();
        } catch (IllegalAccessException e) {
     
            e.printStackTrace();
        } catch (InvocationTargetException e) {
     
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
     
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取Bean
     */
    public Object getBean(String beanName) {
     
        //创建bean之前,判断一下是否为单例,
        //如果为单例,直接看单例池中是否有此实例bean,如果有直接取出,如果没有新创建一个单例bean,并且放入单例池中
        if (singletonObjectList.containsKey(beanName)) {
     
            return singletonObjectList.get(beanName);
        } else {
     
            //属性bean在单例池中不存在,再去beanDefinitionMap中查询是否存在
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //实例化bean
            return doCreateBean(beanName, beanDefinition);
        }
    }
}

你可能感兴趣的:(#,Spring,开发框架,java,spring)