当前位置: 首页 > news >正文

手写简易Spring框架

手写简易Spring框架:从零实现IoC容器

在这篇博客中,我将分享如何从零开始实现一个简易的Spring框架核心功能,包括IoC容器、依赖注入和组件扫描等核心特性。

项目结构概述

这个简易Spring框架主要包含以下几个核心包:

  • my.self.spring.annotation:自定义注解
  • my.self.spring.BeanFactory:Bean工厂实现
  • my.self.spring.ApplicationContext:应用上下文实现
  • my.self.spring.Definition:Bean定义相关类
  • my.self.spring.proxy:代理相关实现

核心功能实现

1. 注解定义

首先定义了Spring中常见的几个核心注解:

// 组件扫描注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {String[] value();
}// 组件注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {String value() default "";
}// 自动装配注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
}// 作用域注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {String value() default "singleton";
}

2. Bean工厂实现

DefaultListableBeanFactory是整个框架的核心,负责Bean的创建、管理和依赖注入:

package my.self.spring.BeanFactory;import my.self.spring.Definition.AnnotateBeanDefinition;
import my.self.spring.Definition.AnnotateGenericBeanDefinition;
import my.self.spring.Definition.BeanDefinition;
import my.self.spring.Definition.BeanDefinitionRegistry;
import my.self.spring.Function.ObjectFactory;
import my.self.spring.annotation.*;
import my.self.spring.proxy.JdkProxyBeanPostProcess;
import my.self.spring.uitls.StringUtils;import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;import static my.self.spring.emun.ScopeType.SINGLETON;/*** @Description:* @author: zh* @Create : 2025/6/26* @Project_name : SelfsSpring* @Version :**/
public class DefaultListableBeanFactory implements BeanFactory, BeanDefinitionRegistry {private final Map<String, AnnotateBeanDefinition> definitionMap= new ConcurrentHashMap<>(256);/*** Bean name 集合*/private final List<String> beanDefinitionNames = new ArrayList<>();/*** 一级缓存*/private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/*** 二级缓存*/private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(256);//三级缓存private final Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>(256);//三级缓存的循环对象private final Set<String> singletonCurrentlyInCreation =new HashSet<>(256);/*** 获取bean根据BeanName** @param beanName* @return*/public Object getBean(String beanName) {return doGetBean(beanName);}private Object doGetBean(String beanName) {Object bean = getSingletonObjects(beanName);if(bean != null){return bean;}//获取BeanDefinitionAnnotateGenericBeanDefinition annotateBeanDefinition = (AnnotateGenericBeanDefinition) definitionMap.get(beanName);//实例化beanbean = createBean(beanName,annotateBeanDefinition.getClazz());if (annotateBeanDefinition.getScope().equals(SINGLETON)) {//是单例的则会添加到singletonObjects中singletonObjects.put(beanName,bean);}return bean;}private Object getSingletonObjects(String beanName) {Object singleton = singletonObjects.get(beanName);if(singleton == null&&  singletonCurrentlyInCreation.contains(beanName)) {singleton = earlySingletonObjects.get(beanName);if(singleton == null){ObjectFactory objectFactory = singletonFactories.get(beanName);if(objectFactory != null){try {//取出半实例化对象singleton = objectFactory.getObject();} catch (Exception e) {throw new RuntimeException(e);}}}}return singleton;}/*** 创建bean** @param beanName* @param clazz* @return*/private Object createBean(String beanName, Class<?> clazz) {try {if(!singletonCurrentlyInCreation.contains(beanName)){singletonCurrentlyInCreation.add(beanName);}//实例化Object bean = clazz.newInstance();earlySingletonObjects.put(beanName,bean);if(singletonCurrentlyInCreation.contains(beanName)){singletonFactories.put(beanName,()->new JdkProxyBeanPostProcess().postProcessBeforeInitialization(earlySingletonObjects.get(beanName),beanName));}//属性注入for(Field field:clazz.getDeclaredFields()){if(field.isAnnotationPresent(Autowired.class)){field.setAccessible(true);Object fieldbean = getBean(field.getName());field.set(bean,fieldbean);}}singletonCurrentlyInCreation.remove(beanName);singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);return bean;} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}/*** 通过beanClazz获取Bean的clazz** @param beanClass* @return*/public Object getBean(Class<?> beanClass) {return  this.getBean(beanClass.getName());}/*** 扫描bean包下的定义** @param beanName* @param beanDefinition*/public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {Class<?> clazz = null;//根据对应的类型去判断是什么方式注入的if (beanDefinition instanceof AnnotateBeanDefinition) {//注解方式注入//找到对应的实现类if (beanDefinition instanceof AnnotateGenericBeanDefinition) {AnnotateGenericBeanDefinition annotateGenericBeanDefinition = (AnnotateGenericBeanDefinition) beanDefinition;definitionMap.put(beanName, annotateGenericBeanDefinition);}}}/*** 扫描注解路径下的全部类--并将其将入初始化工厂中*/public void doScan() {try {//目前只扫描对应包下的组件Set<String> strings = definitionMap.keySet();for (String beanName : strings) {AnnotateGenericBeanDefinition annotateBeanDefinition = (AnnotateGenericBeanDefinition) definitionMap.get(beanName);if (annotateBeanDefinition.getClazz().isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScan = annotateBeanDefinition.getClazz().getAnnotation(ComponentScan.class);String[] packageNames = componentScan.value();for (String packageName:packageNames) {//扫描包下的所有类URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));File file = new File(url.getFile());if (file.isDirectory()) {for (File f : file.listFiles()) {//xxx.xxx.UserServiceClass<?> clazz = this.getClass().getClassLoader().loadClass(packageName.concat(".").concat(f.getName().split("\\.")[0]));String[] split = clazz.getName().split("\\.");String className = split[split.length - 1];if (clazz.isAnnotationPresent(Component.class)) {className = clazz.getAnnotation(Component.class).value().equals("") ?className : clazz.getAnnotation(Component.class).value();className = StringUtils.parserBeanName(className);AnnotateGenericBeanDefinition annotationDefinition = new AnnotateGenericBeanDefinition();annotationDefinition.setClazz(clazz);//将类加入工厂中if (clazz.isAnnotationPresent(Scope.class) && clazz.getAnnotation(Scope.class).value() != null) {annotationDefinition.setScope(clazz.getAnnotation(Scope.class).value());} else {annotationDefinition.setScope(SINGLETON);}definitionMap.put(className, annotationDefinition);beanDefinitionNames.add(className);}}}}}}} catch(Exception e){throw new RuntimeException(e);}}/***  创建单例方法*/public void preInstantiateSingletons() {//防止在创建单例的时候,单例还没有创建完成List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);for (String beanName : beanNames){getBean(beanName);}}
}

3. 应用上下文

AnnotationConfigApplicationContext是面向用户的入口类,提供了基于注解的配置方式:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements BeanDefinitionRegistry {private AnnotateBeanDefinitionReader reader;public AnnotationConfigApplicationContext(Class<?>... componentClass) {this();register(componentClass);super.refresh();}private void register(Class<?>... componentClass) {for (Class<?> aClass : componentClass) {this.reader.register(aClass);}}
}

4. 组件扫描

组件扫描是自动发现和注册Bean的关键功能:

public void doScan() {try {Set<String> strings = definitionMap.keySet();for (String beanName : strings) {AnnotateGenericBeanDefinition beanDefinition = (AnnotateGenericBeanDefinition) definitionMap.get(beanName);if (beanDefinition.getClazz().isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScan = beanDefinition.getClazz().getAnnotation(ComponentScan.class);for (String packageName : componentScan.value()) {// 扫描包路径下的所有类URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));File file = new File(url.getFile());if (file.isDirectory()) {for (File f : file.listFiles()) {Class<?> clazz = this.getClass().getClassLoader().loadClass(packageName.concat(".").concat(f.getName().split("\\.")[0]));if (clazz.isAnnotationPresent(Component.class)) {// 注册Bean定义String className = StringUtils.parserBeanName(clazz.getAnnotation(Component.class).value().equals("") ?clazz.getSimpleName() : clazz.getAnnotation(Component.class).value());AnnotateGenericBeanDefinition annotationDefinition = new AnnotateGenericBeanDefinition();annotationDefinition.setClazz(clazz);annotationDefinition.setScope(clazz.isAnnotationPresent(Scope.class) ? clazz.getAnnotation(Scope.class).value() : SINGLETON);definitionMap.put(className, annotationDefinition);beanDefinitionNames.add(className);}}}}}}} catch(Exception e) {throw new RuntimeException(e);}
}

5.动态代理

函数式接口
package my.self.spring.Function;import jdk.nashorn.internal.objects.annotations.Function;/*** @Description:* @author: zh* @Create : 2025/7/24* @Project_name : SelfsSpring* @Version :**/
@FunctionalInterface
public interface ObjectFactory<T> {T getObject() throws Exception;}
动态代理接口
package my.self.spring.proxy;public interface JDKService {public void execute();
}
动态代理实现类
package my.self.spring.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @Description:* @author: zh   JDk动态代理类* @Create : 2025/7/24* @Project_name : SelfsSpring* @Version :**/
public class  JdkDynamicProxy<T> implements InvocationHandler {private T target;public JdkDynamicProxy(T target) {this.target = target;}public <T> T getProxy(){return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return method.invoke(target, args);}
}
动态代理handler
package my.self.spring.proxy;/*** @Description:* @author: zh* @Create : 2025/7/24* @Project_name : SelfsSpring* @Version :**/
public class JdkProxyBeanPostProcess {public Object postProcessBeforeInitialization(Object bean, String beanName) {if(bean instanceof JDKService){System.out.println(beanName+" instanceof  JDKService");bean = new JdkDynamicProxy(bean).getProxy();}return bean;}
}

6.工具类

package my.self.spring.uitls;/*** @Description:* @author: zh* @Create : 2025/7/24* @Project_name : SelfsSpring* @Version :**/
public class StringUtils {public static String parserBeanName(String beanName) {if(beanName == null || beanName.trim().length() == 0 || "".equals(beanName)){return beanName;}return beanName.substring(0,1).toLowerCase() + beanName.substring(1);}
}

使用示例

定义配置类:

@ComponentScan("my.self.test.bean")
public class AppConfig {
}

定义业务组件:

@Component
public class UserService {@Autowiredprivate OrderService orderService;public void execute() {System.out.println("UserService execute");}
}@Component
public class OrderService implements JDKService {@Autowiredprivate UserService userService;public void execute() {System.out.println("OrderService execute");}
}

启动应用:

public class SpringTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) context.getBean("userService");OrderService orderService = (OrderService) context.getBean("orderService");userService.execute();orderService.execute();}
}

实现亮点

  1. 三级缓存解决循环依赖:通过singletonObjects、earlySingletonObjects和singletonFactories三级缓存机制解决了循环依赖问题。
  2. 基于注解的配置:支持@ComponentScan、@Component、@Autowired等常用注解。
  3. 作用域支持:通过@Scope注解支持单例和原型作用域。
  4. 简洁的API设计:模仿Spring的设计,提供了简单易用的API接口。

总结

这个简易Spring框架实现了IoC容器的核心功能,包括:

  • 组件扫描与自动注册
  • 依赖注入
  • 循环依赖处理
  • 单例管理

虽然功能相比完整的Spring框架简单很多,但核心思想是一致的。通过这个实现,可以更好地理解Spring框架的内部工作原理。

http://www.dtcms.com/a/305011.html

相关文章:

  • 万字详解——OSI七层模型:网络通信的完整架构解析
  • mysql 之多表
  • others-Facebook落地页自建归因逻辑
  • 如何快速把Clickhouse数据同步到Mysql
  • 解决百度网盘双击没反应打不开的问题
  • Element Plus常见基础组件(二)
  • 16大工程项目管理系统对比:开源与付费版本
  • 科研小tip3|Windows中的CompressAi下载与使用
  • leaflet中绘制轨迹线的大量轨迹点,解决大量 marker 绑定 tooltip 同时显示导致的性能问题
  • 机器学习-十大算法之一线性回归算法
  • 通用算法与深度学习基础
  • 机器学习课程介绍
  • 机器学习线性回归:从基础到实践的入门指南
  • 机器学习——线性回归(LinearRegression)
  • 出现错误,Microsoft store初始化失败。请尝试刷新或稍后返回。
  • 深入理解异或运算(XOR)及应用
  • 【变更性别】
  • Webpack基本概念及核心流程
  • Docker初学者需要了解的几个知识点(一):传统虚拟机 VS容器
  • vscode开发微信小程序
  • Shader开发(四)计算机图形学中的颜色定义
  • pthread库和thread库
  • 42、鸿蒙HarmonyOS Next开发:应用上下文Context
  • 20250729使用WPS打开xlsx格式的电子表格时候隐藏显示fx的编辑栏的方法
  • Linux ssh服务安装、启动与开机自启
  • ESim电工仿真软件(电脑版)使用说明
  • 在CSS中,如果你想设置一个元素的高度(height)与其宽度(width)相匹配,但又希望宽度使用百分比来定义,你可以通过几种方式来实现。
  • imx6ull-驱动开发篇2——字符设备驱动开发步骤
  • Cursor(编程ai) 使用 - 2025.7.26
  • Linux - 权限的理解(深入浅出,详细细微)