news 2026/8/13 8:11:51

《SpringBoot 3:入门与应用实战》第 2 章 IOC 思想与实现 阅读笔记 3

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《SpringBoot 3:入门与应用实战》第 2 章 IOC 思想与实现 阅读笔记 3

《SpringBoot 3:入门与应用实战》第 2 章 IOC 思想与实现 阅读笔记 3

2.4 注解驱动的 IOC

从 Spring Boot 发布以来,使用 XML 配置文件的场景越来越少,取而代之的是基于注解配置类来驱动 IOC 容器。从 Spring Framework 推出3.0版本后,支持的最低 Java 版本为 Java 5,Java 5 最有特点的新特性之一就是引入了注解,Spring Framework 3.0 开始也通过引入大量注解代替XML 的方式进行声明式开发。

2.4.1 注解驱动 IOC 的依赖查找

相较于 XML 配置文件的驱动方式,基于注解驱动的配置会全部编写在配置类中,一个配置类可以理解为一个 XML 配置文件。配置类只需要在类上标注一个 @Configuration 注解。
与 XML 配置文件使用 标签的方式类比,通过注解配置类注册 Bean 所使用的是 @Bean 注解。方法的返回值代表注册的类型,方法名代表 Bean 的 id。也可以直接在 @Bean 注解中使用 name 属性显式地声明 Bean 的 id。

packagecom.yangjunbo.annotation.examplea;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassQuickstartConfiguration{// 等同于 <bean id="person" class="com.yangjunbo.annotation.examplea.Person"/>@Bean(name="person")publicPersonperson(){returnnewPerson();}}

注解驱动的场景下,Spring Framework 提供的是 AnnotationConfigApplicationContext,用作注解驱动 IOC 容器。整体的编码方式非常简单,甚至与 XML 配置文件的方式没有什么区别。

packagecom.yangjunbo.annotation.examplea;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassAnnotationConfigApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(QuickstartConfiguration.class);Personperson=ctx.getBean(Person.class);System.out.println(person);}}

2.4.2 注解驱动 IOC 的依赖注入

编写基于 @Bean 注解的依赖注入,注入哪些属性完全是在代码中编写的。这种写法与使用 XML 配置文件的方式完全等价。

packagecom.yangjunbo.annotation.examplea;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassAnnotationDIConfiguration{@BeanpublicPersonperson(){Personperson=newPerson();person.setName("person");person.setAge(123);returnperson;}@BeanpublicCatcat(){Catcat=newCat();cat.setName("test-cat-anno");// 直接拿上面的person()方法作为返回值即可,相当于refcat.setMaster(person());returncat;}}

packagecom.yangjunbo.annotation.examplea;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassAnnotationConfigApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(QuickstartConfiguration.class);Personperson=ctx.getBean(Person.class);System.out.println(person);ApplicationContextctx2=newAnnotationConfigApplicationContext(AnnotationDIConfiguration.class);Catcat=ctx2.getBean(Cat.class);System.out.println(cat);}}

2.4.3 组件注册与扫描机制

翻看 AnnotationConfigApplicationContext 的构造方法,可以发现它还有一个传入 basePackage 的构造方法​,要想理解这个概念,就需要了解注解驱动中一个非常重要的机制:组件注册与扫描。

1.组件注册的根源:@Component

Spring Framework 规定,当一个类上标注了 @Component 注解(并被组件扫描)时,这个类将在 IOC 容器初始化时被创建,并注册到 IOC 容器中成为一个 Bean。
默认情况下,组件生成的 beanName 为“首字母小写的类名”(例如 Person 的默认名称是 person,DemoServiceImpl 的默认名称是demoServiceImpl)​。如果需要指定 Bean 的名称,就在 @Component 注解中声明 value 属性。
注意,如果 标签不声明 id 属性,默认生成的 Bean 的名称不是“首字母小写的类名”​,这一点与注解扫描的规则不同!

packagecom.yangjunbo.annotation.exampleb;importorg.springframework.stereotype.Component;@ComponentpublicclassPerson{}

2.组件扫描

如果只是将 @Component 注解标注在类上而不进行扫描,那么 IOC 容器将无法感知到组件注册,当没有扫描组件时强行获取 Bean,一定会抛出 NoSuchBeanDefinitionException 异常,为此需要引入一个新的注解用于组件扫描:@ComponentScan。
@ComponentScan注解通常标注在配置类上。在代码清单2-31中,我们在配置类ComponentScanConfiguration上额外标注一个@ComponentScan 注解,并指定要扫描的包路径,它就可以扫描指定路径包及子包下的所有 @Component 组件。如果不指定扫描路径,就默认扫描本类所在包及子包下的所有 @Component 组件。可以一次性声明多个扫描的包。
声明了 @ComponentScan 之后,重新启动配置类,可以发现 Person 已经成功被注册,如果 Spring Framework 的版本比较老,可能会看到如下的写法:@ComponentScan(basePackages=“com.linkedbear.spring.annotation.c_scan.bean”),这两个写法实质上是一样的。

packagecom.yangjunbo.annotation.exampleb;importorg.springframework.context.annotation.ComponentScan;importorg.springframework.context.annotation.Configuration;@Configuration@ComponentScan("com.yangjunbo.annotation.exampleb")publicclassComponentScanConfiguration{}

Spring Framework 还提供了第二种组件扫描的使用方式。在 AnnotationConfigApplicationContext 的构造方法中有一个类型为 String 可变参数的构造方法,当我们传入需要扫描的包之后,也可以直接扫描到那些标注了 @Component 的 Bean 并注册到 IOC 容器中。

packagecom.yangjunbo.annotation.exampleb;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassAnnotationConfigApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(ComponentScanConfiguration.class);System.out.println(ctx.getBean(Person.class));ApplicationContextctx2=newAnnotationConfigApplicationContext("com.yangjunbo.annotation.exampleb");System.out.println(ctx2.getBean(Person.class));}}

对于 XML 配置文件驱动的 IOC 容器同样可以启用组件扫描,只需要在 XML 配置文件中声明一个标签 context:component-scan,之后使用ClassPathXmlApplicationContext 驱动 IOC 容器,同样可以获取 person 对象。

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><context:component-scanbase-package="com.yangjunbo.annotation.exampleb"/></beans>

packagecom.yangjunbo.annotation.exampleb;importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassAnnotationConfigApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(ComponentScanConfiguration.class);System.out.println(ctx.getBean(Person.class));ApplicationContextctx2=newAnnotationConfigApplicationContext("com.yangjunbo.annotation.exampleb");System.out.println(ctx2.getBean(Person.class));BeanFactorybeanFactory=newClassPathXmlApplicationContext("annotation/inject-scan.xml");Personperson=beanFactory.getBean(Person.class);System.out.println(person);}}

3.组件注册的其他注解

Spring Framework 为了迎合 Web 应用开发时所采用的经典三层架构,额外提供了三个注解:@Controller、@Service、@Repository,分别对应三层架构中的表现层、业务层、持久层。这三个注解的作用与 @Component 完全一致,其实它们的底层也都是 @Component。
有了以上三个注解,在进行符合三层架构的应用开发时,对于那些业务逻辑层的类​,就可以直接标注注解,而不用一个个地写 标签或者借助 @Bean 注解进行组件注册。

4.@Configuration 也是 @Component

如果在驱动注解 IOC 容器时,直接扫描包括配置类在内的整个根包,则在运行 main 方法时会看到配置类 ComponentScanConfiguration 也被注册到 IOC 容器中。

packagecom.yangjunbo.annotation.exampleb;importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;importjava.util.stream.Stream;publicclassAnnotationConfigApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctx=newAnnotationConfigApplicationContext(ComponentScanConfiguration.class);System.out.println(ctx.getBean(Person.class));ApplicationContextctx2=newAnnotationConfigApplicationContext("com.yangjunbo.annotation.exampleb");System.out.println(ctx2.getBean(Person.class));BeanFactorybeanFactory=newClassPathXmlApplicationContext("annotation/inject-scan.xml");Personperson=beanFactory.getBean(Person.class);System.out.println(person);ApplicationContextctx3=newAnnotationConfigApplicationContext("com.yangjunbo.annotation.exampleb");String[]beanNames=ctx3.getBeanDefinitionNames();Stream.of(beanNames).forEach(System.out::println);}}

翻看 @Configuration 注解的源码,可以发现它也被标注了 @Component 注解,说明被标注 @Configuration 注解的类对于 IOC 容器而言同样也是一个 Bean。

2.4.4 注解驱动与XML驱动互通

如果一个应用中既有注解驱动配置,又有 XML 配置文件,则可能出现由一方引入另一方配置的情况。

1.XML 配置文件引入注解驱动

在 XML 中要引入注解驱动,需要开启注解配置,同时注册对应的配置类。

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 开启注解配置 --><context:annotation-config/><beanclass="com.yangjunbo.annotation.examplec.AnnotationDIConfiguration"/></beans>

packagecom.yangjunbo.annotation.examplec;importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassAnnotationConfigApplication{publicstaticvoidmain(String[]args)throwsException{BeanFactorybeanFactory=newClassPathXmlApplicationContext("annotation/examplec/inject-annotation.xml");Catcat=beanFactory.getBean(Cat.class);System.out.println(cat);}}

2.注解配置类引入 XML 配置文件

在注解配置类中引入 XML 配置文件,需要在配置类上标注 @ImportResource 注解,并声明配置文件的路径。

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><beanid="person"class="com.yangjunbo.annotation.examplec.Person"><propertyname="name"value="test-person-byset"/><propertyname="age"value="18"/></bean><beanid="cat"class="com.yangjunbo.annotation.examplec.Cat"><propertyname="name"value="test-cat"/><!-- ref引用上面的person对象 --><propertyname="master"ref="person"/></bean></beans>

packagecom.yangjunbo.annotation.examplec;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.ImportResource;@Configuration@ImportResource("classpath:annotation/examplec/inject-configuration.xml")publicclassXmlDIConfiguration{}

packagecom.yangjunbo.annotation.examplec;importcom.yangjunbo.annotation.exampleb.ComponentScanConfiguration;importcom.yangjunbo.annotation.exampleb.Person;importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassAnnotationConfigApplication{publicstaticvoidmain(String[]args)throwsException{BeanFactorybeanFactory=newClassPathXmlApplicationContext("annotation/examplec/inject-annotation.xml");Catcat=beanFactory.getBean(Cat.class);System.out.println(cat);ApplicationContextctx=newAnnotationConfigApplicationContext(XmlDIConfiguration.class);System.out.println(ctx.getBean(Cat.class));}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/13 8:10:45

第一次用tpg,封装质量怎么样?

对于初次接触钱币评级的藏友而言&#xff0c;封装质量往往是决定是否送评的关键考量之一。tpg泰平评级作为国内较早建立标准化封装体系的机构之一&#xff0c;其封装工艺与品相判定逻辑紧密关联&#xff0c;直接影响藏品的长期保存状态与市场流通表现。本文将从封装结构、材料选…

作者头像 李华
网站建设 2026/8/13 8:08:24

嵌入式TLS安全实践:BearSSL在资源受限MCU上的集成与优化

1. 项目概述&#xff1a;为什么嵌入式开发者需要关注BearSSL&#xff1f; 如果你在嵌入式领域摸爬滚打过几年&#xff0c;尤其是在资源受限的MCU上折腾过TLS/SSL&#xff0c;那你大概率经历过这样的痛苦&#xff1a;想用OpenSSL&#xff0c;发现它动辄几兆的库体积&#xff0c;…

作者头像 李华
网站建设 2026/8/13 8:08:20

补体-炎症交叉对话的“双通道”——C5a/C5b-9/IFNg/IL6四因子Panel为补体激活相关炎症研究提供精准“分子听诊器”

云克隆Luminex液相悬浮芯片技术实现补体系统激活强度与下游炎症效应的同步定量&#xff0c;从“补体打了多大一仗”到“炎症烧了多大一片”一管血全掌握近日&#xff0c;武汉云克隆科技股份有限公司正式推出C5a、C5b-9、IFNg、IL6四因子联合检测Panel&#xff0c;依托云克隆液相…

作者头像 李华
网站建设 2026/8/13 8:07:57

Java开发日志:一次线上内存溢出问题的排查与反思

凌晨一点四十七分&#xff0c;手机在床头柜上震动&#xff0c;像一只被踩住翅膀的知了。我摸到屏幕&#xff0c;钉钉群里的红色告警正一闪一闪&#xff1a;生产A应用内存使用率超过95%&#xff0c;服务已OOM并自动重启。这是当天第三次了。前两次&#xff0c;我都以为只是流量峰…

作者头像 李华
网站建设 2026/8/13 8:07:27

Meta开源大模型本地部署实战:从Llama 3选型到API工程化

这次我们来看一个 Meta 开放模型的项目。这不是一个具体的工具或软件&#xff0c;而是一个关于 Meta 公司开放其人工智能模型&#xff0c;推动“个人超级智能”普及的趋势性话题。对于开发者、研究者和技术爱好者来说&#xff0c;这意味着我们有机会在本地或云端部署、微调和集…

作者头像 李华
网站建设 2026/8/13 8:07:13

机器学习入门:核心概念与实践指南

1. 机器学习初探&#xff1a;从零开始的认知之旅第一次接触"机器学习"这个概念时&#xff0c;我正坐在大学计算机实验室里&#xff0c;盯着屏幕上那些看似毫无规律的代码发呆。那是在2012年&#xff0c;AlphaGo还没战胜李世石&#xff0c;但机器学习已经悄悄改变着我…

作者头像 李华