当前位置:首页 > 工具 > 正文内容

实现简易版 Spring IOC 容器

访客 工具 2026年7月21日 1

实现简易版 Spring IOC 容器

核心目标

通过模拟 Spring 6.x 的核心功能,构建一个简易的 IOC 容器。主要包含以下功能:

  1. 使用自定义注解进行类扫描(如 @CustomComponent@CustomConfiguration)。
  2. 封装 Bean 的元信息(类似于 Spring 的 BeanDefinition)。
  3. 单例 Bean 的实例化。
  4. 通过 @CustomAutowired 实现依赖注入。

第一步:定义核心注解

以下是用于标识组件和依赖注入的注解:

import java.lang.annotation.*;

// 自定义 @Component 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomComponent {
    String value() default "";
}

// 自定义 @Autowired 注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAutowired {
}

// 自定义 @Configuration 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomConfiguration {
    String scanPackage();
}

第二步:封装 Bean 元信息

创建一个类来存储 Bean 的相关信息,例如对应的类、作用域等。

public class CustomBeanDefinition {
    private Class<?> beanClass;
    private String scope = "singleton";
    private boolean lazyInit = false;

    public CustomBeanDefinition(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    // Getter 和 Setter 方法
    public Class<?> getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public boolean isLazyInit() {
        return lazyInit;
    }

    public void setLazyInit(boolean lazyInit) {
        this.lazyInit = lazyInit;
    }
}

第三步:实现核心 IOC 容器

构建一个简易的容器类,完成扫描、注册、实例化和依赖注入等功能。

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

public class CustomApplicationContext {
    private Map<String, CustomBeanDefinition> beanDefinitionMap = new HashMap<>();
    private Map<String, Object> singletonObjects = new HashMap<>();
    private Class<?> configClass;

    public CustomApplicationContext(Class<?> configClass) {
        this.configClass = configClass;
        scanAndRegisterBeans();
        instantiateSingletonBeans();
    }

    private void scanAndRegisterBeans() {
        CustomConfiguration configAnnotation = configClass.getAnnotation(CustomConfiguration.class);
        String scanPackage = configAnnotation.scanPackage();

        String packagePath = scanPackage.replace(".", "/");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL resource = classLoader.getResource(packagePath);

        if (resource == null) {
            throw new RuntimeException("未找到指定包:" + scanPackage);
        }

        File packageDir = new File(resource.getFile());
        recursiveScan(packageDir, scanPackage);
    }

    private void recursiveScan(File dir, String basePackage) {
        if (!dir.isDirectory()) {
            return;
        }

        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                String subPackage = basePackage + "." + file.getName();
                recursiveScan(file, subPackage);
            } else if (file.getName().endsWith(".class")) {
                processClassFile(file, basePackage);
            }
        }
    }

    private void processClassFile(File classFile, String basePackage) {
        try {
            String className = basePackage + "." + classFile.getName().replace(".class", "");
            Class<?> clazz = Class.forName(className);

            if (clazz.isAnnotationPresent(CustomComponent.class)) {
                CustomComponent component = clazz.getAnnotation(CustomComponent.class);
                String beanName = component.value().isEmpty()
                        ? toLowerFirstChar(clazz.getSimpleName())
                        : component.value();

                CustomBeanDefinition beanDefinition = new CustomBeanDefinition(clazz);
                beanDefinitionMap.put(beanName, beanDefinition);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("加载类失败:" + e.getMessage(), e);
        }
    }

    private void instantiateSingletonBeans() {
        for (String beanName : beanDefinitionMap.keySet()) {
            CustomBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

            if ("singleton".equals(beanDefinition.getScope()) && !beanDefinition.isLazyInit()) {
                Object beanInstance = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, beanInstance);
            }
        }
    }

    private Object createBean(String beanName, CustomBeanDefinition beanDefinition) {
        try {
            Class<?> beanClass = beanDefinition.getBeanClass();
            Object beanInstance = beanClass.getDeclaredConstructor().newInstance();

            injectDependencies(beanInstance);
            return beanInstance;
        } catch (Exception e) {
            throw new RuntimeException("创建 Bean 失败:" + beanName, e);
        }
    }

    private void injectDependencies(Object beanInstance) {
        Field[] fields = beanInstance.getClass().getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(CustomAutowired.class)) {
                field.setAccessible(true);
                Class<?> fieldType = field.getType();
                Object dependency = findDependencyByType(fieldType);

                if (dependency == null) {
                    throw new RuntimeException("未找到依赖 Bean:" + fieldType.getName());
                }

                try {
                    field.set(beanInstance, dependency);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("依赖注入失败:" + field.getName(), e);
                }
            }
        }
    }

    private Object findDependencyByType(Class<?> type) {
        for (Object bean : singletonObjects.values()) {
            if (type.isInstance(bean)) {
                return bean;
            }
        }
        return null;
    }

    public <T> T getBean(Class<T> clazz) {
        for (Object bean : singletonObjects.values()) {
            if (clazz.isInstance(bean)) {
                return (T) bean;
            }
        }
        throw new RuntimeException("未找到 Bean:" + clazz.getName());
    }

    private String toLowerFirstChar(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        char[] chars = str.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }
}

第四步:测试代码

1. 编写业务类
@CustomComponent
public class UserRepository {
    public void queryUser() {
        System.out.println("查询用户数据...");
    }
}

@CustomComponent
public class UserService {
    @CustomAutowired
    private UserRepository userRepository;

    public void getUser() {
        userRepository.queryUser();
        System.out.println("处理用户请求...");
    }
}
2. 配置类
@CustomConfiguration(scanPackage = "com.example.demo")
public class AppConfig {
}
3. 测试类
public class TestApp {
    public static void main(String[] args) {
        CustomApplicationContext context = new CustomApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        userService.getUser();
    }
}
输出结果
查询用户数据...
处理用户请求...
标签: SpringIoC

相关文章

Trojan服务器搭建与配置

一、整体架构(先对齐认知)Clash Meta (PC / iOS / Android)        ↓ TLS   Trojan Server (443)        ↓     InternetTrojan 的核心是: TLS + HTTPS 流量伪装 看起来像正常网站 非常适合...

Tailscale 的详细用法

Tailscale 是一种基于 WireGuard 协议 的 零配置 VPN(虚拟私有网络)服务,让设备之间能够 安全、加密地直接连接,就像它们在同一个本地网络一样。它的核心特点是 简单、安全、跨平台。Tailscale 非常适合 没有公网 IP、两台电脑不在同一局域网 的场景。 简单来说,Tailscale 是什么?Tailscale 是一款让你的各种设备(电脑、服务器、手机...

Clash Tun 模式 导致 爱快(iKuai SD-Wan)内网域名无法访问

一、Clash  DNS 配置dns:  enable: true  listen: 0.0.0.0:53  ipv6: true  enhanced-mode: redir-host  nameserver:    - 223.5.5.5    - 223.6.6.6iKuai 内网域名 ...

深入解析Node.js运行环境与异步I/O架构

深入解析Node.js运行环境与异步I/O架构

核心定义与价值Node.js本质上是一个JavaScript运行环境,而非编程语言或应用框架。它赋予了JavaScript脱离浏览器在服务端、命令行工具及网络应用中执行的能力。其核心意义在于:用单一语言打通前后端开发壁垒。基于事件驱动与非阻塞I/O的架构特性,Node.js在处理API网关、实时通信及微服务等I/O密集型场景时表现卓越,已成为现代后端工程的主流选择。浏览器沙箱限制1995年Java...

ADO.NET SQL参数化查询的最佳实践

在 ADO.NET 中执行 SQL 查询时,参数化查询是一种关键的安全措施和性能优化手段。它通过将 SQL 命令和用户提供的数据分开处理,有效防止了 SQL 注入攻击,并有助于数据库缓存执行计划。下面总结了几种常用的参数化查询方式。 1. 使用 SqlParameter 对象(推荐) 这是最推荐的参数化查询方式。通过显式创建 SqlParameter 对象,您可以精确控制参数的类...

基于ELK的日志集中化分析系统搭建

构建统一日志管理平台的必要性 在分布式架构中,各服务节点独立运行,日志分散存储于不同主机。传统通过命令行工具如grep、awk逐个检索日志的方式,在数据量庞大时效率极低,难以实现快速定位问题。为提升运维效率,需建立集中式日志处理体系,具备日志采集、传输、存储、分析与告警能力。 ELK技术栈核心组件解析 Elasticsearch:分布式搜索引擎,支持全文检索、实时数据分析和高可用集群部署,...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。