手写Spring IoC:注解+反射打造轻量级容器

手写Spring IoC:注解+反射打造轻量级容器

深入理解Spring IoC核心原理,通过注解和反射机制手写一个简易版IoC容器,掌握依赖注入的本质。

1. 引言:Spring IoC的魔力

在Spring框架中,IoC(控制反转)和DI(依赖注入)是最核心的概念。只需一个@Autowired注解,Spring就能自动帮我们注入依赖对象,这背后的原理是什么?

本文将通过注解+反射的方式,手写一个简易版Spring IoC容器,让你深入理解:

  • 注解是如何被扫描和解析的
  • Bean是如何被创建和管理的
  • 依赖注入是如何实现的
  • Spring容器的运行机制

1.1 为什么要手写IoC?

  • 理解原理 - 知其然更知其所以然
  • 面试必备 - Spring IoC是面试高频考点
  • 提升能力 - 掌握反射、注解等核心技术
  • 框架选型 - 理解框架设计思想

2. 核心概念:IoC、DI、注解、反射

2.1 控制反转(IoC)

传统方式:对象自己创建依赖

java 复制代码
public class UserService {
    private UserDao userDao = new UserDaoImpl(); // 主动创建
}

IoC方式:对象由容器创建和注入

java 复制代码
public class UserService {
    @Autowired
    private UserDao userDao; // 被动接收
}

控制反转就是把对象创建和依赖管理的控制权,从应用代码转移到IoC容器。

2.2 依赖注入(DI)

依赖注入是IoC的实现方式,有三种注入方式:

① 构造器注入

java 复制代码
@Component
public class UserService {
    private final UserDao userDao;

    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

② Setter注入

java 复制代码
@Component
public class UserService {
    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

③ 字段注入(最常用)

java 复制代码
@Component
public class UserService {
    @Autowired
    private UserDao userDao;
}

2.3 Java注解

注解是Java 5引入的元数据标记机制。

java 复制代码
// 定义注解
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Target(ElementType.TYPE)           // 作用于类
public @interface Component {
    String value() default "";
}

// 使用注解
@Component("userService")
public class UserService {
}

2.4 Java反射

反射允许程序在运行时动态获取类信息并操作对象。

java 复制代码
// 加载类
Class<?> clazz = Class.forName("com.example.UserService");

// 检查注解
if (clazz.isAnnotationPresent(Component.class)) {
    Component component = clazz.getAnnotation(Component.class);
    String beanName = component.value();
}

// 创建实例
Object instance = clazz.getDeclaredConstructor().newInstance();

// 获取字段
Field[] fields = clazz.getDeclaredFields();

// 设置字段值
field.setAccessible(true);
field.set(instance, value);

3. 第一步:定义核心注解

首先定义我们自己的注解,模拟Spring的核心注解。

3.1 @Component注解

java 复制代码
package com.myspring.annotation;

import java.lang.annotation.*;

/**
 * 标记组件类,类似Spring的@Component
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
    /**
     * Bean名称,默认为类名首字母小写
     */
    String value() default "";
}

3.2 @Autowired注解

java 复制代码
package com.myspring.annotation;

import java.lang.annotation.*;

/**
 * 自动注入依赖,类似Spring的@Autowired
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface Autowired {
    /**
     * 是否必须注入
     */
    boolean required() default true;
}

3.3 @Qualifier注解

java 复制代码
package com.myspring.annotation;

import java.lang.annotation.*;

/**
 * 指定要注入的Bean名称
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface Qualifier {
    String value();
}

3.4 @Scope注解

java 复制代码
package com.myspring.annotation;

import java.lang.annotation.*;

/**
 * 指定Bean的作用域
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value() default "singleton";
}

4. 第二步:实现包扫描器

包扫描器负责扫描指定包下的所有类,找出带有@Component注解的类。

4.1 ClassScanner类

java 复制代码
package com.myspring.core;

import com.myspring.annotation.Component;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 类扫描器:扫描指定包下的所有类
 */
public class ClassScanner {

    /**
     * 扫描指定包下带有@Component注解的类
     *
     * @param basePackage 基础包路径,如"com.example"
     * @return 带有@Component注解的Class列表
     */
    public static List<Class<?>> scanComponents(String basePackage) {
        List<Class<?>> classList = new ArrayList<>();

        try {
            // 将包名转换为路径
            String packagePath = basePackage.replace('.', '/');

            // 获取包的URL路径
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            URL resource = classLoader.getResource(packagePath);

            if (resource == null) {
                System.out.println("包路径不存在: " + basePackage);
                return classList;
            }

            // 获取包的文件路径
            File packageDir = new File(resource.getFile());

            // 递归扫描所有类文件
            scanClasses(packageDir, basePackage, classList);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return classList;
    }

    /**
     * 递归扫描目录下的所有类文件
     */
    private static void scanClasses(File dir, String packageName, List<Class<?>> classList) {
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }

        File[] files = dir.listFiles();
        if (files == null) {
            return;
        }

        for (File file : files) {
            if (file.isDirectory()) {
                // 递归扫描子目录
                scanClasses(file, packageName + "." + file.getName(), classList);
            } else if (file.getName().endsWith(".class")) {
                try {
                    // 获取类名
                    String className = packageName + "." +
                        file.getName().replace(".class", "");

                    // 加载类
                    Class<?> clazz = Class.forName(className);

                    // 检查是否有@Component注解
                    if (clazz.isAnnotationPresent(Component.class)) {
                        classList.add(clazz);
                        System.out.println("扫描到组件: " + className);
                    }

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5. 第三步:创建Bean工厂

Bean工厂负责存储和管理所有的Bean实例。

5.1 BeanDefinition类

java 复制代码
package com.myspring.core;

/**
 * Bean定义信息
 */
public class BeanDefinition {
    private String beanName;
    private Class<?> beanClass;
    private String scope;
    private Object singletonInstance;

    public BeanDefinition(String beanName, Class<?> beanClass, String scope) {
        this.beanName = beanName;
        this.beanClass = beanClass;
        this.scope = scope;
    }

    // Getter和Setter方法
    public String getBeanName() { return beanName; }
    public Class<?> getBeanClass() { return beanClass; }
    public String getScope() { return scope; }
    public Object getSingletonInstance() { return singletonInstance; }
    public void setSingletonInstance(Object instance) {
        this.singletonInstance = instance;
    }
}

5.2 BeanFactory类

java 复制代码
package com.myspring.core;

import com.myspring.annotation.Component;
import com.myspring.annotation.Scope;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Bean工厂:创建和管理Bean实例
 */
public class BeanFactory {

    // 存储Bean定义信息
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 注册Bean定义
     */
    public void registerBeanDefinition(Class<?> clazz) {
        // 获取Bean名称
        Component component = clazz.getAnnotation(Component.class);
        String beanName = component.value();
        if (beanName.isEmpty()) {
            // 默认使用类名首字母小写
            beanName = toLowerFirstCase(clazz.getSimpleName());
        }

        // 获取作用域
        String scope = "singleton";
        if (clazz.isAnnotationPresent(Scope.class)) {
            Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
            scope = scopeAnnotation.value();
        }

        // 创建BeanDefinition并注册
        BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz, scope);
        beanDefinitionMap.put(beanName, beanDefinition);

        System.out.println("注册Bean: " + beanName + ", 作用域: " + scope);
    }

    /**
     * 获取Bean实例
     */
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new RuntimeException("Bean不存在: " + beanName);
        }

        // 单例模式
        if ("singleton".equals(beanDefinition.getScope())) {
            Object instance = beanDefinition.getSingletonInstance();
            if (instance == null) {
                instance = createBean(beanDefinition);
                beanDefinition.setSingletonInstance(instance);
            }
            return instance;
        }

        // 原型模式:每次都创建新实例
        return createBean(beanDefinition);
    }

    /**
     * 根据类型获取Bean
     */
    public Object getBean(Class<?> clazz) {
        for (BeanDefinition bd : beanDefinitionMap.values()) {
            if (clazz.isAssignableFrom(bd.getBeanClass())) {
                return getBean(bd.getBeanName());
            }
        }
        throw new RuntimeException("Bean不存在: " + clazz.getName());
    }

    /**
     * 创建Bean实例
     */
    private Object createBean(BeanDefinition beanDefinition) {
        try {
            Class<?> clazz = beanDefinition.getBeanClass();
            Object instance = clazz.getDeclaredConstructor().newInstance();
            System.out.println("创建Bean实例: " + beanDefinition.getBeanName());
            return instance;
        } catch (Exception e) {
            throw new RuntimeException("创建Bean失败: " +
                beanDefinition.getBeanName(), e);
        }
    }

    /**
     * 首字母小写
     */
    private String toLowerFirstCase(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    /**
     * 获取所有Bean名称
     */
    public Map<String, BeanDefinition> getBeanDefinitions() {
        return beanDefinitionMap;
    }
}

6. 第四步:实现依赖注入

依赖注入是IoC容器的核心功能,通过反射自动为Bean注入依赖。

6.1 DependencyInjector类

java 复制代码
package com.myspring.core;

import com.myspring.annotation.Autowired;
import com.myspring.annotation.Qualifier;
import java.lang.reflect.Field;

/**
 * 依赖注入器:为Bean注入依赖
 */
public class DependencyInjector {

    private BeanFactory beanFactory;

    public DependencyInjector(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    /**
     * 为所有Bean执行依赖注入
     */
    public void injectDependencies() {
        for (BeanDefinition bd : beanFactory.getBeanDefinitions().values()) {
            Object bean = beanFactory.getBean(bd.getBeanName());
            injectBean(bean);
        }
    }

    /**
     * 为单个Bean注入依赖
     */
    public void injectBean(Object bean) {
        Class<?> clazz = bean.getClass();

        // 获取所有字段
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            // 检查是否有@Autowired注解
            if (!field.isAnnotationPresent(Autowired.class)) {
                continue;
            }

            Autowired autowired = field.getAnnotation(Autowired.class);

            try {
                // 确定要注入的Bean
                Object dependency = null;

                // 检查是否有@Qualifier指定Bean名称
                if (field.isAnnotationPresent(Qualifier.class)) {
                    Qualifier qualifier = field.getAnnotation(Qualifier.class);
                    String beanName = qualifier.value();
                    dependency = beanFactory.getBean(beanName);
                } else {
                    // 根据类型查找Bean
                    dependency = beanFactory.getBean(field.getType());
                }

                // 反射设置字段值
                field.setAccessible(true);
                field.set(bean, dependency);

                System.out.println("注入依赖: " + clazz.getSimpleName() +
                    "." + field.getName());

            } catch (Exception e) {
                if (autowired.required()) {
                    throw new RuntimeException("依赖注入失败: " +
                        clazz.getSimpleName() + "." + field.getName(), e);
                }
            }
        }
    }
}

7. 第五步:完善生命周期

7.1 生命周期注解

java 复制代码
package com.myspring.annotation;

import java.lang.annotation.*;

/**
 * Bean初始化后调用
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PostConstruct {
}
java 复制代码
package com.myspring.annotation;

import java.lang.annotation.*;

/**
 * Bean销毁前调用
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PreDestroy {
}

7.2 生命周期管理器

java 复制代码
package com.myspring.core;

import com.myspring.annotation.PostConstruct;
import com.myspring.annotation.PreDestroy;
import java.lang.reflect.Method;

/**
 * Bean生命周期管理器
 */
public class LifecycleManager {

    /**
     * 执行初始化方法
     */
    public static void invokeInitMethod(Object bean) {
        Class<?> clazz = bean.getClass();
        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods) {
            if (method.isAnnotationPresent(PostConstruct.class)) {
                try {
                    method.setAccessible(true);
                    method.invoke(bean);
                    System.out.println("执行初始化方法: " +
                        clazz.getSimpleName() + "." + method.getName());
                } catch (Exception e) {
                    throw new RuntimeException("初始化方法执行失败", e);
                }
            }
        }
    }

    /**
     * 执行销毁方法
     */
    public static void invokeDestroyMethod(Object bean) {
        Class<?> clazz = bean.getClass();
        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods) {
            if (method.isAnnotationPresent(PreDestroy.class)) {
                try {
                    method.setAccessible(true);
                    method.invoke(bean);
                    System.out.println("执行销毁方法: " +
                        clazz.getSimpleName() + "." + method.getName());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

8. 完整示例:使用手写IoC

8.1 创建ApplicationContext

java 复制代码
package com.myspring.core;

import java.util.List;

/**
 * 应用上下文:整合所有功能
 */
public class ApplicationContext {

    private BeanFactory beanFactory;
    private DependencyInjector injector;

    public ApplicationContext(String basePackage) {
        // 1. 创建Bean工厂
        beanFactory = new BeanFactory();

        // 2. 扫描组件
        System.out.println("========== 开始扫描组件 ==========");
        List<Class<?>> componentClasses = ClassScanner.scanComponents(basePackage);

        // 3. 注册Bean定义
        System.out.println("\n========== 注册Bean定义 ==========");
        for (Class<?> clazz : componentClasses) {
            beanFactory.registerBeanDefinition(clazz);
        }

        // 4. 实例化所有单例Bean
        System.out.println("\n========== 实例化单例Bean ==========");
        for (BeanDefinition bd : beanFactory.getBeanDefinitions().values()) {
            if ("singleton".equals(bd.getScope())) {
                beanFactory.getBean(bd.getBeanName());
            }
        }

        // 5. 依赖注入
        System.out.println("\n========== 执行依赖注入 ==========");
        injector = new DependencyInjector(beanFactory);
        injector.injectDependencies();

        // 6. 执行初始化方法
        System.out.println("\n========== 执行初始化方法 ==========");
        for (BeanDefinition bd : beanFactory.getBeanDefinitions().values()) {
            Object bean = beanFactory.getBean(bd.getBeanName());
            LifecycleManager.invokeInitMethod(bean);
        }

        System.out.println("\n========== IoC容器启动完成 ==========\n");
    }

    /**
     * 获取Bean
     */
    public Object getBean(String beanName) {
        return beanFactory.getBean(beanName);
    }

    /**
     * 根据类型获取Bean
     */
    public <T> T getBean(Class<T> clazz) {
        return (T) beanFactory.getBean(clazz);
    }

    /**
     * 关闭容器
     */
    public void close() {
        System.out.println("\n========== 关闭IoC容器 ==========");
        for (BeanDefinition bd : beanFactory.getBeanDefinitions().values()) {
            Object bean = beanFactory.getBean(bd.getBeanName());
            LifecycleManager.invokeDestroyMethod(bean);
        }
    }
}

8.2 编写测试类

java 复制代码
package com.example.dao;

import com.myspring.annotation.Component;

@Component
public class UserDao {
    public void save() {
        System.out.println("UserDao: 保存用户数据");
    }
}
java 复制代码
package com.example.service;

import com.example.dao.UserDao;
import com.myspring.annotation.Autowired;
import com.myspring.annotation.Component;
import com.myspring.annotation.PostConstruct;
import com.myspring.annotation.PreDestroy;

@Component
public class UserService {

    @Autowired
    private UserDao userDao;

    public void register(String username) {
        System.out.println("UserService: 注册用户 " + username);
        userDao.save();
    }

    @PostConstruct
    public void init() {
        System.out.println("UserService初始化完成");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("UserService准备销毁");
    }
}
java 复制代码
package com.example;

import com.example.service.UserService;
import com.myspring.core.ApplicationContext;

/**
 * 测试类
 */
public class Main {
    public static void main(String[] args) {
        // 创建IoC容器
        ApplicationContext context = new ApplicationContext("com.example");

        // 获取Bean并使用
        UserService userService = context.getBean(UserService.class);
        userService.register("张三");

        // 关闭容器
        context.close();
    }
}

8.3 运行输出

makefile 复制代码
========== 开始扫描组件 ==========
扫描到组件: com.example.dao.UserDao
扫描到组件: com.example.service.UserService

========== 注册Bean定义 ==========
注册Bean: userDao, 作用域: singleton
注册Bean: userService, 作用域: singleton

========== 实例化单例Bean ==========
创建Bean实例: userDao
创建Bean实例: userService

========== 执行依赖注入 ==========
注入依赖: UserService.userDao

========== 执行初始化方法 ==========
执行初始化方法: UserService.init
UserService初始化完成

========== IoC容器启动完成 ==========

UserService: 注册用户 张三
UserDao: 保存用户数据

========== 关闭IoC容器 ==========
执行销毁方法: UserService.destroy
UserService准备销毁

9. 总结

9.1 核心要点

通过手写IoC容器,我们深入理解了:

  1. 注解机制 - 使用@Retention@Target定义注解
  2. 反射技术 - 类加载、实例创建、字段注入
  3. 扫描解析 - 递归扫描包路径,解析注解信息
  4. Bean管理 - 单例/原型模式,Bean定义存储
  5. 依赖注入 - 自动查找和注入依赖对象
  6. 生命周期 - 初始化和销毁回调方法

9.2 与Spring IoC的对比

功能 手写版本 Spring IoC
注解扫描 简单递归 高性能ASM字节码扫描
Bean管理 Map存储 多级缓存+循环依赖处理
依赖注入 字段注入 字段+构造器+Setter
作用域 singleton/prototype 更多作用域
AOP支持 完整AOP框架
性能优化 基础 大量优化策略
相关推荐
愤怒的代码8 小时前
在 Android 中执行 View.invalidate() 方法后经历了什么
android·java·kotlin
memgLIFE8 小时前
SQL 优化方法详解(1)
java·数据库·sql
2201_757830878 小时前
Bean原理篇
java·开发语言
小宇的天下8 小时前
Calibre 3Dstack--每日一个命令day 6 [process和export layout](3-6)
java·前端·数据库
牛奔8 小时前
docker compose up 命令,默认配置文件自动查找规则
java·spring cloud·docker·容器·eureka
工具罗某人8 小时前
docker快速部署jenkins
java·docker·jenkins
华如锦9 小时前
四:从零搭建一个RAG
java·开发语言·人工智能·python·机器学习·spring cloud·计算机视觉
Tony_yitao9 小时前
22.华为OD机试真题:数组拼接(Java实现,100分通关)
java·算法·华为od·algorithm
JavaGuru_LiuYu9 小时前
Spring Boot 整合 SSE(Server-Sent Events)
java·spring boot·后端·sse
爬山算法9 小时前
Hibernate(26)什么是Hibernate的透明持久化?
java·后端·hibernate