
《SpringBoot 3入门与应用实战》第 2 章 IOC 思想与实现 阅读笔记 62.6.4 复杂类型注入复杂类型的数据如数组、List、Set、Map、Properties等这些注入通常是在XML配置文件中使用。基于注解驱动的复杂类型注入通常会在配置类中 Bean 注解标注的方法内通过编程式注入。1.数组、List、Set注入基于 XML 配置文件注入属性时2.2.2 节中都是编写 标签的属性但 标签中也可以编写标签体。借助 IDEA 可以发现 标签中可以编写的子标签非常多。数组、List、Set 的注入方式是相似的。、、 标签内部可以继续嵌套Set 集合注入时内部除了用 标签引用了外部声明的 id 为 “mimi” 的 bean 对象还直接使用 标签创建了一个没有 id 的“匿名 bean 对象”这种写法与 Java 中的匿名内部类很相似它们既没有名字也不能被其他类和对象引用。?xml version1.0 encodingUTF-8?beansxmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbeanclasscom.yangjunbo.di.examplei.Personpropertynamenamesarrayvalue张三/valuevalue三三来迟/value/array/propertypropertynametelslistvalue88881234/valuevalue12345678/value/list/propertypropertynamecatssetbeanclasscom.yangjunbo.di.examplei.Cat/refbeanmimi//set/property/beanbeanidmimiclasscom.yangjunbo.di.examplei.Cat//beans2.Map、Properties注入Map 和 Properties 的底层是以键值对的方式存储。Map 集合在迭代时使用 Entry 来获取键 (key) 和值 (value)Properties 也类似但是 key 和 value 只能是 String 类型。?xml version1.0 encodingUTF-8?beansxmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbeanclasscom.yangjunbo.di.examplei.Personpropertynamenamesarrayvalue张三/valuevalue三三来迟/value/array/propertypropertynametelslistvalue88881234/valuevalue12345678/value/list/propertypropertynamecatssetbeanclasscom.yangjunbo.di.examplei.Cat/refbeanmimi//set/propertypropertynameeventsmapentrykey8:00value起床/!-- 撸猫 --entrykey9:00value-refmimi/!-- 买猫 --entrykey14:00beanclasscom.yangjunbo.di.examplei.Cat//entryentrykey18:00value睡觉//map/propertypropertynamepropspropspropkeysex男/proppropkeyage18/prop/props/property/beanbeanidmimiclasscom.yangjunbo.di.examplei.Catpropertynamenamevaluemimi//bean/beanspackagecom.yangjunbo.di.examplei;importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassexampleIApplication{publicstaticvoidmain(String[]args)throwsException{BeanFactorybeanFactorynewClassPathXmlApplicationContext(di/exampleh/inject-list.xml);PersonpersonbeanFactory.getBean(Person.class);System.out.println(person);}}2.6.5 回调注入回调注入是一种比较特殊的注入方式它由 Spring Framework 的扩展机制得来。在实际的项目开发中如果我们遇到某些 Bean 需要注入Spring Framework的 内置组件如 ApplicationContext则需要利用回调注入来实现。在较高版本的 Spring Framework 中使用 Autowired 注解即可在大多数普通 Bean 中注入 Spring Framework 的内置组件但还是有一小部分特殊的 Bean 中无法正常使用 Autowired 注解注入该部分内容会在后续的高级篇中讲解。1.回调的根源Aware回调注入机制源自 Spring Framework 3.1版本它的核心是一个接口Aware。Aware 本身内部没有定义任何方法只是一个类似于Serializable 的标识性接口但是其下有一系列子接口。借助 IDEA 观察继承关系可以发现 Aware 接口的子接口非常多而且从命名方式能得知这些接口分别是用来注入哪个组件的如 ApplicationContextAware 注入的组件是 ApplicationContext。常用的子接口绝大多数可以借助Autowired 注解注入个别需要 Aware 接口来帮忙注入。下面分别介绍两个相对有代表性的 Aware 子接口的回调注入使用。2.ApplicationContextAwareApplicationContextAware 的使用方式非常简单只需要在需要注入的类中实现该接口并重写 setApplicationContext 方法即可。AwaredTestBean 中有一个 ApplicationContext 类型的变量 ctx该变量在 setApplicationContext 方法被回调时赋值。随后使用组件扫描的方式驱动 IOC 容器获取 AwaredTestBean 并调用 printBeanNames 方法检验效果。运行 AwareApplication 的 main 方法可以发现 IOC 容器中的组件名称被一一打印证明 ApplicationContext 被成功注入。packagecom.yangjunbo.di.examplej;importorg.springframework.beans.BeansException;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.stereotype.Component;importjava.util.stream.Stream;Component()publicclassAwaredTestBeanimplementsApplicationContextAware{privateApplicationContextctx;publicvoidprintBeanNames(){Stream.of(ctx.getBeanDefinitionNames()).forEach(System.out::println);}OverridepublicvoidsetApplicationContext(ApplicationContextctx)throwsBeansException{this.ctxctx;}}packagecom.yangjunbo.di.examplej;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassAwareApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctxnewAnnotationConfigApplicationContext(com.yangjunbo.di.examplej);AwaredTestBeanbbbctx.getBean(AwaredTestBean.class);bbb.printBeanNames();}}3.BeanNameAware如果当前的 Bean 需要依赖它本身的名称那么使用注解 Autowired 和 Value 将无法注入此时就需要使用 BeanNameAware 接口来辅助注入当前 Bean 的 Name。AwaredTestBean 实现 BeanNameAware 接口并增加 getName 方法用于打印自身的 beanName。启动类 AwareApplication添加 AwaredTestBean 的 getName 方法调用并打印。重新运行 main 方法可以发现控制台正确打印 “awaredTestBean”证明 BeanNameAware 接口也正常运行。packagecom.yangjunbo.di.examplej;importorg.springframework.beans.BeansException;importorg.springframework.beans.factory.BeanNameAware;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ApplicationContextAware;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.stereotype.Component;importjava.util.stream.Stream;Component()publicclassAwaredTestBeanimplementsApplicationContextAware,BeanNameAware{privateStringbeanName;privateApplicationContextctx;publicStringgetName(){returnbeanName;}publicvoidprintBeanNames(){Stream.of(ctx.getBeanDefinitionNames()).forEach(System.out::println);}OverridepublicvoidsetApplicationContext(ApplicationContextctx)throwsBeansException{this.ctxctx;}OverridepublicvoidsetBeanName(Stringname){this.beanNamename;}}packagecom.yangjunbo.di.examplej;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassAwareApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctxnewAnnotationConfigApplicationContext(com.yangjunbo.di.examplej);AwaredTestBeanbbbctx.getBean(AwaredTestBean.class);bbb.printBeanNames();bbb.getName();}}2.6.6 延迟注入延迟注入对应 2.5 节的延迟查找它解决的是不确定被注入的 Bean 是否存在的问题。延迟注入所用到的核心 API 依然是 ObjectProvider。演示了 3 种依赖注入的情况下ObjectProvider的使用方式。packagecom.yangjunbo.di.examplek;/** * ClassName: Person * Package: com.yangjunbo.di.examplek * Description: * * Author 杨钧博 * Create 2026/8/13 10:02 * Version 1.0 */publicclassPerson{}packagecom.yangjunbo.di.examplek;importorg.springframework.beans.factory.ObjectProvider;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;/** * ClassName: Dog * Package: com.yangjunbo.di.examplek * Description: * * Author 杨钧博 * Create 2026/8/13 10:01 * Version 1.0 */ComponentpublicclassDog{// setter方法延迟注入privatePersonperson;AutowiredpublicvoidsetPerson(ObjectProviderPersonperson){// 有Bean才取出注入this.personperson.getIfAvailable();}}packagecom.yangjunbo.di.examplek;importcom.yangjunbo.annotation.exampleb.Person;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;/** * ClassName: exampleKApplication * Package: com.yangjunbo.di.examplek * Description: * * Author 杨钧博 * Create 2026/8/13 10:03 * Version 1.0 */publicclassexampleKApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctxnewAnnotationConfigApplicationContext(com.yangjunbo.di.examplek);System.out.println(ctx.getBean(Dog.class));}}3 种依赖注入的情况下 ObjectProvider 的使用方式packagecom.yangjunbo.di.examplek;importorg.springframework.beans.factory.ObjectProvider;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;/** * ClassName: Dog * Package: com.yangjunbo.di.examplek * Description: * * Author 杨钧博 * Create 2026/8/13 10:01 * Version 1.0 */ComponentpublicclassDogA{// setter方法延迟注入privatePersonperson;AutowiredpublicvoidsetPerson(ObjectProviderPersonperson){// 有Bean才取出注入this.personperson.getIfAvailable();}}packagecom.yangjunbo.di.examplek;importorg.springframework.beans.factory.ObjectProvider;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;/** * ClassName: Dog * Package: com.yangjunbo.di.examplek * Description: * * Author 杨钧博 * Create 2026/8/13 10:01 * Version 1.0 */ComponentpublicclassDogB{// 构造器延迟注入privatePersonperson;AutowiredpublicDogB(ObjectProviderPersonperson){// 如果没有Bean就采用默认策略创建this.personperson.getIfAvailable(Person::new);}}packagecom.yangjunbo.di.examplek;importorg.springframework.beans.factory.ObjectProvider;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;/** * ClassName: Dog * Package: com.yangjunbo.di.examplek * Description: * * Author 杨钧博 * Create 2026/8/13 10:01 * Version 1.0 */ComponentpublicclassDogC{// 属性延迟注入AutowiredprivateObjectProviderPersonperson;OverridepublicStringtoString(){// 每用一次都要调用一次getIfAvailable方法returnDog{personperson.getIfAvailable(Person::new)};}}packagecom.yangjunbo.di.examplek;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;/** * ClassName: exampleKApplication * Package: com.yangjunbo.di.examplek * Description: * * Author 杨钧博 * Create 2026/8/13 10:03 * Version 1.0 */publicclassexampleKApplication{publicstaticvoidmain(String[]args)throwsException{ApplicationContextctxnewAnnotationConfigApplicationContext(com.yangjunbo.di.examplek);System.out.println(ctx.getBean(DogA.class));System.out.println(ctx.getBean(DogB.class));System.out.println(ctx.getBean(DogC.class));}}2.7 小结本章从原生 Servlet 的三层架构开始推演代码结构的演变直至IOC思想的产生。IOC 的核心思想是将主动权交予 IOC 容器由 IOC 容器掌控依赖关系。IOC 思想的核心实现方式有依赖查找和依赖注入。Spring Framework 中 IOC 容器的底层实现包含 BeanFactory 与 ApplicationContext其中 BeanFactory 实现了最基本的 Bean 容器ApplicationContext 在此基础上添加了诸如 AOP、事件驱动、后置处理器等众多特性。