可以找酒店案例的网站,百度seo公司兴田德润,简洁 手机 导航网站模板下载,微信小程序免费模板平台Spring 框架核心原理与应用#xff08;上#xff09;
一、Spring 框架概述
#xff08;一#xff09;诞生背景 随着 Java 应用程序规模的不断扩大以及复杂度的日益提升#xff0c;传统的 Java开发方式在对象管理、代码耦合度等方面面临诸多挑战。例如#xff0c;对象之…Spring 框架核心原理与应用上
一、Spring 框架概述
一诞生背景 随着 Java 应用程序规模的不断扩大以及复杂度的日益提升传统的 Java开发方式在对象管理、代码耦合度等方面面临诸多挑战。例如对象之间的依赖关系往往硬编码在程序中使得代码的可维护性、可扩展性变差测试也变得困难。为了解决这些问题Spring框架应运而生它提供了一套轻量级的、面向切面编程以及依赖注入等先进特性的解决方案帮助开发者更高效、灵活地构建 Java 应用程序。 二整体架构
Spring 框架有着一套层次分明且功能丰富的架构体系其核心部分主要包含多个模块各模块相互协作共同支撑起整个框架的强大功能。Spring Core这是 Spring 框架的基础核心模块提供了如 IoC控制反转和 DI依赖注入的基础功能实现包括一些核心的工具类、资源加载机制以及对 Java 反射机制的运用等是其他模块构建的基石。Spring Beans主要聚焦于对 JavaBean 的管理定义了 Bean 的定义、创建、配置以及生命周期管理等相关规范和实现确保 Bean 在 Spring 框架内能被正确地实例化、装配以及销毁。Spring Context构建在 Spring Core 和 Spring Beans 之上为应用程序提供了一种配置和管理的运行时上下文环境方便在整个应用中共享资源、获取 Bean 实例等并且支持国际化、事件传播等高级特性。除此之外还有像 Spring AOP面向切面编程用于实现横切面逻辑的织入Spring Data 方便与各种数据库进行交互Spring MVC 用于构建 Web 应用的 MVC 架构等诸多模块它们分别针对不同的应用场景提供特定的功能支持。
三核心模块功能
1. Spring Core
资源加载机制提供了统一的资源加载抽象使得可以方便地从不同的位置如文件系统、类路径等加载各种资源如配置文件、属性文件等。通过 Resource 接口及其众多实现类内部运用了 Java 的类加载器等机制来定位和读取资源内容例如 ClassPathResource 用于从类路径下加载资源FileSystemResource 用于从文件系统加载资源无论哪种资源类型外部使用时都可以通过统一的接口方法进行操作提高了资源获取的灵活性和通用性。核心工具类包含了如 StringUtils 用于字符串相关的便捷操作像判断字符串是否为空、去除空格等ReflectionUtils 则是对 Java 反射机制进行了进一步的封装和优化方便在框架内部频繁地进行反射操作比如获取类的方法、字段等信息并进行安全的调用和访问避免了反射操作中一些繁琐的权限检查和异常处理步骤简化了代码逻辑这些工具类在整个 Spring 框架中被广泛使用提升了代码的复用性和开发效率。
2. Spring Beans
Bean 定义解析支持多种方式来定义 Bean常见的有 XML 配置和基于注解的配置。对于 XML 配置通过 BeanDefinitionReader 接口及其实现类如 XmlBeanDefinitionReader来解析 XML 文件中的 bean 标签等配置信息将其转换为内存中的 BeanDefinition 对象该对象包含了 Bean 的类名、作用域、依赖关系等关键信息。基于注解的配置则是通过扫描类路径下带有特定注解如 Component 及其衍生注解 Service、Repository、Controller 等的类利用 ClassPathBeanDefinitionScanner 等类解析注解信息同样构建出对应的 BeanDefinition 对象为后续的 Bean 实例化和装配做准备。Bean 生命周期管理定义了一套完整的 Bean 从创建到销毁的生命周期流程涵盖了实例化通过反射机制调用构造函数创建对象实例后面会详细讲解、属性注入依赖注入阶段解决对象之间的依赖关系、初始化调用实现了 InitializingBean 接口的 afterPropertiesSet 方法或者配置了 init-method 的自定义初始化方法以及最终的销毁调用实现了 DisposableBean 接口的 destroy 方法或者配置了 destroy-method 的自定义销毁方法等多个阶段每个阶段都有相应的回调机制和处理逻辑确保 Bean 在不同阶段能执行合适的操作满足应用程序的多样化需求。
3. Spring Context
应用上下文创建与配置提供了多种类型的应用上下文实现如 ClassPathXmlApplicationContext从类路径下的 XML 配置文件加载配置信息创建上下文、FileSystemXmlApplicationContext从文件系统中的 XML 配置文件创建上下文以及 AnnotationConfigApplicationContext基于注解配置创建上下文等。这些上下文类在创建时会首先加载配置信息根据对应的配置源类型然后初始化 Spring 容器构建出整个应用运行的上下文环境内部会管理所有的 Bean 实例以及它们之间的关系并且可以方便地在应用中通过 getBean 方法获取所需的 Bean 实例进行后续操作。事件机制实现了一套应用程序事件传播的机制通过定义 ApplicationEvent 抽象类以及相应的监听器接口 ApplicationListener允许应用中的不同组件通过发布和监听事件来实现解耦的通信。例如当某个 Bean 的状态发生变化或者某个业务操作完成时可以发布对应的事件而其他感兴趣的组件实现了 ApplicationListener 并注册到应用上下文可以接收到事件并做出相应的响应这种基于事件的交互方式增强了应用程序各部分之间的协作灵活性使得代码结构更加清晰、易于维护。
二、Spring 的依赖注入DI机制
一通过 XML 配置实现依赖注入
1. 实现原理源码解读 在 Spring 通过 XML 配置实现依赖注入时核心的操作围绕着对 XML 文件中 bean标签配置的解析以及将解析后的信息用于创建和装配 Bean 实例。 以下是 XmlBeanDefinitionReader 部分关键源码解读它负责读取 XML 文件并解析其中的 Bean 定义信息
public class XmlBeanDefinitionReader implements BeanDefinitionReader {// 用于加载 XML 资源的 XML 解析器底层基于 Java 的 XML 解析技术如 DOM、SAX 等实现对 XML 文件结构和内容的解析private final XmlBeanDefinitionParser parser new XmlBeanDefinitionParser();// 核心的加载 Bean 定义的方法接收一个资源对象如代表 XML 文件的资源解析其中的 Bean 定义并返回对应的 BeanDefinitionHolder 列表public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));}public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {// 省略部分前置的资源有效性等检查逻辑try {// 获取 XML 文档对象通过底层的 XML 解析技术比如调用 DocumentBuilderFactory 等创建解析器来解析 XML 文件得到 Document 对象Document doc doLoadDocument(encodedResource);// 利用解析器解析 XML 文档中的 Bean 定义信息将其转换为 BeanDefinition 对象并包装成 BeanDefinitionHolderreturn registerBeanDefinitions(doc, encodedResource.getResource());} catch (Exception ex) {// 省略异常处理逻辑throw new BeanDefinitionStoreException(IOException parsing XML document from encodedResource.getResource(), ex);} finally {// 省略资源释放等逻辑}}// 解析 XML 文档对象获取其中的 Bean 定义节点bean 标签对应的节点并进一步解析这些节点的属性、子元素等信息来构建 BeanDefinition 对象protected void doRegisterBeanDefinitions(Element root) {BeanDefinitionParserDelegate parent this.delegate;this.delegate new BeanDefinitionParserDelegate(getValidationModeForResource(root));if (this.delegate.isDefaultNamespace(root)) {String profileSpec root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {// 省略处理配置文件中的 profile 相关逻辑用于根据不同环境配置不同的 Bean 定义} else {// 遍历根元素下的所有子节点即各个 bean 标签对应的节点等解析并注册 Bean 定义parseBeanDefinitions(root, this.delegate);}} else {// 处理非默认命名空间的节点如用于支持自定义标签等情况这里省略详细源码this.delegate.parseCustomElement(root, getBeanFactory());}this.delegate parent;}// 解析单个 bean 标签对应的节点根据其属性如 class、scope 等属性以及子元素如 property 用于属性注入等构建 BeanDefinition 对象protected void parseBeanDefinitionElement(Element ele, BeanDefinition containingBd) {String id ele.getAttribute(ID_ATTRIBUTE);String nameAttr ele.getAttribute(NAME_ATTRIBUTE);// 处理 Bean 的名称id 和 name 属性相关逻辑确保获取到合适的 Bean 名称用于后续标识ListString aliases new ArrayList();if (StringUtils.hasLength(nameAttr)) {String[] nameArr StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}if (StringUtils.hasLength(id) !aliases.contains(id)) {aliases.add(0, id);}// 解析 bean 标签中的 class 属性获取要实例化的 Bean 的类信息String beanClassName ele.getAttribute(CLASS_ATTRIBUTE);try {if (StringUtils.hasLength(beanClassName)) {// 通过类加载器加载类获取对应的 Class 对象为后续实例化做准备Class? beanClass ClassUtils.forName(beanClassName, getBeanClassLoader());// 构建基本的 BeanDefinition 对象包含类信息等基础属性BeanDefinition bd new GenericBeanDefinition();bd.setBeanClassName(beanClassName);bd.setBeanClass(beanClass);// 解析 bean 标签的其他属性如 scope 等设置到 BeanDefinition 对象中parseBeanDefinitionAttributes(ele, bd);// 解析 bean 标签的子元素如 property 标签用于属性注入这里调用相关方法进行详细解析和配置注入信息parseMetaElements(ele, bd);parseLookupOverrideElements(ele, bd);parseReplacedMethodElements(ele, bd);parseConstructorArgElements(ele, bd);parsePropertyElements(ele, bd);parseQualifierElements(ele, bd);// 根据解析后的完整信息将 BeanDefinition 对象注册到 Bean 工厂用于后续管理和实例化 BeanBeanDefinitionHolder bdHolder new BeanDefinitionHolder(bd, id, aliases);registerBeanDefinition(bdHolder);}} catch (ClassNotFoundException ex) {// 省略异常处理逻辑throw new BeanDefinitionStoreException(Invalid bean class beanClassName , ex);}}
}从上述源码可以看出XmlBeanDefinitionReader 通过一系列步骤先利用 XML 解析技术获取 XML 文档对象然后遍历其中的 bean 标签节点解析出类名、属性、依赖关系等关键信息构建成 BeanDefinition 对象最后将这些对象注册到 Bean 工厂。
在实际进行依赖注入阶段以属性注入通过 property标签配置为例当通过 BeanFactory具体实现类如 DefaultListableBeanFactory创建和初始化 Bean 实例时会根据 BeanDefinition 中的属性注入配置来执行注入操作。关键源码如下部分简化示意
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {// 创建 Bean 实例的核心方法根据 BeanDefinition 来实例化并初始化 BeanOverrideprotected Object createBean(String beanName, RootBeanDefinition mbd, Nullable Object[] args)throws BeanCreationException {try {// 实例化 Bean通过反射调用构造函数后面会详细讲解构造函数实例化的相关源码逻辑Object beanInstance doCreateBean(beanName, mbd, args);// 进行属性注入遍历 BeanDefinition 中配置的属性信息将依赖的 Bean 实例注入到当前 Bean 中populateBean(beanName, mbd, beanInstance);// 执行初始化方法等后续操作如调用实现了 InitializingBean 接口的方法等initializeBean(beanName, beanInstance, mbd);return beanInstance;} catch (Exception ex) {// 省略异常处理逻辑throw new BeanCreationException(mbd.getResourceDescription(), beanName, Instantiation of bean failed, ex);}}// 进行属性注入的方法解析 BeanDefinition 中的属性配置找到对应的依赖 Bean 实例并注入protected void populateBean(String beanName, RootBeanDefinition mbd, Nullable Object beanInstance) {// 获取 Bean 定义中的属性配置信息通过之前解析 XML 中的 property 等标签构建的配置PropertyValues pvs mbd.getPropertyValues();if (pvs.isEmpty()) {return;}// 判断是否采用自动注入模式如 byName、byType 等如果是则按照相应规则查找依赖 Bean 实例进行注入if (mbd.getResolvedAutowireMode() AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs new MutablePropertyValues(pvs);// 省略部分自动注入模式下的查找和注入逻辑比如 byName 模式会根据属性名称查找对应的 Bean 实例注入if (mbd.getResolvedAutowireMode() AUTOWIRE_BY_TYPE) {// byType 模式会根据属性类型查找匹配的 Bean 实例注入涉及到类型匹配、查找等复杂逻辑autowireByType(beanName, mbd, newPvs);}pvs newPvs;}// 遍历每个属性配置通过反射获取对应的 setter 方法利用 Java 反射机制找到属性对应的 setter 方法并调用进行注入for (PropertyValue pv : pvs) {String propertyName pv.getName();Object propertyValue pv.getValue();if (propertyValue instanceof BeanReference) {// 如果属性值是指向另一个 Bean 的引用比如 property 标签中 ref 属性指向另一个 bean则获取对应的 Bean 实例注入BeanReference beanReference (BeanReference) propertyValue;propertyValue resolveBeanReference(beanReference, beanName, mbd, beanInstance);}try {// 通过反射找到 setter 方法并调用将属性值注入到 Bean 实例中MethodDescriptor md BeanUtils.findPropertyDescriptor(beanInstance.getClass(), propertyName).getWriteMethod();if (md! null) {ReflectionUtils.makeAccessible(md.getMethod());md.getMethod().invoke(beanInstance, propertyValue);}} catch (Exception ex) {// 省略异常处理逻辑throw new BeanCreationException(mbd.getResourceDescription(), beanName,Could not set property values on bean beanName , ex);}}}
}在 populateBean 方法中先是获取 BeanDefinition 里记录的属性配置信息然后根据不同的自动注入模式进行相应处理最后通过反射机制找到属性对应的 setter 方法调用该方法将属性值可能是普通对象值或者是指向其他 Bean 的引用经过相应的查找和解析后注入到 Bean 实例中完成依赖注入操作。
2. 性能特点
启动性能使用 XML 配置进行依赖注入时在 Spring 容器启动阶段由于需要解析 XML 文件、构建 BeanDefinition 对象以及进行各种配置的验证和预处理等操作相对来说启动速度会稍慢一些。尤其是当 XML 文件较大、Bean 定义众多或者存在复杂的配置结构如多层嵌套的 bean 标签、大量的条件配置等时解析 XML 的时间成本以及内存占用都会增加影响容器的启动效率。运行时性能在运行时每次获取 Bean 实例以及进行依赖注入操作如果涉及到复杂的自动注入查找等情况通过反射机制查找和调用 setter 方法等操作会带来一定的性能开销。不过Spring 内部对于频繁使用的 Bean 等情况有一定的缓存机制比如缓存 BeanDefinition、部分实例化后的 Bean 等可以在一定程度上缓解这种性能损耗使得在常规的应用场景下运行时性能通常能维持在可接受的范围。
应用场景 大型项目配置管理在一些大型的企业级 Java 项目中往往有着复杂的模块划分和众多的组件依赖关系使用 XML 配置方式可以将整个项目的 Bean 配置集中管理方便不同的开发团队、运维团队查看和修改配置信息清晰地梳理出各个模块之间的依赖关系并且可以根据不同的环境如开发环境、测试环境、生产环境通过配置文件的切换或者部分配置的调整来灵活部署应用。 历史项目维护与集成对于一些已经存在的老项目如果要接入 Spring 框架或者进行功能扩展使用 XML 配置 历史项目维护与集成对于一些已经存在的老项目如果要接入 Spring 框架或者进行功能扩展使用 XML 配置方式是比较友好的选择。因为可以在不大量改动原有代码结构的基础上通过编写 XML 配置文件来逐步引入 Spring 的相关功能比如将原有的对象创建和依赖关系管理迁移到 Spring 容器中进行统一管控。这样既能够利用 Spring 框架带来的优势如更灵活的依赖注入、方便的生命周期管理等又能最大程度减少对老项目已有代码逻辑的冲击便于后续的维护与持续集成新功能使得老项目可以平滑地向现代化的架构模式演进。
二基于注解如 Autowired 等实现依赖注入
1. 实现原理源码解读 在 Spring 基于注解实现依赖注入时关键在于对各类注解的解析以及利用这些解析结果来完成 Bean 之间的依赖关联操作。 以常用的 Autowired 注解为例来深入剖析其源码实现原理。首先Spring 在启动过程中会进行类路径扫描通过 ClassPathBeanDefinitionScanner 等类来查找带有特定注解的类并将其解析为 BeanDefinition 对象以下是相关部分源码示意
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {// 扫描指定的包路径下的类查找符合条件带有特定注解等的类并解析为 BeanDefinition 对象public SetBeanDefinitionHolder findCandidateComponents(String basePackage) {SetBeanDefinitionHolder candidates new HashSet();try {// 获取包路径下的所有资源类文件等利用类加载器等机制定位资源String packageSearchPath ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) / this.resourcePattern;Resource[] resources getResourcePatternResolver().getResources(packageSearchPath);for (Resource resource : resources) {if (resource.isReadable()) {try {// 读取资源对应的元数据信息通过 ASM 等字节码读取技术或者 Java 反射机制获取类的基本信息MetadataReader metadataReader getMetadataReaderFactory().getMetadataReader(resource);// 判断类是否符合候选组件条件是否带有如 Component 及其衍生注解等if (isCandidateComponent(metadataReader)) {// 根据元数据信息解析出 BeanDefinition 对象ScannedGenericBeanDefinition sbd new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);if (isCandidateComponent(sbd)) {// 进一步完善 BeanDefinition 对象比如解析类上的其他注解信息等candidates.add(new BeanDefinitionHolder(sbd, sbd.getBeanClassName()));}}} catch (IOException ex) {// 省略异常处理逻辑throw new BeanDefinitionStoreException(IOException parsing candidate component class: resource, ex);}}}} catch (IOException ex) {// 省略异常处理逻辑throw new BeanDefinitionStoreException(I/O failure during classpath scanning, ex);}return candidates;}// 判断元数据对应的类是否为候选组件主要看是否带有特定的注解标识protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {AnnotationMetadata annotationMetadata metadataReader.getAnnotationMetadata();return (annotationMetadata.hasAnnotation(Component.class.getName()) ||annotationMetadata.hasMetaAnnotation(Component.class.getName())) !annotationMetadata.isAnnotation();}
}从上述源码能看出ClassPathBeanDefinitionScanner 通过扫描类路径下的资源利用 MetadataReader 获取类的元数据信息然后依据是否带有如 Component 等注解来判断是否将其作为候选组件解析成 BeanDefinition 对象这个过程为后续基于注解的依赖注入做了前置准备。 当涉及到 Autowired 注解的依赖注入处理时在 Bean 创建和初始化阶段AutowiredAnnotationBeanPostProcessor 发挥了关键作用以下是其部分关键源码解读
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {// 处理 Bean 的属性注入查找带有 Autowired 等注解的属性并进行注入操作Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);} catch (Throwable ex) {// 省略异常处理逻辑throw new BeanCreationException(beanName, Injection of autowired dependencies failed, ex);}return pvs;}// 查找给定类中带有 Autowired 等相关注解的属性、方法等信息构建 InjectionMetadata 对象用于后续注入操作private InjectionMetadata findAutowiringMetadata(String beanName, Class? clazz, Nullable PropertyValues pvs) {// 从缓存中查找已有的 InjectionMetadata 对象如果存在则直接返回提高性能避免重复解析InjectionMetadata metadata this.injectionMetadataCache.get(clazz);if (metadata null) {ListInjectionMetadata.InjectionPoint points new ArrayList();// 遍历类的属性查找带有 Autowired 等注解的属性添加到 InjectionPoint 列表中ReflectionUtils.doWithLocalFields(clazz, field - {AnnotationAttributes ann findAutowiredAnnotation(field);if (ann! null) {if (Modifier.isStatic(field.getModifiers())) {// 对于静态属性的特殊处理Spring 不支持对静态属性直接注入抛出异常提示等逻辑此处省略详细源码throw new IllegalStateException(Autowired annotation is not supported on static fields: field);}boolean required determineRequiredStatus(ann);points.add(new InjectionMetadata.InjectionPoint(field, required));}});// 遍历类的方法查找带有 Autowired 等注解的可注入方法如带有 Autowired 的 setter 方法等添加到 InjectionPoint 列表中ReflectionUtils.doWithLocalMethods(clazz, method - {Method bridgedMethod BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}AnnotationAttributes ann findAutowiredAnnotation(bridgedMethod);if (ann! null) {if (Modifier.isStatic(bridgedMethod.getModifiers())) {// 对于静态方法的特殊处理类似静态属性不支持直接注入省略详细源码throw new IllegalStateException(Autowired annotation is not supported on static methods: method);}boolean required determineRequiredStatus(ann);points.add(new InjectionMetadata.InjectionPoint(bridgedMethod, required));}});metadata new InjectionMetadata(clazz, points);this.injectionMetadataCache.put(clazz, metadata);}return metadata;}// 查找属性或方法上的 Autowired 等相关注解并解析注解信息获取如是否必须注入等属性private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {// 遍历支持的注解类型如 Autowired、Value 等查找是否存在相应注解for (Class? extends Annotation type : this.autowiredAnnotationTypes) {AnnotationAttributes ann AnnotationUtils.getAnnotationAttributes(ao, type);if (ann! null) {return ann;}}return null;}
}在 postProcessProperties 方法中先通过 findAutowiringMetadata 方法查找出给定 Bean 类中带有 Autowired 注解的属性和方法等信息构建成 InjectionMetadata 对象然后调用其 inject 方法进行实际的注入操作。在 inject 方法此处省略详细源码展示中对于属性注入会通过反射机制获取属性类型再根据类型在 Spring 容器中查找匹配的 Bean 实例可以是按类型匹配、按名称匹配等多种策略依据具体配置和容器内的情况最后通过反射调用 setter 方法或者直接设置属性值对于非私有属性等情况将依赖的 Bean 实例注入到当前 Bean 中对于方法注入同理会根据方法参数类型等查找匹配的 Bean 实例然后调用方法完成注入。
2. 性能特点
启动性能相较于 XML 配置基于注解的方式在启动时不需要解析大量的 XML 文件内容而是通过类路径扫描来识别带有注解的类在一定程度上可以加快启动速度尤其是在项目规模不是特别巨大且注解使用合理的情况下。不过类路径扫描本身也需要遍历类文件、解析字节码信息等操作对于包含极大量类的项目这个过程也会消耗一定时间和内存资源但通常比 XML 配置的解析成本要低一些。运行时性能在运行时和 XML 配置依赖注入类似基于注解的注入同样需要利用反射机制来查找和调用相关方法、设置属性值等也存在一定的性能开销。但 Spring 内部会对一些常用的注入点等信息进行缓存如前面提到的 InjectionMetadata 缓存减少了重复查找和解析注解的次数使得运行时的性能损耗在可接受范围内并且随着 Java 虚拟机在反射优化等方面的不断改进如方法内联等技术应用到反射调用场景其运行时性能也在逐步提升。
3. 应用场景
快速开发小型项目在小型的 Java 项目或者一些敏捷开发的场景中使用基于注解的依赖注入方式可以让开发更加便捷高效。开发者只需要在需要注入依赖的地方简单地添加 Autowired 等注解无需编写繁琐的 XML 配置文件能够快速地搭建起项目的业务逻辑加快开发迭代速度并且代码看起来更加简洁直观易于理解和维护。与代码结合紧密的功能模块对于那些和业务代码逻辑紧密结合需要根据代码的具体结构和功能动态调整依赖关系的模块注解方式的依赖注入更具优势。比如在开发一个基于 Spring 的微服务应用时各个微服务内部的业务类之间的依赖可以通过注解精准地表达随着业务功能的不断扩展和调整能够方便地在代码中直接修改注解配置来适应新的依赖需求而不用在 XML 文件中去查找和修改对应的 bean 配置提高了代码的灵活性和可维护性。
三、Spring 的控制反转IOC容器的启动流程和实现原理
一启动流程
Spring IOC 容器的启动是一个涉及多个阶段、多个组件协作的复杂过程以下是其大致的启动流程概述
资源定位首先根据配置信息无论是 XML 配置文件还是基于注解配置等方式确定要加载的资源位置。例如对于 ClassPathXmlApplicationContext 这种基于类路径下 XML 配置文件启动的容器它会通过类加载器等机制定位到指定的 XML 文件资源对于基于注解配置的 AnnotationConfigApplicationContext则会确定要扫描的类路径范围等这个阶段主要是为后续的配置解析做好准备工作确保能准确获取到描述应用程序配置的相关资源。 配置解析XML 配置解析如果是基于 XML 配置如前面所述会通过 XmlBeanDefinitionReader 等类对 XML 文件进行解析将其中的 bean 标签等配置信息转换为 BeanDefinition 对象这些对象包含了 Bean 的类名、属性、依赖关系、生命周期配置等关键信息构建起了内存中对应用程序中所有 Bean 的一种抽象描述模型为后续的 Bean 实例化和管理提供依据。注解配置解析对于基于注解的配置通过 ClassPathBeanDefinitionScanner 等组件扫描类路径下带有特定注解如 Component 及其衍生注解的类同样解析出对应的 BeanDefinition 对象同时还会解析类上的其他相关注解如 Autowired 等用于依赖注入、Scope 用于定义 Bean 的作用域等信息并整合到 BeanDefinition 对象中完善对每个 Bean 的描述。Bean 工厂初始化创建 BeanFactory通常是 DefaultListableBeanFactory 等具体实现类这是 Spring IOC 容器的核心组件之一它负责管理所有的 BeanDefinition 对象以及后续的 Bean 实例化、依赖注入等操作。将前面解析得到的所有 BeanDefinition 对象注册到 BeanFactory 中使其知晓应用程序中需要创建和管理哪些 Bean并且会进行一些初始的配置和准备工作比如设置 Bean 工厂的一些属性如是否允许循环依赖、自动注入模式等构建起内部用于存储和查找 BeanDefinition 的数据结构如各种映射表等方便后续根据 Bean 名称或类型快速定位 BeanDefinition。Bean 实例化与初始化基于注册在 BeanFactory 中的 BeanDefinition 对象开始逐个实例化 Bean。首先通过反射机制调用构造函数创建 Bean 的对象实例对于构造函数的选择会根据配置以及参数匹配等情况确定后面会详细讲解构造函数实例化的源码逻辑然后进行依赖注入操作如前面介绍的通过 XML 配置或注解方式进行属性注入等接着执行初始化操作包括调用实现了 InitializingBean 接口的 afterPropertiesSet 方法或者配置了 init-method 的自定义初始化方法等使得 Bean 在完全创建好后处于可用状态准备好为应用程序提供服务。在这个过程中还会处理 Bean 之间的依赖关系如果存在循环依赖的情况比如 A Bean 依赖 B Bean同时 B Bean 又依赖 A BeanSpring 也有相应的处理机制通过提前暴露部分创建中的 Bean 实例等策略此处省略详细的循环依赖处理源码逻辑来确保 Bean 都能正确地被实例化和初始化。容器就绪经过上述一系列步骤所有的 Bean 都按照配置完成了实例化、初始化等操作Spring IOC 容器也就准备就绪可以通过容器提供的方法如 getBean 方法等获取到所需的 Bean 实例应用程序就可以利用这些 Bean 实例来执行业务逻辑整个 Spring 框架开始为应用提供支撑实现了对象管理、依赖注入等核心功能让应用程序的各个组件可以在一个解耦且有序的环境中协同工作。
二实现原理源码解读
1. 资源定位与配置解析相关源码回顾
前面已经详细介绍了在 XML 配置下 XmlBeanDefinitionReader 解析 XML 文件以及在注解配置下ClassPathBeanDefinitionScanner 扫描类路径解析注解并构建 BeanDefinition 对象的源码逻辑这里不再赘述它们共同完成了从外部配置到内存中 BeanDefinition 模型的转换为后续的 Bean 工厂操作提供了基础数据。
2. Bean 工厂初始化源码解读
以下是 DefaultListableBeanFactory 部分关键源码展示其初始化以及 BeanDefinition 注册相关逻辑
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {// 用于存储所有的 BeanDefinition 对象的映射表以 Bean 名称作为键BeanDefinition 对象作为值方便快速查找和管理private final MapString, BeanDefinition beanDefinitionMap new ConcurrentHashMap();// 注册 BeanDefinition 对象到 beanDefinitionMap 中的方法这是将外部配置解析后的信息纳入 Bean 工厂管理的关键操作Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, Bean name must not be empty);Assert.notNull(beanDefinition, Bean definition must not be null);if (beanDefinition instanceof AbstractBeanDefinition) {try {// 对 AbstractBeanDefinition 类型的 BeanDefinition 进行验证和准备工作比如解析其中的一些表达式等配置信息((AbstractBeanDefinition) beanDefinition).validate();} catch (BeanDefinitionValidationException ex) {// 省略异常处理逻辑throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,Validation of bean definition failed, ex);}}BeanDefinition existingDefinition this.beanDefinitionMap.get(beanName);if (existingDefinition! null) {// 如果已经存在同名的 BeanDefinition根据配置决定是覆盖还是抛出异常等处理方式比如可以配置允许覆盖等情况if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanName,Cannot register bean definition [ beanDefinition ] for bean beanName : There is already a bean definition [ existingDefinition ] bound);}// 省略部分覆盖时的详细逻辑比如可能涉及到更新缓存等操作this.beanDefinitionMap.put(beanName, beanDefinition);} else {// 如果不存在同名的 BeanDefinition直接添加到映射表中this.beanDefinitionMap.put(beanName, beanDefinition);// 同时更新一些相关的辅助数据结构如按类型查找 BeanDefinition 的映射表等方便后续根据类型查找 Beanif (existingDefinition null containsBeanDefinition(beanName)) {// 省略部分更新逻辑}}}// 创建 Bean 工厂实例时的初始化方法进行一些内部数据结构等的初始化设置public DefaultListableBeanFactory() {super();// 初始化用于存储按照类型查找 BeanDefinition 的映射表方便后续基于类型获取对应的 BeanDefinitionthis.mergedBeanDefinitionHolders new ConcurrentHashMap();// 初始化用于存储单例对象的缓存在创建单例 Bean 实例后会存放在这里后续获取单例 Bean 可直接从缓存中取提升性能this.singletonObjects new ConcurrentHashMap();// 初始化用于存储正在创建中的单例 Bean 的缓存用于处理单例 Bean 的循环依赖问题后面实例化过程中会涉及其使用逻辑this.singletonFactories new HashMap();// 初始化用于存储提前暴露的单例 Bean 实例处于半成品状态可能尚未完成全部初始化流程的缓存同样是解决循环依赖的关键数据结构this.earlySingletonObjects new HashMap();// 初始化用于存储单例 Bean 对应的工厂对象通过工厂方法创建单例 Bean 时会用到的缓存this.factoryBeanInstanceCache new ConcurrentHashMap();// 设置是否允许 Bean 定义覆盖默认值可以根据配置或者框架内部默认策略来确定影响同名 BeanDefinition 注册时的处理方式this.allowBeanDefinitionOverriding true;// 设置是否允许循环依赖不同的应用场景下可以根据需求进行配置默认是允许一定程度的循环依赖处理this.allowCircularReferences true;}// 根据给定的 BeanDefinition 创建对应的 Bean 实例的核心方法涵盖了实例化、依赖注入、初始化等完整流程Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, Nullable Object[] args)throws BeanCreationException {try {// 检查是否需要提前实例化比如对于一些配置了懒加载lazy-init的 Bean会根据条件判断是否现在就创建if (mbd.isLazyInit() !mbd.hasBeanClass()) {return null;}// 确保 Bean 对应的 Class 对象已经加载通过类加载器等机制加载类如果还未加载的情况Class? resolvedClass resolveBeanClass(mbd, beanName);if (resolvedClass null !mbd.hasBeanClass()) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,Bean class not found);}mbd.prepareMethodOverrides();try {// 执行 Bean 的实例化操作通过反射调用合适的构造函数创建对象实例后面会详细展示实例化相关的具体源码逻辑Object beanInstance doCreateBean(beanName, mbd, args);return beanInstance;} catch (BeanCreationException ex) {// 异常处理逻辑如果实例化过程出现问题进行相应的错误处理和日志记录等throw ex;} catch (Exception ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,Unexpected error during bean creation, ex);}} catch (Exception ex) {// 更广泛的异常处理逻辑涵盖前面各个步骤中可能出现的问题throw new BeanCreationException(mbd.getResourceDescription(), beanName,Instantiation of bean failed, ex);}}// 实际执行 Bean 实例化的方法内部涉及到通过反射调用构造函数、处理循环依赖等关键逻辑protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Nullable Object[] args) {// 创建 Bean 实例的包装对象用于处理诸如 AOP 代理等情况如果有代理需求后续会基于此创建代理对象替代原始的 Bean 实例BeanWrapper instanceWrapper null;if (mbd.isSingleton()) {// 对于单例 Bean先从缓存中查看是否已经存在正在创建的该 Bean 的实例如果存在说明可能处于循环依赖情况直接返回已有的实例后续会进一步完善其创建流程instanceWrapper this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper null) {// 通过反射创建 Bean 实例调用合适的构造函数根据 BeanDefinition 中的类信息以及参数信息args进行实例化操作instanceWrapper createBeanInstance(beanName, mbd, args);}Object beanInstance instanceWrapper.getWrappedInstance();Class? beanType instanceWrapper.getWrappedClass();if (mbd.isSingleton()) {// 将刚创建的 Bean 实例放入正在创建中的单例 Bean 缓存中标记其正在创建用于处理可能出现的循环依赖情况this.singletonFactories.put(beanName, () - getEarlyBeanReference(beanName, mbd, beanInstance));}// 进行属性注入操作将依赖的其他 Bean 实例注入到当前创建的 Bean 实例中前面已经详细介绍过依赖注入相关的源码逻辑此处不再赘述populateBean(beanName, mbd, beanInstance);if (mbd.isSingleton()) {// 将提前暴露的单例 Bean 实例从正在创建的缓存中移除放入提前暴露的单例 Bean 实例缓存earlySingletonObjects中表明已经完成了属性注入等部分流程可以供其他依赖它的 Bean 进一步使用解决循环依赖Object earlySingletonExposure this.singletonFactories.remove(beanName);if (earlySingletonExposure! null) {// 将提前暴露的实例添加到对应的缓存中this.earlySingletonObjects.put(beanName, earlySingletonExposure);// 注册一个销毁回调方法用于在容器关闭等情况下清理该 Bean 实例相关资源比如释放内存、关闭连接等如果有需要的情况registerDisposableBeanIfNecessary(beanName, beanInstance, mbd);}}// 执行 Bean 的初始化操作包括调用实现了 InitializingBean 接口的方法、配置的 init-method 等使 Bean 完全进入可用状态initializeBean(beanName, beanInstance, mbd);return beanInstance;}// 通过反射调用合适的构造函数创建 Bean 实例的方法内部会根据 BeanDefinition 中的构造函数配置以及参数情况进行选择和调用protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Nullable Object[] args) {// 获取要创建的 Bean 的 Class 对象Class? beanClass mbd.getBeanClass();if (beanClass! null !Modifier.isPublic(beanClass.getModifiers()) !mbd.isNonPublicAccessAllowed()) {// 对于非公有类的情况进行特殊的权限处理比如通过设置访问权限等方式确保可以通过反射调用构造函数涉及到 Java 安全管理器等相关机制此处省略详细源码throw new BeanCreationException(mbd.getResourceDescription(), beanName,Bean class isnt public, and non-public access not allowed: beanClass.getName());}// 尝试查找合适的构造函数根据参数匹配等情况来确定使用哪个构造函数进行实例化如果有明确指定的构造函数比如通过 XML 配置或者注解指定了构造函数参数等情况则按照指定的来Constructor?[] ctors determineConstructorsFromBeanDefinition(mbd, beanClass);if (ctors! null ctors.length 0) {return autowireConstructor(beanName, mbd, ctors, args);}// 如果没有明确指定构造函数或者找不到合适的构造函数使用默认的无参构造函数进行实例化前提是类存在无参构造函数否则会抛出异常return instantiateBean(beanName, mbd);}// 使用默认的无参构造函数通过反射创建 Bean 实例的方法相对比较简单直接调用 Class 对象的 newInstance 方法来创建protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {try {// 获取要实例化的 Bean 的 Class 对象Class? beanClass mbd.getBeanClass();// 创建 BeanWrapper 对象用于包装创建出来的 Bean 实例方便后续对实例进行各种操作如属性注入、获取类型等BeanWrapper bw new BeanWrapperImpl(beanClass);// 通过反射调用无参构造函数创建实例并将实例设置到 BeanWrapper 中Object beanInstance beanClass.newInstance();bw.setWrappedInstance(beanInstance);return bw;} catch (InstantiationException | IllegalAccessException ex) {// 异常处理逻辑如果实例化过程出现问题比如类没有无参构造函数等情况抛出相应的异常并进行错误记录等操作throw new BeanCreationException(mbd.getResourceDescription(), beanName,Could not instantiate bean class [ mbd.getBeanClass().getName() ], ex);}}}从上述源码可以清晰地看到 DefaultListableBeanFactory 在整个 Bean 实例化和初始化过程中的关键逻辑流程。首先在初始化阶段构建起了多个重要的缓存和数据结构用于存储不同状态下的 Bean 实例、BeanDefinition 以及相关的工厂对象等这些结构为后续高效地管理和处理 Bean 的创建、依赖关系解决等提供了基础保障。在 createBean 方法中启动了完整的 Bean 创建流程其中 doCreateBean 方法是核心环节它先尝试从缓存中获取可能已经存在的正在创建的单例 Bean 实例用于处理循环依赖然后通过 createBeanInstance 方法进行实际的实例化操作这里会根据 BeanDefinition 中构造函数的配置情况选择合适的构造函数有指定就按指定的来没有则尝试使用无参构造函数通过反射进行调用创建出 Bean 实例。接着进行属性注入操作将依赖的其他 Bean 实例填充到当前创建的 Bean 实例中再处理单例 Bean 的循环依赖相关缓存操作最后执行初始化步骤使得 Bean 实例完全准备好可以投入到应用程序的业务逻辑中使用。
3. 性能特点
启动阶段性能
资源消耗方面Spring IOC 容器启动时尤其是首次启动需要加载配置无论是 XML 还是扫描类路径解析注解等、解析并构建众多的 BeanDefinition 对象还要初始化 BeanFactory 内部的各种缓存和数据结构这个过程会占用一定的内存空间。对于大型项目配置繁多、Bean 数量庞大时内存消耗会比较明显可能导致启动过程中内存占用较高甚至在内存资源紧张的环境下影响启动速度或者出现内存不足相关问题。时间成本方面配置解析、BeanDefinition 构建以及 BeanFactory 的初始化操作都需要耗费时间像 XML 配置文件的解析可能因文件大小、复杂程度例如存在大量嵌套的 bean 标签、复杂的条件配置等而花费较多时间类路径扫描查找带注解的类同样在面对大量类文件时耗时会增加而且后续实例化和初始化 Bean 时如果存在复杂的依赖关系如多层嵌套依赖、循环依赖等处理这些依赖并确保 Bean 正确创建也会在启动阶段带来额外的时间开销。
运行阶段性能
Bean 获取性能一旦容器启动完成后续获取 Bean 实例时如果是单例 Bean得益于 BeanFactory 中的单例缓存机制如 singletonObjects 缓存从缓存中直接获取已创建好的单例 Bean 速度是比较快的能快速响应应用程序对 Bean 的请求。但对于非单例如原型 prototype 作用域的 Bean每次获取都需要重新创建实例、进行依赖注入和初始化等操作相对来说耗时会更长不过这也符合原型 Bean 的设计初衷即每次获取都是一个新的独立实例。依赖注入性能在运行时进行依赖注入无论是基于 XML 配置的属性注入还是基于注解的注入如前面所述依然依赖反射机制来查找和调用相关方法、设置属性值等存在一定的性能损耗。然而Spring 通过缓存部分常用的注入相关信息如 InjectionMetadata 等以及对单例 Bean 实例化后相关依赖关系的固定化处理减少重复查找和注入操作使得在常规业务场景下这种性能损耗通常不会对整体应用程序性能造成严重影响能够维持在一个可接受的运行效率水平。
4. 应用场景
企业级应用开发在大型的企业级 Java 应用中Spring 的 IOC 容器发挥着至关重要的作用。它可以管理海量的 Bean 实例处理复杂的模块间依赖关系例如在一个电商系统中订单模块、用户模块、商品模块等众多业务模块对应的服务层、数据访问层等各类 Bean 都可以通过 Spring IOC 容器进行统一管理实现解耦式的开发和部署。开发团队可以专注于各自模块的业务逻辑开发通过配置XML 或注解定义好 Bean 之间的依赖由 Spring 容器负责在启动时完成实例化和装配确保整个系统能够稳定、高效地运行。微服务架构在微服务架构下每个微服务本身也是一个相对独立的 Java 应用Spring IOC 容器同样适用。它可以帮助微服务轻松管理内部的各种组件 Bean比如在一个用户认证微服务中认证服务、权限管理服务、用户信息服务等相关 Bean 通过 Spring IOC 容器进行创建和依赖管理并且结合 Spring Cloud 等相关框架还能实现微服务之间的配置管理、服务发现等功能使得微服务架构下的应用开发更加便捷、高效各个微服务能够灵活地进行独立开发、部署和扩展同时又能通过合适的机制进行协同工作。测试环境搭建在进行单元测试、集成测试等不同层次的测试时Spring IOC 容器也大有用处。可以方便地在测试类中通过配置通常采用注解配置方式更简洁创建一个轻量级的 Spring 容器将被测对象以及其依赖的对象都作为 Bean 纳入容器管理然后通过容器获取相关 Bean 实例进行测试操作这样能够更真实地模拟应用程序的实际运行环境准确地测试出各个组件的功能以及它们之间的协作是否正常提高测试的全面性和有效性。