概述
学习自2025年Java春招手写Spring源码,1000分钟让你彻底搞懂Spring底层原理与源码实现,挑战7天打卡春招上岸!_哔哩哔哩_bilibili
记录手写Spring相关的知识点,本部分主要为手写模拟Spring的Bean的生命周期,包括容器初始化、BeanDefinition扫描、单例和多例Bean、依赖注入、Aware回调、初始化以及BeanPostProcessor
思维导图
基础概念
Spring的生命周期如下:
实例化:Spring容器根据配置文件或注解实例化Bean对象
属性注入:Spring将依赖(通过构造器、setter方法或字段注入)注入到Bean实例中
初始化前的扩展机制:如果Bean实现了BeanNameAware等aware接口,则执行aware注入
初始化前(BeanPostProcessor):在Bean初始化前,通过BeanPostProcessor接口对Bean进行一些额外的处理
初始化:调用InitializingBean接口的afterPropertiesSet方法或通过init-method属性指定的初始化方法
初始化后(BeanPostProcessor):在Bean初始化后,通过BeanPostProcessor接口进行进一步的处理
使用Bean:Bean已初始化完成,可以被容器中的其他Bean使用
销毁:当容器关闭时,Spring调用DisposableBean接口的destroy方法或通过destroy-method属性指定的销毁方法
流程图如下所示:

- 实例化时,会有一个推断构造方法的过程,对于一个Bean,如果没有构造方法,会报错,如果有一个构造方法,Spring会使用这个无参构造方法,如果有多个构造方法,如果多个构造方法中有无参,则Spring用无参,如果没有无参,则报错,因为Spring也不知道会用哪个构造方法
- 推断构造方法中,如果使用的有参构造方法,注入的Bean的注入过程中(单例Bean),先ByType在ByName,即先根据类型去单例池中去找,如果有多个再根据名称去找
- 依赖注入同推断构造方法,先ByType在ByName
- 初始化前,如果Bean对象的方法上有一个注解叫@PostConstruct,那么这个方法会在初始化前运行
- AOP在初始化后这个环节,如果是一个单例Bean,那么放到单例池里的是代理对象,如果不是AOP,那么放到单例池里的就是原本的Bean
手写模拟
初始化
新建一个普通的java项目,创建两个目录,一个service,一个spring,前者测试,后者保存手写的Spring的代码
Spring代码
-
首先即为Spring的容器类,容器类里需要有一个配置类,和对应的构造方法
-
容器类中有一个经典的getBean方法,在此处也先定义上
1 2 3 4 5 6 7 8 9 10 11
| public class CrossacidApplicationContext { private Class configClass; public CrossacidApplicationContext(Class appConfigClass) { this.configClass = appConfigClass; } public Object getBean(String beanName) { return null; } }
|
配置类上要有Bean的扫描路径,故需要写一个注解,来表明需要扫描哪个路径下的Bean
1 2 3 4 5
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ComponentScan { String value() default ""; }
|
那么对应的,就需要标识Bean,此处就定义一个新的注解
1 2 3 4 5 6
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Component {
String value() default ""; }
|
测试代码
配置类如下所示:
1 2 3 4
| @ComponentScan("crossacid.service") public class AppConfig {
}
|
显示扫描crossacid.service这个包下面的Bean
定义一个UserService
1 2 3 4
| @Component("userService") public class UserService{
}
|
将AppConfig这一配置类作为参数传入自定义容器中,并设置一个UserService的getBean测试,此处肯定为null,因为还什么都没有写
1 2 3 4 5 6 7
| public class Test {
public static void main(String[] args) { CrossacidApplicationContext applicationContext = new CrossacidApplicationContext(AppConfig.class); UserService userService = (UserService) applicationContext.getBean("userService"); } }
|
BeanDefinition扫描
Spring代码
Spring扫描Bean时,不会直接创建一个完整的Bean,这样是为了避免循环依赖,因此,首先定义一个BeanDefinition:
1 2 3 4 5 6 7 8 9 10 11
| public class BeanDefinition { private Class type;
public Class getType() { return type; }
public void setType(Class type) { this.type = type; } }
|
为此,需要在容器类CrossacidApplicationContext中添加一个字段,即beanDefinitionMap
1 2 3 4 5
| public class CrossacidApplicationContext { private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); }
|
Spring扫描Bean的步骤如下:
- 获取配置类上的
ComponentScan注解
- 获取
ComponentScan注解上的扫描路径
- 获取容器类的类加载器
- 使用
getResource方法获取当前路径的URL(IDEA默认配置下,此时即编译后的out路径下对应的class文件夹)
- 通过
URL的getFile方法获取文件夹
- 遍历文件夹,针对每个文件,判断是否为.class结尾
- 对于以.class结尾的文件,获取其全类名
- 接下来,需要进一步分析,对应的类是否存在
Component注解
代码添加在CrossacidApplicationContext的初始化方法中,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| public CrossacidApplicationContext(Class appConfigClass) { this.configClass = appConfigClass;
if (configClass.isAnnotationPresent(ComponentScan.class)) { if (configClass.isAnnotationPresent(ComponentScan.class)) { ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class); String path = componentScanAnnotation.value(); path = path.replace(".", "/");
ClassLoader classLoader = CrossacidApplicationContext.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 fileName = f.getAbsolutePath(); if (fileName.endsWith(".class")) { String basePath = "crossacid-spring\\out\\production\\crossacid-spring\\"; String className = fileName.substring(fileName.indexOf(basePath) + basePath.length(), fileName.indexOf(".class")); className = className.replace("\\", "."); System.out.println(className); try { Class<?> clazz = classLoader.loadClass(className); if (clazz.isAnnotationPresent(Component.class)) { Component component = clazz.getAnnotation(Component.class); String beanName = component.value(); if (beanName.equals("")) { beanName = Introspector.decapitalize(clazz.getSimpleName()); } BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setType(clazz); beanDefinitionMap.put(beanName, beanDefinition); } } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } } } } } }
|
单例和多例Bean实例化
Spring代码
单例Bean和多例Bean涉及到Spring的Bean的作用域问题(Scope),此处手写,只简单实现singleton和prototype,前者为单例,即整个IOC容器内部仅此一个,而后者则为多实例。
完整Spring作用域如下引用
singleton:默认,单例,整个IOC容器内部仅此一个
prototype:原型,会存在多个Bean实例
request:每个请求都会新建一个属于自己的Bean实例,这种作用域只存在于Spring Web中
session:一个http session中只有一个bean实例,这种作用域只存在于Spring Web中
application:整个SevelretContext生命周期内,只有一个bean,这种作用域仅存在于Spring Web中
websocket:一个WebSocket生命周期内一个bean实例,这种作用域仅存在于Spring Web中
首先定义一个Scope注解,使用方法同其他自定义注解,不写则默认为单例
1 2 3 4 5
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Scope { String value() default ""; }
|
在Spring的BeanDefinition这块,也需要添加上对应的操作和判断
1 2 3 4 5 6 7 8 9 10 11 12
| BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setType(clazz); if (clazz.isAnnotationPresent(Scope.class)) { Scope scopeAnnotation = clazz.getAnnotation(Scope.class); beanDefinition.setScope(scopeAnnotation.value()); } else { beanDefinition.setScope("singleton"); }
beanDefinitionMap.put(beanName, beanDefinition);
|
为了区分单例Bean和多例Bean,此时需要一个新的属性,单例Bean的Map,同样写在容器类的属性中,代码如下
1
| private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
|
在扫描完BeanDefinition,将所有的BeanDefinition都创建好放到beanDefinitionMap中后,再将所有的单例Bean放到singletonObjects中
1 2 3 4 5 6 7
| for (String beanName : beanDefinitionMap.keySet()) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); if (beanDefinition.getScope().equals("singleton")) { Object bean = createBean(beanName, beanDefinition); singletonObjects.put(beanName, bean); } }
|
其中有个createBean方法,需要手动实现,即创建Bean的方法
1 2 3 4 5 6 7 8 9
| private Object createBean(String beanName, BeanDefinition beanDefinition) { Class clazz = beanDefinition.getType(); try { Object instance = clazz.getConstructor().newInstance(); return instance; } return null; }
|
从而,在获取Bean对象时,即执行getBean时,即可根据作用域来进行区分,步骤如下:
- 获取beanDefinitionMap中的Bean
- 如果为空则报空指针
- 否则获取作用域
- 如果为单例,则去单例的Map进行查找,不是则跳转6
- 如果为空,则创建该单例(实际上在扫描的时候就应该创建过了)并返回
- 如果不是单例,则创建Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public Object getBean(String beanName) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) { throw new NullPointerException(); } else { String scope = beanDefinition.getScope(); if (scope.equals("singleton")) { Object bean = singletonObjects.get(beanName); if (bean == null) { Object o = createBean(beanName, beanDefinition); singletonObjects.put(beanName, o); } return bean; } else { return createBean(beanName, beanDefinition); } } }
|
测试代码
此处可以测试一下多例,修改下UserService的Scope
1 2 3 4 5 6 7
| @Scope("prototype") @Component("userService") public class UserService implements BeanNameAware, InitializingBean, UserInterface{ public void test() { System.out.println(orderService); } }
|
那么就可以修改测试代码
1 2 3 4 5 6 7 8 9 10 11
| public class Test {
public static void main(String[] args) { CrossacidApplicationContext applicationContext = new CrossacidApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("userService")); System.out.println(applicationContext.getBean("userService")); System.out.println(applicationContext.getBean("userService")); System.out.println(applicationContext.getBean("orderService")); } }
|
如果是prototype,那么此时打印的结果应该是不一样的,反之不写这个,或者是singleton的话,则打印的结果应该是一样的
依赖注入
依赖注入是一个很经典的Spring的环节,在Spring中解决依赖注入主要是基于三级缓存的,此处只是简单实现一下
Spring代码
首先是定义一个依赖注入的注解Autowried(类型注入,Spring提供,多个同类型的Bean需要使用@Qualifier指定具体的Bean)
1 2 3 4 5
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Autowired {
}
|
手写这块,主要是在createBean方法中进行实现
首先实例化一个Instace,然后通过反射获取其属性,看看属性上是否有Autowired注解,如果有,则根据那个属性的名称去获取对应的Bean进行注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private Object createBean(String beanName, BeanDefinition beanDefinition) { Class clazz = beanDefinition.getType(); try { Object instance = clazz.getConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { field.setAccessible(true); field.set(instance, getBean(field.getName())); } } return instance; } 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); } return null; }
|
测试代码
新建一个OrderService的Bean
1 2 3
| @Component public class OrderService { }
|
修改原来的UserService,加入一个OrderService的Bean,并创建一个test方法进行测试
1 2 3 4 5 6 7 8 9 10
| @Component("userService") public class UserService {
@Autowired private OrderService orderService;
public void test() { System.out.println(orderService); } }
|
则测试类方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| package crossacid.service;
import crossacid.spring.CrossacidApplicationContext;
public class Test {
public static void main(String[] args) { CrossacidApplicationContext applicationContext = new CrossacidApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService"); userService.test(); } }
|
会发现成功输出了OrderService这个属性的信息
Aware回调
此处的理解为一个扩展的机制,可以在Bean初始化前,获取Bean名称、BeanFactory等容器资源,此处仅实现获取Bean名称的接口
Spring代码
首先写一个BeanNameAware的接口
1 2 3
| public interface BeanNameAware { public void setBeanName(String beanName); }
|
然后再createBean方法中进行补充,如果实现了这个接口,则执行这个接口的setBeanName方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| try { Object instance = clazz.getConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { field.setAccessible(true); field.set(instance, getBean(field.getName())); } }
if (instance instanceof BeanNameAware) { ((BeanNameAware) instance).setBeanName(beanName); } return instance; } catch (Exception e) { throw new Exception(e); }
|
测试代码
让UserService实现这个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Component("userService") public class UserService implements BeanNameAware {
@Autowired private OrderService orderService;
private String beanName;
@Override public void setBeanName(String beanName) { this.beanName = beanName; }
public void test() { System.out.println(orderService); } }
|
初始化
Spring代码
初始化机制,Spring会提供一个InitializingBean的接口,会提供一个afterPropertiesSet方法,首先也写一一个InitializingBean接口
1 2 3
| public interface InitializingBean { public void afterPropertiesSet(); }
|
在createBean中也补充对应逻辑,如果某个Bean实现了InitializingBean接口,则执行它的afterPropertiesSet方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| try { Object instance = clazz.getConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { field.setAccessible(true); field.set(instance, getBean(field.getName())); } }
if (instance instanceof BeanNameAware) { ((BeanNameAware) instance).setBeanName(beanName); } if (instance instanceof InitializingBean) { ((InitializingBean) instance).afterPropertiesSet(); } return instance; } catch (Exception e) { throw new Exception(e); }
|
测试代码
更新UserService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Component("userService") public class UserService implements BeanNameAware, InitializingBean {
@Autowired private OrderService orderService;
private String beanName;
@Override public void setBeanName(String beanName) { this.beanName = beanName; }
@Override public void afterPropertiesSet() { System.out.println("xxxxxxxxxxxxxxxxxxxxxx"); }
public void test() { System.out.println(orderService); } }
|
BeanPostProcessor
BeanPostProcessor这块就和AOP相关了,就是一个后置处理器,Spring就提供了一个BeanPostProcessor接口
Spring代码
首先写一个BeanPostProcessor接口,里面包含两个方法,postProcessBeforeInitialization、postProcessAfterInitialization,一个前置,一个后置,即初始化前和初始化后,其中初始化后的方法一般和AOP关联
1 2 3 4
| public interface BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName); public Object postProcessAfterInitialization(Object bean, String beanName); }
|
针对这个BeanPostProcessor接口,Spring中也有一个单独的Map来存,即在容器类中添加该属性
1
| private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
|
在扫描到实现了这个接口的Bean时,就也会有一些额外的逻辑(这段在容器类的构造方法中),如果实现了这个接口,就会创建一个对应的BeanPostProcessor放到beanPostProcessorList中去
1 2 3 4 5 6 7 8 9 10 11 12 13
| if (clazz.isAnnotationPresent(Component.class)) { if (BeanPostProcessor.class.isAssignableFrom(clazz)) { BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance(); beanPostProcessorList.add(instance); }
beanDefinitionMap.put(beanName, beanDefinition); }
|
那么在creatBean方法中,也需要扩展对应的逻辑,初始化前,执行这个对应的instance的postProcessBeforeInitialization方法,初始化后,执行postProcessAfterInitialization方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| try { Object instance = clazz.getConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { field.setAccessible(true); field.set(instance, getBean(field.getName())); } }
if (instance instanceof BeanNameAware) { ((BeanNameAware) instance).setBeanName(beanName); } for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName); } if (instance instanceof InitializingBean) { ((InitializingBean) instance).afterPropertiesSet(); } for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName); } return instance; } catch (Exception e) { throw new Exception(e); }
|
测试代码
就和实现AOP一样,这块也写一个方法,实现BeanPostProcessor接口,就类似一个切面吧
这里面在初始化前加了一些逻辑,初始化后执行代理类的一些方法
UserService在初始化前时,就会执行createBean中的这一段代码beanPostProcessor.postProcessBeforeInitialization(instance, beanName)因为它符合postProcessBeforeInitialization中的判断,就会打印一个111
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Component public class CrossacidBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { if (beanName.equals("userService")) { System.out.println(111); } return bean; }
@Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (beanName.equals("userService")) { Object proxyInstance = Proxy.newProxyInstance(CrossacidBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("切面逻辑"); return method.invoke(bean, args); } }); return proxyInstance; } return bean; } }
|
如果此处UserService需要实现AOP,那么初始化后,需要返回一个代理类,即执行postProcessAfterInitialization方法,此处的逻辑是创建一个代理类并执行相应方法并返回这个代理类
因为上述使用的是jdk动态代理,所以需要让UserService实现一个接口
1 2 3
| public interface UserInterface { public void test(); }
|
1 2 3 4 5 6 7 8 9 10
| @Component("userService") public class UserService implements UserInterface{
@Autowired private OrderService orderService;
public void test() { System.out.println(orderService); } }
|
那么,此时就要修改测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Test {
public static void main(String[] args) { CrossacidApplicationContext applicationContext = new CrossacidApplicationContext(AppConfig.class);
UserInterface userService = (UserInterface) applicationContext.getBean("userService"); userService.test();
} }
|
就可以看到”切面逻辑“在控制台输出