最近工作接触到的知识比较底层,因此为了突破瓶颈,彻底搞明白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 {
@Autowired
private 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.service
File file = new File(pathValue);
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
1. 解析每一个.class文件,并且进行实例化得到Bean
2. 获取到beanName或者使用默认类名第一个字母小写
3. singletonBeanMap.put(beanName,Bean); 将Bean存放在Map中
}
}
}
}
private Object createBean(...) {
// 根据类路径创建Bean
return 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.service
File file = new File(pathValue);
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
0. 生成 beanName
1. 解析每一个.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 创建Bean
return 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;
@Override
public 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;
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
public String getBeanName(){
return beanName;
}
@Override
public 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 {
@Override
public void postProcessorBeforeInitialization(String beanName, Object bean) {
if(bean instanceof Admin){
System.out.println(beanName+" before ..." + bean);
}
}
@Override
public 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.service
String[] 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站周瑜,小傅哥。