本文主要是介绍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 {@Overridepublic 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);// 使用Controllercontroller.doSomething();}
}
详细解释:
SimpleIoCContainer
通过反射机制处理构造函数注入。registerBean()
方法负责根据构造函数参数类型递归创建对象并注册到容器中。getBean()
方法用于获取已注册的Bean实例。如果Bean未注册,容器会自动注册并创建该Bean。main()
方法展示了如何使用这个IoC容器来管理Controller
和它的依赖Service
。
1.3 构造函数注入的类图和流程图
类图
解释:
SimpleIoCContainer
类管理了所有的Bean创建和注入过程。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 {@Overridepublic void execute() {System.out.println("Service is executing...");}
}// AdvancedService接口,供Controller类可选依赖
interface AdvancedService {void performAdvancedTask();
}// AdvancedService接口的具体实现类
class AdvancedServiceImpl implements AdvancedService {@Overridepublic void performAdvancedTask() {System.out.println("Advanced service is performing a task...");}
}// Controller类,依赖于Service和AdvancedService
class Controller {private final Service service;// 使用@Autowired注解标记需要通过Setter方法注入的依赖@Autowiredprivate 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);// 使用Controllercontroller.doSomething();}
}
详细解释:
SimpleIoCContainer
扩展了功能,injectDependencies()
方法负责处理@Autowired
注解的字段,通过Setter方法注入依赖。AdvancedService
和AdvancedServiceImpl
被引入,用于展示Setter方法注入的实现。
2.3 Setter方法注入的类图和流程图
类图
流程图
解释:
- 流程图展示了容器如何通过反射机制处理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 方法),则调用方法进行注入。 -
字段注入:当
isField
为true
时,Spring 将通过ReflectionUtils.makeAccessible
将字段设为可访问,然后使用反射将解析出的依赖值注入到目标字段中。 -
方法注入(Setter 注入):当
isField
为false
时,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框架,成为更优秀的开发者!
这篇关于Spring 源码解读:实现依赖注入的构造函数与Setter注入的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!