通过手写模拟,了解Spring的底层源码启动过程,了解BeanDefinition、BeanPostProcessor的概念,了解Spring解析配置类等底层源码工作流程,通过手写模拟,了解依赖注入,Aware回调等底层源码工作流程,通过手写模拟,了解Spring AOP的底层源码工作流程
当然,代码实现很粗糙,目的是为了更好的廖家spring底层bean加载的过程
项目地址:https://gitee.com/fanzitianxing/write-spring
注:此为maven项目,项目中额pom.xml不依赖任何jar包,所有的注解实例都是自己定义写
相关注解类:
@Retention(RetentionPolicy.RUNTIME) //注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 @Target({ElementType.FIELD,ElementType.METHOD}) public @interface Autowired { //此注解为Spring 自动注入 boolean required() default true; } ----------------------------------------------------------------------------------------- @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) /** * 对应spring中@Component注解 作用就是把普通pojo实例化到spring容器中,相当于配置文件中的 * <bean id="" class=""/>) */ public @interface Component { String value() default ""; } ----------------------------------------------------------------------------------------- @Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 @Target(ElementType.TYPE) /** * 对应spring中@ComponentScan注解 作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中 */ public @interface ComponentScan { String value() default ""; } ---------------------------------------------------------------------------------------- @Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 @Target(ElementType.TYPE) /** * 对应spring中@Lazy注解 作用就是指定bean是否是懒加载 * */ public @interface Lazy { } ------------------------------------------------------ @Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 @Target(ElementType.TYPE) /** * 对应spring中@Scope注解 作用就是指定bean的作用域 基本作用域singleton(单例)、prototype(多例),Web 作用域(reqeust、session、globalsession),自定义作用域 * */ public @interface Scope { String value() default ""; } ----------------------------------------------------------- /** * @author fanzitianxing * @title: FztxValue * @projectName write-spring * @description: TODO * @date 2021/9/722:44 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface FztxValue { String value() default ""; }
相关接口类:
/** * 用于实例化后的回调 */ public interface BeanNameAware { void setBeanName(String beanName); } ---------------------------------------------------- /** *Bean后置处理器,Spring容器在初始化bean的时候,会回调BeanPostProcessor中的两个方法 * @author fanzitianxing * @date 2021/9/7 * @param * @return */ public interface BeanPostProcessor { /** *初始化前 * @author fanzitianxing * @date 2021/9/7 * @param [bean, beanName] * @return java.lang.Object */ default Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } /** *初始化后 * @author fanzitianxing * @date 2021/9/7 * @param [bean, beanName] * @return java.lang.Object */ default Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } } -------------------------------- public interface InitializingBean { void afterPropertiesSet(); }
Spring 启动的时候都做了什么?
1)扫描
Spring在启动的时候,会去加载配置类AppConfig是否有通过@ComponentScan注解指定扫描路径范围,并且扫描的是target目录下的.class文件,并且转化为BeanDefinition对象,最后添加到beanDefinitionMap中,同时如果是BeanPostProcessor接口的实现类会存放到bean的后置器list中,之后在初始化前和初始化后使用
(2)实例化
实例化就是创建实例的bean,并不是目录下所有的.class文件都会实例化,只有非懒加载的单例Bean才会被实例化,如果一个类上添加了@Scope(“prototype”)或者@Lazy 注解时,那么Spring启动时是不会实例化该对象的,只有在使用该对象的时候才会实例化
1.实例化
表示从target目录下获取所有非懒加载的单例Bean,并且为了后续流程不在走同样的实例流程,每次实例一个,就放入一个ConcurrentHashMap<String, BeanDefinition>集合中存起来,key为bean的名称,value为bean的定义类,里面描述了这个bean的一些信息,如是否为单例,是否为懒加载,bean的实例类型等等信息
2.属性填充
补充BeanDefinition信息,并且此处涉及注入依赖的问题,如果OrderService通过注解@Autowired注入到UserService 中,是先通过ByType类型去匹配,在通过ByName去寻找
问:@Autowired 为什么是先ByType后ByName?
解:因为OrderService上被注解@Component(“orderService”),并且配置的别名是orderService,而别名是随意取得,其他的类也可以叫这个,所有在ConcurrentHashMap<String, BeanDefinition>容器中可能存在实例名称相同,但是对应的实例Bean对象不一样,如果先通过ByName去寻找,可能会匹配到多个实例Bean对象
补充:@Resource注解,是直接通过ByName匹配的
3.初始化前
遍历BeanPostProcessor接口实现类list,执行postProcessBeforeInitialization方法在初始化前做增强处理
4.Aware回调
实现BeanNameAware接口中的setBeanName方法,获取Spring实例bean对象的别名
5.初始化
实现InitializingBean接口的afterPropertiesSet方法,表示在一个bean对象实例完成时,校验一下是否创建成功,例如:Spring创建UserService时,不希望注入的属性对象orderService为空,否则抛异常
5.添加到单例池
如果实例的对象是单例,则添加到创建的单例池中,以便后面使用的时候不需要再次实例化以便,直接从单例池中获取(这里比源码中的要简单一些,便于理解)
6.初始化后
遍历BeanPostProcessor接口实现类list,执行postProcessAfterInitialization方法在初始后前做增强处理,springAop就是在这里实现的
package com.spring; import java.beans.Introspector; import java.io.File; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class FztxApplicationContext { private Class configClass; //每次创建好一个bean的定义信息,就存起来,一个文件路径下可能会有多个.class文件,key为bean的名称,value为bean的定义信息 private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>(); //单例池,存放所有的单例bean private Map<String,Object> singleObjects = new HashMap<>(); //就是一个list,存放bean的后置处理器 private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>(); /** * Spring 启动,两大核心步骤 * 1.扫描指定路径下的所有类(扫描的事target下的.class文件) * 2.创建实例bean(自动注入)--Spring启动的时候只会实例化非懒加载的单例bean * * @param configClass */ public FztxApplicationContext(Class configClass) { this.configClass = configClass; /** * 扫描类,得到BeanDefinition(里面封装的bean的属性) * 依据@ComponentScan("com.fztx.service") 注解配置的路径扫描,路径可以为多个 */ scan(configClass); /** * 实例化非懒加载单例bean,分五步执行 * 1.实例化 * 2.属性填充 * 3.初始化前 * 4.Aware回调 * 5.初始化 * 6.初始化后 * 7.添加到单例池 */ instanceSingletonBean(); } /** * 实例化非懒加载单例bean */ private void instanceSingletonBean() { for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) { String beanName = entry.getKey(); BeanDefinition beanDefinition = entry.getValue(); //判断是否是非懒加载单例bean if (beanDefinition.getScopeValue().equals("singleton")) { //创建单例bean Object bean = doCreateBean(beanName, beanDefinition); //放到单例池中,getbean方法时单例bean可以直接到单例池中取 singleObjects.put(beanName,bean); } } } /** * 创建bean */ private Object doCreateBean(String beanName, BeanDefinition beanDefinition) { //基于bean的定义,也就是BeanDefinition创建bean Class clazz = beanDefinition.getBeanClass(); Object instance = null; try { /** * 1.实例化bean * 这里取了无参构造方法,没有实现推断构造方法 */ instance = clazz.getConstructor().newInstance(); /** * 2.属性填充 * */ //获取实例bean中的所有属性 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { //判断属性中是否有@Autowired 注解注入的 if (field.isAnnotationPresent(Autowired.class)) { //此处后面在补充先通过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 * */ /** * 我们可以自己实现BeanPostProcessor接口去实现我们自己的功能 * bean后置处理器的两个方法可以进行扩展实现我们的需求 */ /** * 3.初始化前--->遍历bean的后置处理器list,执行postProcessBeforeInitialization初始化前操作 * */ for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName); } /** * 4.Aware回调--->判断当前创建的实例bean是否实现了BeanNameAware回调接口 * Spring在扫描带@Component注解的类时,会给类赋值一个名称(或者@Component中配置), * 此时需要知道bean对应的名称是什么,所以回调获取bean的名称 */ if (instance instanceof BeanNameAware) { ((BeanNameAware) instance).setBeanName(beanName); } /** * 5.初始化,校验spring创建的bean是否创建成功 * 执行顺序放在实例bean、属性填充、Aware回调之后 */ if (instance instanceof InitializingBean) { ((InitializingBean) instance).afterPropertiesSet(); } /** * 5.初始化前--->遍历bean的后置处理器list,执行postProcessAfterInitialization初始化后操作 * aop就是在这个地方实现 */ for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName); } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return instance; } /** * Spring在启动的时候,扫描给定路径下的所有.class文件 * * @param configClass */ private void scan(Class configClass) { /** * 1.扫描指定路径下的所有类(扫描的是target下的.class文件) * 转化为BeanDefinition对象,最后添加到beanDefinitionMap中 */ //先得到扫描路径 if (configClass.isAnnotationPresent(ComponentScan.class)) { //判断是否存在@ComponentScan注解 //存在的话根据注解value的值去对应路径扫描class文件 ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class); String path = componentScanAnnotation.value(); System.out.println("Spring启动扫描的包路径地址:" + path); //扫描包路径得到路径下所有的.class文件 List<Class> beanClasses = getBeanClasses(path); //遍历beanClasses,将bean的部分属性信息封装在BeanDefinition, //以便在之后获取的bean的时候,不要再次走一遍扫描 -->实例化的流程了 for (Class clazz : beanClasses) { //判断当前bean有没有被@Component注解标识,只有加了@Component注解的类才加载到容器中 if (clazz.isAnnotationPresent(Component.class)) { //添加Bean的后置处理逻辑,Spring在扫描时,将实现BeanPostProcessor接口全部添加到后置处理器集合中 if (BeanPostProcessor.class.isAssignableFrom(clazz)) { BeanPostProcessor instance = null; try { instance = (BeanPostProcessor) clazz.getConstructor().newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } beanPostProcessorList.add(instance); }else{ //一个实例bean,要么是被Spring自动生成,要么是从注解@Component上获取(注解不唯一,这里只举一个例子) Component componentAnnotation = (Component) clazz.getAnnotation(Component.class); //获取注解@Component中标识的bean的名称,例如@Component("userService")形式 String beanName = componentAnnotation.value(); //如果Component没有设置beanName,spring自动生成,首字母小写 if ("".equals(beanName)){ beanName = Introspector.decapitalize(clazz.getSimpleName());//spring底层自动生成beanname } //创建bean定义对象并设置属性 BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setBeanClass(clazz); //判断是否有@Scope注解 if (clazz.isAnnotationPresent(Scope.class)) { Scope scopeAnnotation = (Scope) clazz.getAnnotation(Scope.class); //获取单例或原型的值 String scopeValue = scopeAnnotation.value(); beanDefinition.setScopeValue(scopeValue); }else{ //没有则默认是单例 beanDefinition.setScopeValue("singleton"); } //判断是否有@Lazy懒加载注解 if (clazz.isAnnotationPresent(Lazy.class)) { beanDefinition.setLazyValue(true); } beanDefinitionMap.put(beanName,beanDefinition); } } } } } /** * 从指定的路径中获取bean * 此类为自己单独模拟获取的,写的比较简单,方便理解 */ private List<Class> getBeanClasses(String path) { List<Class> beanClasses = new ArrayList<>(); //获取一个类加载器 ClassLoader classLoader = FztxApplicationContext.class.getClassLoader(); //根据类加载器获取指定路径的资源,传入相对路径,得到的是文件夹 file:/E:/TuLing/code/write-spring/target/classes/com/fztx/service URL resource = classLoader.getResource(path.replace(".", "/")); System.out.println("Spring扫描的路径地址:" + resource); File file = new File(resource.getFile()); if (file.isDirectory()) { for (File f : file.listFiles()) { //得到class的文件路径+名字 E:\TuLing\code\write-spring\target\classes\com\fztx\service\UserService.class String absolutePath = f.getAbsolutePath(); //由于此文件夹下可能存在其他非.class类型的文件,所以需要判断 if (absolutePath.endsWith(".class")) { //classloader加载类需要class的包+名 //获取.class文件的对应的类名 String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class")); //替换 className = className.replace("\\", "."); //通过类加载器加载获取一个对象 try { Class<?> clazz = classLoader.loadClass(className); beanClasses.add(clazz); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } return beanClasses; } /** * 获取Bean */ public Object getBean(String beanName){ if (!beanDefinitionMap.containsKey(beanName)) { throw new NullPointerException(); } BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); //创建bean之前,判断一下是否为单例, //如果为单例,直接看单例池中是否有此实例bean,如果有直接取出,如果没有新创建一个单例bean,并且放入单例池中 if (beanDefinition.getScopeValue().equals("singleton")) { Object bean = singleObjects.get(beanName); if(bean == null){ bean = doCreateBean(beanName, beanDefinition); singleObjects.put(beanName,bean); } return bean; }else{ //原型bean,根据beanDefinition去新创建一个bean Object bean = doCreateBean(beanName, beanDefinition); return bean; } } }