【Spring 底层原理】手搓一个Spring框架
文章目录
- 准备工作
- Spring 框架到底在干啥?
- 几个概念辨析
- 注解的定义
- 自定义核心注解
- 配置类
- 启动类
- 辅助类
- Spring 容器
- XxxAware 回调机制
- 初始化机制
- 前置、后置处理器
- 完整的容器代码
- 源码下载
最近工作接触到的知识比较底层,因此为了突破瓶颈,彻底搞明白Spring到底帮我们干了些啥。学习了很多关于Spring的知识,看了这么多资料能讲清楚的人不多,我将尝试将Spring的原理和读者讲明白。我们手写一个简单的Spring框架用于搞清楚其核心原理,这个手写框架我把它叫做Summer框架,平行时空的Spring框架。 追求高质量文章需要兄弟萌支持,一键三连!
准备工作
Spring 框架到底在干啥?
不知道大家在使用Spring
时是否会思考这个框架到底发挥了什么作用呢?大家可能背过八股文可能会脱口而出,我知道Spring
帮我们IOC
和AOP
,也就是帮我们依赖注入和支持切面。这个回答很八股,别烦了,老哥告诉你,Spring
框架就是一个HashMap
,各种注解就是各种标记,通过各种注解从而来识别应该将哪些类进行实例化成Bean
然后放入到HashMap
中,当通过@AutoWired
自动注入一个所依赖的实例Bean
时就直接去HashMap
中寻找是否已经被实例化了,如果没有就当场创建并放入到HashMap
中。当然以上过程是Spring
框架IOC
的简单核心逻辑,仅涉及到单例且不涉及到循环依赖。那么AOP
的逻辑是什么呢?其实很简单,就是允许你在原本的Bean
实例方法执行的前后搞一个回调函数,这个回调函数只管被Spring
容器执行,Spring
不管它是如何实现的,由用户自己定义,仅此而已。这就是Spring
框架的核心原理,其他的无非就是缝缝补补解决一些存在的问题,学一学就好了。例如Spring
容器在实例化一个Bean
时有步骤的,第一步怎么样,第二步怎么样,这些步骤连起来就叫做Bean
的生命周期。好了,既然看到了这里,那么接下来正式进入高潮学习,支持高质量原创文章,一键三连哦!
几个概念辨析
Spring
容器是什么?- 依赖注入是什么?
Bean
是什么?- 回调函数是什么?
注解的定义
在讲Spring
框架之前,如何自己定义一个注解必须要了解,Java
中的注解可以写在类上、方法上、属性上、参数上等。其实就是一个标记的作用,注解也可以有参数。可以通过类对象判断是否存在指定的注解,也可以通过类对象拿到指定注解的参数值。这样的话在代码中就能发挥巨大的作用。如下,我就定义了一个性别的注解,这个注解有啥用呢?如果类也有性别的话,那么可以通过在类上加上该注解来注明。
package com.example.log;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.Type)
@Retention(RetentionPolicy.RUNTIME)
public @interface Gender {/*** 默认变量:value*/String value() default "woman"; // 默认参数
}
自定义注解必须使用@interface
来标识,并可以定义变量,这里定义的变量与传统Java类的变量不太一样,看起来更像一个方法;default
用于定义默认值;注意:参数成员只能用基本类型byte,short,char,int,long,float,double,boolean
八种基本数据类型和String、Enum、Class、Annotations
等数据类型,以及这一些类型的数组。我们也称为JDK的元注解。
- @Retention:定义注解的保留策略
@Retention(RetentionPolicy.SOURCE)
,注解仅存在于源码中,在class
字节码文件中不包含@Retention(RetentionPolicy.CLASS)
,默认的保留策略,注解会在class
字节码文件中存在,但运行时无法获得@Retention(RetentionPolicy.RUNTIME)
,注解会在class
字节码文件中存在,在运行时可以通过反射获取到
- @Target:指定被修饰的Annotation可以放置的位置(被修饰的目标)
@Target(ElementType.TYPE)
,接口、类@Target(ElementType.FIELD)
,属性@Target(ElementType.METHOD)
,方法@Target(ElementType.PARAMETER)
,方法参数@Target(ElementType.CONSTRUCTOR)
,构造函数@Target(ElementType.LOCAL_VARIABLE)
,局部变量@Target(ElementType.ANNOTATION_TYPE)
,注解@Target(ElementType.PACKAGE)
,包
一般@Retention设置成RetentionPolicy.RUNTIME即可,@Target一般设置在类或者方法或者属性上。 那么搞清楚了如何写一个自定义注解类后,此时是不是有疑惑,那么注解的逻辑在哪里实现呢?这里简单剧透一下,注解仅仅作为一个标识,当某个方法执行时我们需要在自定义注解逻辑中去判断是否有被这个注解所标识,如果有则具体处理,否则放行;以上你很容易想到使用 AOP切片和拦截器实现。这两个方法的共同点就是在加了注解的方法执行之前可以先进行注解标识判断。那么最简单的就是通过类对象来判断是否存在指定注解和获取指定注解的值。例如:
Class personClass = Person.class;
if (personClass.isAnnotationPresent(Gender.class)) {Gender genderAnnotation = (Gender) personClass.getAnnotation(Gender.class);String gender = genderAnnotation.value();if("woman".equals(gender)){sout("Person这个类上面标记了@Gender()或者@Gender("woman")");}if("man".equals(gender)){sout("Person这个类上面标记了@Gender("man")");}}
了解了注解的自定义之后,我们就可以直接进入到Spring
框架的核心原理篇章了,我们直接手写一个Spring
框架,我把它叫做Summer
框架。通过Summer
框架介绍核心的Spring
框架再逐渐引入Spring
源码,其实思想是一致的,只不过源码使用了一些设计模式,设计上更加完美。你只要创建最简单的Java
工程(甚至都不需要Maven
工程),这里先给出整体的项目结构,如下图:
自定义核心注解
Spring
框架有哪些核心注解呢?因此,我们的Summer
框架也需要这些核心注解,定义四个注解类,自定义注解如下:
// 组件扫描注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {String value() default "";
}
---------------------------------
// 组件注明注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {String value() default "";
}
---------------------------------
// 注明Bean的多例还是单例,默认单例
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {String value() default "singleton";
}
---------------------------------
// 属性注入(依赖注入)注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
}
配置类
首先第一步是编写配置类,配置类需要作为SummerApplicationContext
容器类的参数,配置类中先什么都不用写,只需要扫描两个指定的路径即可。它的作用就是路径扫描,说人话就是扫描给定的路径下的文件是不是class
文件,如果是再判断有没有被Component
注解标注,如果标注了那就需要自动注入到容器中。那么先剧透这些,具体的稍后揭晓。很简单配置类如下:
@ComponentScan("org.cheney.service,org.cheney.config")
public class AppConfig {
}
启动类
写完了配置类,那么就可以将配置类传参给容器对象,这样的话容器就能自动去扫描AppConfig.class
类上面注解指定的路径。SummerApplicationContext
是需要我们自己定义的容器,模仿了Spring
容器底层的核心,所有的一切都是为这个类服务的,下面代码是启动类,只是给出了用法,它的用法和Spring 的 SpringApplicationContext
一致。传入配置类即可。使用方法如下:
public class SummerApplication {public static void main(String[] args) {SummerApplicationContext summerApplicationContext =new SummerApplicationContext(AppConfig.class); }
}
全篇都是围绕如何设计SummerApplicationContext
类来揭露Spring
的底层原理,因此我们后面基本上都是围绕它来详细介绍的。除此之外,我们先造一些需要的类来展示Spring
容器的机制。
辅助类
我们首先定义一个User
类,这个类没有什么不同,为了让这个类能够被我们后续设计的容器统一管理起来,所以需要使用@Component
注解一下,另外还可以选择使用@Scope
定义容器是单例持有该类还是多例。代码如下:
@Component("user")
//@Component
@Scope("prototype")
//@Scope("singleton")
public class User {
}
再定义一个OrderService
类和UserService
类,其中OrderService
类是UserService
类的一个依赖属性,因此需要通过@Autowired
注解进行注入,并且在UserService
中还调用了OrderService
的一个方法。如下:
@Component
public class OrderService {void pong(){System.out.println("Ping Pong,I am a OrderService!");}
}-----------------------------------------------------------------@Component
public class UserService {@Autowiredprivate OrderService orderService; public void pingOrderService(){orderService.pong();}
}
好了,目前的话辅助类和需要的注解我们都准备的差不多了。在这里,我需要明确一下我们的目标是什么!我们希望有个框架可以帮我们统一管理所有使用@Componet
注解标注了类的Bean
,并且能够自动注入被@Autowired
注释标记的依赖,这两个就是IOC的能力。另外,我们还希望我们设计的框架支持在实例化Bean
的前后能够执行一下程序员预设的方法,程序员只需要告诉框架在实例化某个Bean
前后应该执行那个方法,框架会自动执行。这个就是Spring
所谓的生命周期。目前以上两点是非常核心的能力,顺带一些Spring
简单的功能我们也介绍一下。所以,下面直接开始手撸Spring
框架——Summer
框架的开始!
Spring 容器
到现在了,请问什么叫做Spring
容器?如果回答不出那么也不要烦了,Spring
容器就是维护了一个HashMap
的类,只不过在源码中是线程安全的ConcurrentHashMap
。容器当然是用来存放东西的,Spring
容器存放的是Bean
,什么是Bean
?Bean
就是被@Component
注解标注了的类的对象,例如上面我们创建的辅助类User
,User
类被容器创建的实例就是一个Bean
,你自己new
出来的对象没有被容器管理,不能称为Bean
。容器负责创建,然后放入HashMap
,使用、销毁等对Bean
的操作,当然还有其他更多的生命周期管理。综上所述,我们的容器应该具备以下功能:
- 在容器类的构造方法中就扫描所有的类,并处理。
- 有一个
ConcurrentHashMap
用于存放Bean
。 - 可以根据扫描到的信息创建
Bean
,并判断属性是否有@Autowired
注解标注,如果有就需要进行属性依赖注入。 - 接受一个配置类,根据配置类的扫描路径自动扫描
@Componet
注解的类并实例化到ConcurrentHashMap
中。 - 可以通过
Bean
的名字直接从ConcurrentHashMap
中获取Bean
。
通过上面分析,我们的容器类的大概逻辑应该如下所示:
public class SummerApplicationContext2 {private Class configClass;private ConcurrentHashMap<String, Object> singletonBeanMap =new ConcurrentHashMap<>();public SummerApplicationContext2(Class configClass) {this.configClass = configClass;// 判断是否存在@ComponentScan注解if (configClass.isAnnotationPresent(ComponentScan.class)) {ComponentScan scanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);// 获取注解中给定的扫描路径的值String pathValue = scanAnnotation.value(); // org.cheney.serviceFile file = new File(pathValue);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {1. 解析每一个.class文件,并且进行实例化得到Bean2. 获取到beanName或者使用默认类名第一个字母小写3. singletonBeanMap.put(beanName,Bean); 将Bean存放在Map中}}} }private Object createBean(...) { // 根据类路径创建Beanreturn bean;}public Object getBean(String beanName) {// 单例对象通过beanName直接从Map中获取Bean对象// 多例,通过beanName如何创建Bean呢?return 名字为beanName的Bean;}
}
上面给出的伪代码基本上讲清楚了容器的基本原理,只是很多细节我没有去实现,其实上面的伪代码是有问题的。因为,在扫描路径的时候,一旦发现了有@Component
的.class
文件就进行了实例化,并放入了Map
中。其实这一步做的太早了,还记得我们有一个getBean(String beanName)
方法吗?如果获取的Bean是一个多例,也就是每一次调用getBean
方法都返回不同的对象Bean,那这个应该如何实现呢?因此,在扫描路径时不能太早的实例化Bean
,因为只有单例Bean
才可以在这里实例化,多例的话不能实例化。因此在扫描路径时一旦发现了有@Component
的.class
文件我们就先记录下来,也就是先把需要类的详细信息记载小本本上,如果调用getBean
发现是多例(也就是Map中没有beanName的对象)就通过beanName
从小本本上拿到类的详细信息,从而可以有的放矢地创建新的对象。那么很明显这个小本本也是一个HashMap
结构,在Spring源码中叫做BeanDefinition
。我们给出BeanDefinition
类的定义:
public class BeanDefinition {private Class type;private String scope;public BeanDefinition(){}public BeanDefinition(Class type, String scope) {this.type = type;this.scope = scope;}public Class getType() {return type;}public void setType(Class type) {this.type = type;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}
}
BeanDefinition
的目的仅仅是记录扫描到的类的详细信息,这里给出的定义是简化后的定义。这里只有class
和scope
连个属性,一旦提供了class
,就拿到了其字节码文件。拿到了字节码就可以为所欲为,通过它进行反射,包括创建实例等。而scope
则记录了是单例还是多例。因此我们的代码需要修改成如下的代码(详细的代码最后给出!):
public class SummerApplicationContext2 {private Class configClass;private ConcurrentHashMap<String, Object> singletonBeanMap =new ConcurrentHashMap<>();private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<>();public SummerApplicationContext2(Class configClass) {this.configClass = configClass;// 判断是否存在@ComponentScan注解if (configClass.isAnnotationPresent(ComponentScan.class)) {ComponentScan scanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);// 获取注解中给定的扫描路径的值String pathValue = scanAnnotation.value(); // org.cheney.serviceFile file = new File(pathValue);if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {0. 生成 beanName1. 解析每一个.class文件,获取class和scope两个属性2. 创建该类的一个 beanDefinition 对象。3. beanDefinitionMap.put(beanName,beanDefinition); 将beanDefinition存放在Map中}}}// 将beanDefinition中的Singleton进行实例化for (String beanName : beanDefinitionMap.keySet()) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if ("singleton".equals(beanDefinition.getScope())) {Object bean = createBean(beanName, beanDefinition);singletonBeanMap.put(beanName, bean);}}}private Object createBean(String beanName, BeanDefinition beanDefinition) {1. 根据 beanDefinition 创建Beanreturn bean;}public Object getBean(String beanName) {1. 通过beanName直接从singletonBeanMap中获取Bean对象2. 如果Map中没有(多例情况),则获取beanDefinition, 调用 createBean 使用 beanDefinition 创建Bean对象return bean;}
}
恭喜你,简单的Spring
框架基本上成型了。我们需要做的就是修修补补,把一些重要的有用的细节完善、例如回调方法(XxxAware机制)、前置处理器、初始化方法、后置处理器等完善即可。详细的代码会在后面给出!
XxxAware 回调机制
关于XxxAware回调机制,不懂的同学可以参考一下我以前的一篇博客BeanFactoryAware 机制简介,XxxAware机制提供了一个接口,你需要什么东西只需要实现对应的接口就可以拿到。因为在容器类中会自动判断这个类是否实现了对应的接口,如果实现了就会调用setXxx的方法,将Xxx对象回调回去。这里我再进行详细介绍一下实现原理,假设我们需要让@Component
注解了的类可以获取到自己的Bean在容器中的名字,请问你会如何实现?Spring
的做法就是上面的原理,首先提供一个BeanNameAware
接口,代码如下:
public interface BeanNameAware {void setBeanName(String beanName);
}
然后假设User
对象需要获取自己在容器中的名字,只需要实现该接口即可:
@Component("superAdmin")
public class User implements BeanNameAware{private String beanName;@Overridepublic void setBeanName(String beanName) {this.beanName = beanName;}public String getBeanName(){return beanName;}
}
目前并没有什么卵用,需要在容器中判断是否实现了BeanNameAware
接口,然后将beanName
通过回调方法setBeanName()
被动设置。就是在容器类的createBean()
方法中实现,代码如下:
private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz = beanDefinition.getType();try {Object bean = clazz.getConstructor().newInstance();// 实现依赖注入,通过反射获取属性,判断属性是否有Autowired注解Field[] fields = clazz.getDeclaredFields();for (Field f : fields) {if (f.isAnnotationPresent(Autowired.class)) {f.setAccessible(true);f.set(bean, getBean(f.getName()));}}// BeanNameAware 回调if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);} return bean;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}
只要我们再容器中提供了BeanNameAware
的回调逻辑,那么只需要实现对应的接口就可以获取到容器中才能获取到的对象。非常灵活,其实在Spring
中可以通过实现BeanFactoryAware
接口来获得当前BeanFactory
,也可以通过实现ApplicationContextAware
接口来获取到对应的上下文容器。其实实现都是这个逻辑!
初始化机制
与上面的XxxAware
回调机制很类似,XxxAware
回调机制是通过判断是否实现指定接口然后回调将参数赋值回去,其接口方法只需要接住传回来的参数即可。初始化机制也是回调,但是并不需要传参,也就是不需要赋值回去。容器只是调用指定的初始化方法,初始化方法的内容是程序员自己实现的,容器并不关心。如下我们先实现初始化接口:
public interface InitializingBean {public void afterPropertiesSet();
}
其中有一个初始化的方法叫做 afterPropertiesSet()
,从名字可以看出这个方法是在依赖注入或者说属性设置完毕之后执行的,所以在容器中的实现我们需要注意一下位置。然后如果说User
需要初始化,只需要实现该接口既可,如下:
@Component("superAdmin")
public class User implements BeanNameAware, InitializingBean {private String beanName;@Overridepublic void setBeanName(String beanName) {this.beanName = beanName;}public String getBeanName(){return beanName;}@Overridepublic void afterPropertiesSet() {// 执行其他初始化操作 ...System.out.println("super admin 执行初始化方法!");}
}
在容器中,我们需要判断是否实现了InitializingBean
接口,然后调用afterPropertiesSet()
方法。加上这部分逻辑的createBean()
方法代码如下:
private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz = beanDefinition.getType();try {Object bean = clazz.getConstructor().newInstance();// 实现依赖注入Field[] fields = clazz.getDeclaredFields();for (Field f : fields) {if (f.isAnnotationPresent(Autowired.class)) {f.setAccessible(true);f.set(bean, getBean(f.getName()));}}// Aware 回调if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);} // 初始化,在所有属性设置完成后执行!if (bean instanceof InitializingBean) {((InitializingBean) bean).afterPropertiesSet();}return bean; } catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}
前置、后置处理器
在 Spring
中,BeanPostProcessor
是一个非常有用的接口,它可以在 Spring
容器初始化 Bean
的前后进行一些自定义的处理。千万注意一点,BeanPostProcessor
是面向所有的Bean
的,也就是说如果不加限制只需要实现了BeanPostProcessor
接口,就会对所有的Bean
的初始化前后都进行自定义的增强。以下是关于如何实现BeanPostProcessor
的详细介绍。首先定义BeanPostProcessor
接口如下:
public interface BeanPostProcessor {public void postProcessorBeforeInitialization(String beanName,Object bean);public void postProcessorAfterInitialization(String beanName,Object bean);
}
然后我们实现该接口就会导致所有的Bean的初始化前后都会执行对应的两个方法,例如我们让AppBeanPostProcessor
实现BeanPostProcessor
接口,为了避免所有的Bean
初始化都执行前后置方法,我们需要在方法中进行判断,只有User
类才执行。代码如下:
@Component
public class AppBeanPostProcessor implements BeanPostProcessor {@Overridepublic void postProcessorBeforeInitialization(String beanName, Object bean) {if(bean instanceof Admin){System.out.println(beanName+" before ..." + bean);}}@Overridepublic void postProcessorAfterInitialization(String beanName, Object bean) {if(bean instanceof Admin){System.out.println(beanName+" after ..." + bean);}}
}
到这里其实,写完了都不会执行,因为没有在容器类中编写对应的逻辑。BeanPostProcessor
接口不是只能实现一次,可以实现多次,因此我们需要在容器中记录所有的BeanPostProcessor
,然后在创建Bean
初始化前后全部拿出来都调用一遍,省略其他的代码,增加的代码如下:
// 在容器的构造方法中增加如下代码
private ArrayList<BeanPostProcessor> beanPostProcessorList =new ArrayList<>();
public SummerApplicationContext(Class configClass) {// 前置 后置 处理器if (BeanPostProcessor.class.isAssignableFrom(clazz)) {BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();beanPostProcessorList.add(instance);}}
到现在,可以给出完整的createBean()
代码,如下所示,注意在初始化前后有我们的前置和后置方法的调用。
private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz = beanDefinition.getType();try {Object bean = clazz.getConstructor().newInstance();// 实现依赖注入Field[] fields = clazz.getDeclaredFields();for (Field f : fields) {if (f.isAnnotationPresent(Autowired.class)) {f.setAccessible(true);f.set(bean, getBean(f.getName()));}}// Aware 回调if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}// BeanPostProcessor 初始化前,前置处理器for (BeanPostProcessor processor : beanPostProcessorList) {processor.postProcessorBeforeInitialization(beanName,bean);} // 初始化if (bean instanceof InitializingBean) {((InitializingBean) bean).afterPropertiesSet();}// BeanPostProcessor 初始化后,后置处理器for (BeanPostProcessor processor : beanPostProcessorList) {processor.postProcessorAfterInitialization(beanName,bean);}return bean;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}
完整的容器代码
package org.cheney.summer;import org.cheney.summer.annotation.Autowired;
import org.cheney.summer.annotation.Component;
import org.cheney.summer.annotation.ComponentScan;
import org.cheney.summer.annotation.Scope;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.concurrent.ConcurrentHashMap;/*** Hello world!*/
public class SummerApplicationContext {private Class configClass;private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<>();private ConcurrentHashMap<String, Object> singletonBeanMap =new ConcurrentHashMap<>();private ArrayList<BeanPostProcessor> beanPostProcessorList =new ArrayList<>();public SummerApplicationContext(Class configClass) {this.configClass = configClass;if (configClass.isAnnotationPresent(ComponentScan.class)) {ComponentScan scanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);String pathValue = scanAnnotation.value(); // org.cheney.serviceString[] paths = pathValue.split(",");for (String path : paths) {path = path.replace(".", "/");ClassLoader classLoader = SummerApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(path);File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String absolutePath = f.getAbsolutePath();if (absolutePath.endsWith(".class")) {// 判断目录下的.class文件是否有 @Component注解String className = absolutePath.substring(absolutePath.indexOf("org"), absolutePath.indexOf(".class"));className = className.replace("\\", ".");
// System.out.println(className);try {Class<?> clazz = classLoader.loadClass(className);if (clazz.isAnnotationPresent(Component.class)) {// 扫描到了 @Component 注解,生成一个 BeanDefinition 对象BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(clazz);if (clazz.isAnnotationPresent(Scope.class)) {String value = clazz.getAnnotation(Scope.class).value();beanDefinition.setScope(value);} else {beanDefinition.setScope("singleton");}String beanName = clazz.getAnnotation(Component.class).value();if ("".equals(beanName)) {
// beanName = getBeanName(clazz);beanName = Introspector.decapitalize(clazz.getSimpleName());}beanDefinitionMap.put(beanName, beanDefinition);// 前置 后置 处理器if (BeanPostProcessor.class.isAssignableFrom(clazz)) {BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();beanPostProcessorList.add(instance);}}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}}}// 将beanDefinition中的Singleton进行实例化for (String beanName : beanDefinitionMap.keySet()) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if ("singleton".equals(beanDefinition.getScope())) {Object bean = createBean(beanName, beanDefinition);singletonBeanMap.put(beanName, bean);}}}private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz = beanDefinition.getType();try {Object bean = clazz.getConstructor().newInstance();// 实现依赖注入Field[] fields = clazz.getDeclaredFields();for (Field f : fields) {if (f.isAnnotationPresent(Autowired.class)) {f.setAccessible(true);f.set(bean, getBean(f.getName()));}}// Aware 回调if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}// BeanPostProcessor 初始化前,前置处理器for (BeanPostProcessor processor : beanPostProcessorList) {processor.postProcessorBeforeInitialization(beanName,bean);}// 初始化if (bean instanceof InitializingBean) {((InitializingBean) bean).afterPropertiesSet();}// BeanPostProcessor 初始化后,后置处理器for (BeanPostProcessor processor : beanPostProcessorList) {processor.postProcessorAfterInitialization(beanName,bean);}return bean;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}public Object getBean(String beanName) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (null == beanDefinition) {throw new NullPointerException("Summer容器中不存在叫做" + beanName + "的Bean");}if (beanDefinition.getScope().equals("singleton")) {Object bean = singletonBeanMap.get(beanName);if (null == bean) {bean = createBean(beanName, beanDefinition);singletonBeanMap.put(beanName, bean);}return bean;} else {return createBean(beanName, beanDefinition);}}
}
源码下载
【手写Spring框架】summer 源码。支持高质量中文博客,嘿嘿嘿,一键三连,支持知识免费分享!参考内容:B站周瑜,小傅哥。