Spring 源码解读:实现依赖注入的构造函数与Setter注入

引言

依赖注入(Dependency Injection)是Spring框架的核心特性之一,它通过将对象的依赖交由IoC容器管理,帮助开发者实现松耦合的代码结构。Spring支持多种依赖注入方式,其中最常见的是构造函数注入和Setter方法注入。本篇文章将通过一个完整的自定义IoC容器案例详细演示这两种注入方式,并进行深入的Spring 5.x源码解读。

第一部分:构造函数注入

1.1 构造函数注入的基本概念

构造函数注入是一种在对象创建时,通过构造函数将依赖传递给对象的方式。这种方式在对象创建时即保证了依赖的不可变性,适用于强制性依赖的场景。

1.2 自定义实现构造函数注入

我们将设计一个简单的IoC容器,支持通过构造函数注入依赖。

代码实现

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

// 定义Service接口,供Controller类依赖
interface Service {
    void execute();
}

// Service接口的具体实现类
class ServiceImpl implements Service {
    @Override
    public void execute() {
        System.out.println("Service is executing...");
    }
}

// Controller类依赖于Service接口
class Controller {
    private final Service service;

    // 通过构造函数注入Service实例
    public Controller(Service service) {
        this.service = service;
    }

    // 调用Service的方法
    public void doSomething() {
        service.execute();
    }
}

// 自定义的简单IoC容器,用于管理对象创建和依赖注入
public class SimpleIoCContainer {
    // 使用Map来存储Bean实例,key为类类型,value为对应的实例
    private Map<Class<?>, Object> beanMap = new HashMap<>();

    // 注册Bean,处理构造函数注入
    public void registerBean(Class<?> beanClass) {
        try {
            // 获取类的构造函数(假设每个类只有一个构造函数)
            Constructor<?> constructor = beanClass.getConstructors()[0];
            // 获取构造函数的参数类型
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            Object[] parameters = new Object[parameterTypes.length];

            // 递归处理依赖的实例化
            for (int i = 0; i < parameterTypes.length; i++) {
                parameters[i] = getBean(parameterTypes[i]);
            }

            // 使用构造函数创建对象实例
            Object bean = constructor.newInstance(parameters);
            // 将创建的实例注册到容器中
            beanMap.put(beanClass, bean);
        } catch (Exception e) {
            throw new RuntimeException("Failed to register bean: " + beanClass.getName(), e);
        }
    }

    // 获取Bean实例
    public <T> T getBean(Class<T> beanClass) {
        // 如果容器中还没有这个类型的实例,就先注册一个
        if (!beanMap.containsKey(beanClass)) {
            registerBean(beanClass);
        }
        // 返回已注册的实例
        return beanClass.cast(beanMap.get(beanClass));
    }

    public static void main(String[] args) {
        // 创建IoC容器
        SimpleIoCContainer container = new SimpleIoCContainer();

        // 获取Controller实例,依赖自动注入
        Controller controller = container.getBean(Controller.class);

        // 使用Controller
        controller.doSomething();
    }
}

详细解释:

  • SimpleIoCContainer通过反射机制处理构造函数注入。registerBean()方法负责根据构造函数参数类型递归创建对象并注册到容器中。
  • getBean()方法用于获取已注册的Bean实例。如果Bean未注册,容器会自动注册并创建该Bean。
  • main()方法展示了如何使用这个IoC容器来管理Controller和它的依赖Service
1.3 构造函数注入的类图和流程图
类图
SimpleIoCContainer
-Map<Class, Object> beanMap
+void registerBean(Class beanClass)
+T getBean(Class<T> beanClass)
«interface»
Service
+void execute()
ServiceImpl
+void execute()
Controller
-Service service
+Controller(Service service)
+void doSomething()

解释

  • SimpleIoCContainer类管理了所有的Bean创建和注入过程。
  • ServiceController是典型的依赖关系,展示了如何通过构造函数注入依赖。
流程图
SimpleIoCContainer.getBean
检查Bean是否已注册
获取Controller的构造函数
递归获取Service依赖
调用Controller构造函数,传入Service实例
返回初始化的Controller实例

解释

  • 流程图展示了容器如何通过反射机制处理构造函数注入。
  • 从检查Bean是否已注册,到递归处理依赖,最后返回初始化的实例,这些步骤展示了构造函数注入的全流程。
1.4 Spring源码解读:构造函数注入

在Spring Framework 5.x中,构造函数注入是通过ConstructorResolver类来处理的。这个类的核心任务是根据配置选择合适的构造函数,并通过反射机制实例化对象。

autowireConstructor 方法
public BeanWrapper autowireConstructor(
        String beanName, RootBeanDefinition mbd, Constructor<?>[] chosenCtors, Object[] explicitArgs) {

    BeanWrapperImpl bw = new BeanWrapperImpl();
    // 初始化BeanWrapper,设置相应的转换器
    this.beanFactory.initBeanWrapper(bw);

    Constructor<?> constructorToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    Object[] argsToUse = null;

    // 如果有显式参数,直接使用这些参数
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    } else {
        // 否则,解析构造函数参数
        ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
        int minNrOfArgs = cargs.getArgumentCount();

        // 遍历所有构造函数,寻找最合适的一个
        for (Constructor<?> candidate : chosenCtors) {
            Class<?>[] paramTypes = candidate.getParameterTypes();
            if (paramTypes.length >= minNrOfArgs) {
                ArgumentsHolder argsHolder = createArgumentArray(beanName, mbd, bw, paramTypes);
                if (argsHolder != null) {
                    argsHolderToUse = argsHolder;
                    constructorToUse = candidate;
                    argsToUse = argsHolder.arguments;
                    break;
                }
            }
        }
    }

    // 通过反射调用构造函数,创建Bean实例
    Object beanInstance = bw.instantiate(constructorToUse, argsToUse);

    return bw;
}

详细解读:

  • autowireConstructor 方法:这个方法负责自动选择和调用Bean的构造函数。它首先初始化一个BeanWrapperImpl实例,这是Spring用于包装Bean对象的工具类。
  • 参数解析:如果没有显式传递参数,Spring会根据Bean定义中的配置解析构造函数参数,createArgumentArray方法在此过程中起到了关键作用,它会根据构造函数的参数类型,匹配相应的依赖。
  • 构造函数选择:Spring会遍历所有可用的构造函数,并选择最匹配的一个。这一过程的核心在于对每个构造函数参数类型的匹配与转换。
  • 实例化:最终,通过反射调用选定的构造函数,完成Bean的实例化。
createArgumentArray 方法
private ArgumentsHolder createArgumentArray(
        String beanName, RootBeanDefinition mbd, BeanWrapper bw, Class<?>[] paramTypes) {

    ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
    // 依次处理每个参数
    for (int i = 0; i < paramTypes.length; i++) {
        MethodParameter methodParam = new MethodParameter(paramTypes[i], i);
        // 从容器中解析依赖,处理类型转换
        Object argValue = resolveDependency(methodParam, beanName);
        args.arguments[i] = bw.convertForProperty(argValue, paramTypes[i]);
    }
    return args;
}

**详细

解读**:

  • createArgumentArray 方法:负责为构造函数的每个参数创建对应的值。它会调用resolveDependency方法从Spring容器中查找并注入合适的依赖。
  • 依赖解析:Spring会根据参数的类型和位置,找到匹配的依赖对象,进行注入。这也是Spring IoC容器的核心功能之一,即自动装配(autowiring)。
  • 类型转换convertForProperty 方法负责将找到的依赖对象转换为构造函数所需要的类型。这使得Spring能够在构造函数参数类型与实际Bean类型之间进行灵活的匹配。

第二部分:Setter方法注入

2.1 Setter方法注入的基本概念

Setter方法注入允许在对象创建后,通过Setter方法动态地注入依赖。这种方式提供了更大的灵活性,适用于那些可能需要更改或后期注入依赖的对象。

2.2 自定义实现Setter方法注入

我们将扩展前面的IoC容器,实现对Setter方法注入的支持。

代码实现

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

// 定义一个简单的注解,用于标记需要依赖注入的字段
@interface Autowired {}

// Service接口,供Controller类依赖
interface Service {
    void execute();
}

// Service接口的具体实现类
class ServiceImpl implements Service {
    @Override
    public void execute() {
        System.out.println("Service is executing...");
    }
}

// AdvancedService接口,供Controller类可选依赖
interface AdvancedService {
    void performAdvancedTask();
}

// AdvancedService接口的具体实现类
class AdvancedServiceImpl implements AdvancedService {
    @Override
    public void performAdvancedTask() {
        System.out.println("Advanced service is performing a task...");
    }
}

// Controller类,依赖于Service和AdvancedService
class Controller {
    private final Service service;

    // 使用@Autowired注解标记需要通过Setter方法注入的依赖
    @Autowired
    private AdvancedService advancedService;

    // 构造函数注入Service实例
    public Controller(Service service) {
        this.service = service;
    }

    // 调用依赖的方法
    public void doSomething() {
        service.execute();
        if (advancedService != null) {
            advancedService.performAdvancedTask();
        } else {
            System.out.println("No advanced service available.");
        }
    }
}

// 自定义的IoC容器,扩展了对Setter方法注入的支持
public class SimpleIoCContainer {
    // 使用Map来存储Bean实例,key为类类型,value为对应的实例
    private Map<Class<?>, Object> beanMap = new HashMap<>();

    // 注册Bean,处理构造函数注入和Setter方法注入
    public void registerBean(Class<?> beanClass) {
        try {
            // 获取类的构造函数(假设每个类只有一个构造函数)
            Constructor<?> constructor = beanClass.getConstructors()[0];
            // 获取构造函数的参数类型
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            Object[] parameters = new Object[parameterTypes.length];

            // 递归处理依赖的实例化
            for (int i = 0; i < parameterTypes.length; i++) {
                parameters[i] = getBean(parameterTypes[i]);
            }

            // 使用构造函数创建对象实例
            Object bean = constructor.newInstance(parameters);
            // 将创建的实例注册到容器中
            beanMap.put(beanClass, bean);

            // 处理通过@Autowired注解标记的字段的依赖注入
            injectDependencies(bean);
        } catch (Exception e) {
            throw new RuntimeException("Failed to register bean: " + beanClass.getName(), e);
        }
    }

    // 处理通过Setter方法进行的依赖注入
    private void injectDependencies(Object bean) {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Autowired.class)) {
                field.setAccessible(true);
                try {
                    // 获取依赖的实例
                    Object dependency = getBean(field.getType());
                    // 通过反射将依赖注入到字段中
                    field.set(bean, dependency);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Failed to inject dependencies for bean: " + bean.getClass().getName(), e);
                }
            }
        }
    }

    // 获取Bean实例
    public <T> T getBean(Class<T> beanClass) {
        // 如果容器中还没有这个类型的实例,就先注册一个
        if (!beanMap.containsKey(beanClass)) {
            registerBean(beanClass);
        }
        // 返回已注册的实例
        return beanClass.cast(beanMap.get(beanClass));
    }

    public static void main(String[] args) {
        // 创建IoC容器
        SimpleIoCContainer container = new SimpleIoCContainer();

        // 获取Controller实例,依赖自动注入
        Controller controller = container.getBean(Controller.class);

        // 使用Controller
        controller.doSomething();
    }
}

详细解释:

  • SimpleIoCContainer扩展了功能,injectDependencies()方法负责处理@Autowired注解的字段,通过Setter方法注入依赖。
  • AdvancedServiceAdvancedServiceImpl被引入,用于展示Setter方法注入的实现。
2.3 Setter方法注入的类图和流程图
类图
SimpleIoCContainer
-Map<Class, Object> beanMap
+void registerBean(Class beanClass)
+T getBean(Class<T> beanClass)
-void injectDependencies(Object bean)
«interface»
Service
+void execute()
«interface»
AdvancedService
+void performAdvancedTask()
ServiceImpl
+void execute()
AdvancedServiceImpl
+void performAdvancedTask()
Controller
-Service service
-AdvancedService advancedService
+Controller(Service service)
+void doSomething()
流程图
SimpleIoCContainer.getBean
检查Bean是否已注册
获取Controller的构造函数
递归获取Service依赖
调用Controller构造函数,传入Service实例
处理 Autowired注解的字段
递归获取AdvancedService依赖
通过反射注入AdvancedService实例到Controller
返回初始化的Controller实例

解释

  • 流程图展示了容器如何通过反射机制处理Setter方法注入。
  • 包括如何递归获取依赖并通过反射注入到字段中。
2.4 Spring源码解读:Setter方法注入

在Spring 5.x中,Setter方法注入是通过AutowiredAnnotationBeanPostProcessor类来实现的,该类会在Bean初始化的过程中扫描并注入被@Autowired注解标注的依赖。

InjectedElement.inject 方法

InjectedElement 类是 Spring 框架中用于处理依赖注入的一个核心类,它的 inject 方法负责将依赖注入到目标对象中。对于 @Autowired 注解的字段,该方法最终会通过反射调用相应的 Setter 方法来进行注入。

protected void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    if (this.isField) {
        // 如果是字段注入,通过反射将值直接设置到字段中
        Field field = (Field) this.member;
        Object value = this.resolveFieldValue(field, target, beanName);
        ReflectionUtils.makeAccessible(field);
        field.set(target, value);
    } else {
        // 如果是方法(通常是Setter方法),调用方法注入依赖
        Method method = (Method) this.member;
        Object[] arguments = this.resolveMethodArguments(method, target, beanName);
        if (arguments != null) {
            ReflectionUtils.makeAccessible(method);
            method.invoke(target, arguments);
        }
    }
}

详细解读:

  • this.isField:该判断用于确定当前注入的元素是字段还是方法。如果是字段,则直接通过反射设置字段的值;如果是方法(通常为 Setter 方法),则调用方法进行注入。

  • 字段注入:当 isFieldtrue 时,Spring 将通过 ReflectionUtils.makeAccessible 将字段设为可访问,然后使用反射将解析出的依赖值注入到目标字段中。

  • 方法注入(Setter 注入):当 isFieldfalse 时,Spring 将认为需要通过方法注入依赖。通常,这意味着

调用目标对象的 Setter 方法。Spring 首先解析方法的参数,然后通过 method.invoke(target, arguments) 调用实际的 Setter 方法,将解析出的依赖注入目标对象。

resolveMethodArguments 方法

这个方法是 InjectedElement 类中用于解析方法参数的关键方法。

protected Object[] resolveMethodArguments(Method method, Object target, String beanName) {
    Class<?>[] paramTypes = method.getParameterTypes();
    Object[] arguments = new Object[paramTypes.length];
    for (int i = 0; i < paramTypes.length; i++) {
        arguments[i] = this.resolveDependency(new MethodParameter(method, i), beanName);
    }
    return arguments;
}

详细解读:

  • 参数解析:该方法首先获取方法的参数类型 paramTypes,然后逐个参数解析它们的依赖。每个参数的依赖解析通过 resolveDependency 方法完成,这个方法会从 Spring 的 IoC 容器中找到相应类型的 Bean 并返回。

  • 返回参数数组:所有解析出的依赖被存放在 arguments 数组中,最终该数组将作为参数传递给目标对象的 Setter 方法。

总结

通过这篇文章的自定义实现和Spring 5.x源码解读,你应该对Spring中的依赖注入机制有了深入的理解。这些知识将帮助你更好地使用Spring框架进行企业级应用的开发。


互动与思考

在实际项目中,你是否遇到过依赖管理的复杂问题?通过实现自定义IoC容器,你对Spring的依赖注入机制有了哪些新的认识?欢迎在评论区分享你的思考和问题!


如果你觉得这篇文章对你有帮助,请别忘了:

  • 点赞
  • 收藏 📁
  • 关注 👀

让我们一起深入学习Spring框架,成为更优秀的开发者!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

捕风捉你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值