# spring-lessons **Repository Path**: RingoTangs/spring-lessons ## Basic Information - **Project Name**: spring-lessons - **Description**: spring、IoC的学习 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-08-20 - **Last Updated**: 2022-02-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: Spring, Java, ioc, 源码 ## README # 一、Spring IoC容器概述 ## 1. 依赖查找 ### 1.1. 环境搭建 ```xml org.springframework spring-context ``` 在Maven `resource` 目录下创建 `META-INF` 文件夹,写 spring 的配置文件。 ```xml ``` ### 1.2. 根据Bean名称查找 根据 `Bean` 名称查找包括: - 实时查找。 - 延时查找。 配置 Bean: ```xml ``` 从 Ioc 容器中获取 Bean: ```java public class DependencyLookupDemo { public static void main(String[] args) { // 配置 XML 文件 // 启动 Spring Application Context String configLocation = "classpath:/META-INF/dependency-look-context.xml"; BeanFactory beanFactory = new ClassPathXmlApplicationContext(configLocation); lookUpInRealTime(beanFactory); lookUpInLazy(beanFactory); } // 1: 延时查找 private static void lookUpInLazy(BeanFactory beanFactory) { // 并没有实例化我们注入的 bean ObjectFactory objectFactory = (ObjectFactory) beanFactory.getBean("objectFactory"); // 在这里实例化注入的 bean User user = objectFactory.getObject(); System.out.println("延时查找: " + user); } // 2: 实时查找 private static void lookUpInRealTime(BeanFactory beanFactory) { User user = (User) beanFactory.getBean("user"); System.out.println("实时查找: " + user); } } ``` **延迟加载**:ObjectFactory 对象并不是直接返回了实际的 Bean,而是一个 Bean 的查找代理。当得到 ObjectFactory 对象时,相当于 Bean 没有被创建,只有当 getObject() 方法时,才会触发 Bean 实例化等生命周期。 ### 1.2. 根据Bean类型查找 ```java public class DependencyLookupDemo { public static void main(String[] args) { // 配置 XML 文件 // 启动 Spring Application Context String configLocation = "classpath:/META-INF/dependency-look-context.xml"; BeanFactory beanFactory = new ClassPathXmlApplicationContext(configLocation); lookupByType(beanFactory); lookupCollectionByType(beanFactory); } // 1: 根据类型查找 Bean, 以集合的方式返回 private static void lookupCollectionByType(BeanFactory beanFactory) { if (beanFactory instanceof ListableBeanFactory) { ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory; Map users = listableBeanFactory.getBeansOfType(User.class); System.out.println("查找到的所有 User 集合对象: " + users); } } // 2: 按照类型查找 private static void lookupByType(BeanFactory beanFactory) { User user = beanFactory.getBean(User.class); System.out.println("按照类型查找: " + user); } } ``` 根据类型查找Bean,并以集合的方式返回,和 spring-boot 中注入类似: ```java @Autowired Map users; ``` ### 1.3. 根据Java注解查找 自定义注解: ```java /** * 自定义注解 * * @author Ringo * @date 2021/8/21 23:51 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Super { } ``` 创建父类: ```java public class User { private Long id; private String name; // ..get set } ``` 创建子类: ```java // 标注上 @Super 注解 @Super public class SuperUser extends User{ private String address; // ..get set } ``` 将 `User、SuperUser` 加入到 IoC 容器: 注:`@Primary` 有同样的效果。 ```xml ``` 在 IoC 容器中安装注解查找 Bean: ```java public class DependencyLookupDemo { public static void main(String[] args) { // 配置 XML 文件 // 启动 Spring Application Context String configLocation = "classpath:/META-INF/dependency-look-context.xml"; BeanFactory beanFactory = new ClassPathXmlApplicationContext(configLocation); // 按注解类型查找bean lookupByAnnotationType(beanFactory); } // 通过注解方式查找 private static void lookupByAnnotationType(BeanFactory beanFactory) { if (beanFactory instanceof ListableBeanFactory) { ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory; Map users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class); System.out.println(users); } } } ``` ## 2. 依赖注入 ### 2.1. 集合Bean对象注入 创建`UserRepository`类,属性为集合。 ```java public class UserRepository { private Collection users; // get set... } ``` `dependency-injection-context.xml` 写一个新的配置文件。 注意:新的配置会导入之前的配置 `dependency-look-context.xml`。 ```xml ``` 在IoC容器中查找 `UserRepository` 的bean。 ```java public class DependencyInjectionDemo { public static void main(String[] args) { // 这里使用的是新创建的配置 String configLocation = "classpath:/META-INF/dependency-injection-context.xml"; BeanFactory beanFactory = new ClassPathXmlApplicationContext(configLocation); lookupInType(beanFactory); } private static void lookupInType(BeanFactory beanFactory) { UserRepository userRepository = beanFactory.getBean(UserRepository.class); System.out.println(userRepository); } } ``` ### 2.2. 注入内建非Bean对象 `UserRepository` 中的属性为 `BeanFactory`: ```java public class UserRepository { private BeanFactory beanFactory; // get set... } ``` 将 `UserRepository` 直接加入到容器, 并自动注入: ```xml ``` 依赖查找 和 依赖注入两种方式查看: ```java public class DependencyInjectionDemo { public static void main(String[] args) { String configLocation = "classpath:/META-INF/dependency-injection-context.xml"; BeanFactory beanFactory = new ClassPathXmlApplicationContext(configLocation); // 依赖注入 System.out.println(beanFactory.getBean(UserRepository.class).getBeanFactory()); // 依赖查找 // 内建依赖不能通过依赖查找找到 System.out.println(beanFactory.getBean(BeanFactory.class)); } } ``` - 依赖注入输出结果:org.springframework.beans.factory.support.DefaultListableBeanFactory@7a5d012c: defining beans [user,superUser,objectFactory,userRepository]; root of factory hierarchy。 - 依赖查找则报错:No qualifying bean of type 'org.springframework.beans.factory.BeanFactory' available。 ## 3. Spring IoC依赖来源 Spring IoC依赖来源: 1. 自定义 Bean。 2. 容器内建 Bean 对象。 3. 容器内建依赖(依赖注入,非Bean)。 ```java // 依赖来源一:自定义Bean UserRepository userRepository = beanFactory.getBean(UserRepository.class); // 依赖来源二:依赖注入(容器内建依赖) BeanFactory 不是Bean, 不能用依赖查找 // 但是可以用【依赖注入】的方式来获取 System.out.println(userRepository.getBeanFactory()); // 依赖来源三:容器内创建的Bean Environment environment = beanFactory.getBean(Environment.class); ``` 注意:自定义的bean和内建的bean能够通过依赖查找查询到,但是内建依赖是不能通过依赖查找查询到,因为他们的来源不同。 BeanFactory 并不是 Bean,Java Bean 是一种功能组件,它是 Java对象的方式封装。这里重点是讨论 IoC 依赖的来源,所谓依赖就是被组合的对象。 ## 4. Spring IoC配置元信息 Bean定义配置: - 基于 XML 文件。 - 基于 Properties 文件。 - 基于 Java 注解。 - 基于 Java API。 IoC容器配置: - 基于 XML 文件。 - 基于 Java 注解。 - 基于 Java API。 外部化属性配置: - 基于 Java 注解(例如:Spring Boot中使用的 @Value)。 ## 5. BeanFactory和ApplicationContext谁是IoC容器? 回顾之前创建的 `UserRepository`: ```java public class UserRepository { private BeanFactory beanFactory; // get set... } ``` 将 `UserRepository` 加入到容器中,并且配置 `autowired`。 ```xml ``` 但是为什么等式不成立? ```java String configLocation = "classpath:/META-INF/dependency-injection-context.xml"; BeanFactory beanFactory = new ClassPathXmlApplicationContext(configLocation); // 依赖注入 UserRepository userRepository = beanFactory.getBean(UserRepository.class); // 这个等式为什么不成立? System.out.println(userRepository.getBeanFactory() == beanFactory); ``` - ApplicationContext是BeanFactory的子接口,说明ApplicationContext is BeanFactory。并且ApplicationContext 是BeanFactory的包装类,也就是内部组合了BeanFactory的实现-DefaultListableBeanFactory。 - 为什么包装了DefaultListableBeanFactory,因为它需要简化且丰富功能来为企业开发提供更高的便捷性,也就是说ApplicationContext 是DefaultListableBeanFactory的超集。 - 至于为什么UserRepository注入的BeanFactory 不等于ClassPathXmlApplicationContext得到的BeanFactory ,是因为AbstractApplicationContext#prepareBeanFactory中 指明了 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); 也就是说当byType是BeanFactory.class的时候,获得是的ApplicationContext中的DefaultListableBeanFactory对象。 - 那真正的IOC的底层实现就是BeanFactory的实现类,因为ApplicationContext是委托DefaultListableBeanFactory来操作getBean等方法的。 `ApplicationContext` 就是 `BeanFactory`,只不过子类比父类功能更强大。 实际上,可以用 `ApplicationContext` 来 代替 `BeanFactory`。 ```java String configLocation = "classpath:/META-INF/dependency-injection-context.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation); // 依赖注入 UserRepository userRepository = applicationContext.getBean(UserRepository.class); System.out.println(applicationContext); System.out.println(userRepository.getBeanFactory()); // 这个等式为什么不成立? System.out.println(userRepository.getBeanFactory() == applicationContext); // 这个等式成立 System.out.println(userRepository.getBeanFactory() == applicationContext.getAutowireCapableBeanFactory()); ``` ![类继承图](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/ClassPathXmlApplicationContext继承关系图.6g9a7x7dhi40.jpg) ## 6. Application Context特性 `ApplicationContext` 除了 IoC 容器角色, 还提供: - 面向切面(AOP)。 - 配置元信息(Configuration Metadata)。 - 资源管理(Resources)。 - 事件(Events)。 - 国际化(i18n)。 - 注解(Annotations)。 - Environment 抽象(Environment Abstract)。 ## 7. Spring IoC 容器选BeanFactory还是ApplicationContext? 结论: - `BeanFactory` 是 Spring 底层 IoC 容器。 - `ApplicationContext` 是具备应用特性的 `BeanFactory` 超集。 ### 7.1. BeanFactory 显式声明 `DefaultListableBeanFactory` 充当 Spring IoC 容器: ```java // 单纯的使用 BeanFactory 获得 IoC 容器中的 Bean // 1: 创建 BeanFactory 容器 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // 2: 加载配置 // 构造器参数是 BeanDefinitionRegistry // DefaultListableBeanFactory 恰好实现了 BeanDefinitionRegistry XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); String location = "classpath:META-INF/dependency-look-context.xml"; // 返回加载 Bean Definitions 的数量 int count = reader.loadBeanDefinitions(location); System.out.println("加载 Bean Definition 的数量: " + co User.class); System.out.println("查找到容器中所有 User Bean: " + users); ``` 如果使用 `BeanFactory` 当做 IoC 容器,就没有事件发布、国际化等特性了! ### 7.2. ApplicationContext 显示声明 `AnnotationConfigurationApplication` 来使用 Spring IoC 容器: ```java /** * 注解的 {@link ApplicationContext} 作为 IoC 容器示例 * * @author Ringo * @date 2021/8/22 21:23 */ public class AnnotationApplicationContextAsIocContainerDemo { public static void main(String[] args) { // 1: 创建 ApplicationContext 作为 IoC 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 2: 将当前类注册为 Configuration 类 applicationContext.register(AnnotationApplicationContextAsIocContainerDemo.class); // 3: 调用 refresh() 启动应用上下文(必须调用) applicationContext.refresh(); // 4: IoC 容器中找 Bean Map users = applicationContext.getBeansOfType(User.class); System.out.println("IoC 容器中查找到的User Bean: " + users); } /** * 通过 Java 注解的方式定义 Bean */ @Bean public User user() { User user = new User(); user.setId(10L); user.setName("zs"); return user; } } ``` ## 8. 面试题 ### 8.1. 什么是 Spring IoC 容器? Spring 框架实现了控制反转原则(IoC)。IoC 包括【依赖查找】和【依赖注入】。 依赖注入(DI):可以通过属性、构造器、Set方法区注入依赖。IoC 容器会把注入的依赖放到创建的Bean中。 ### 8.2. BeanFactory与FactoryBean? `BeanFactory` 是 IoC 的底层容器。 `FactoryBean` 是创建 Bean 的一种方式,帮助实现复杂的初始化逻辑。 ### 8.3. Spring IoC 容器启动时做了哪些准备? IoC 配置元信息读取和解析、IoC 容器生命周期、Spring 事件发布、国际化等。 # 二、Spring Bean基础 ## 1. BeanDefinition元信息 ### 1.1. BeanDefinition 元信息? | 属性(Property) | 说明 | | ------------------------ | ------------------------------------------------ | | Class | Bean全类目,必须是具体类,不能用抽象类或者接口。 | | Name | Bean的名称或者ID。 | | Scope | Bean的作用域(singleton、prototype等)。 | | Constructor arguments | Bean构造器参数(用于依赖注入)。 | | Properties | Bean属性设置(用于依赖注入)。 | | Autowiring mode | Bean自动绑定模式(如:通过名称 byName)。 | | Lazy initialization mode | Bean延迟初始化模式(延迟和非延迟)。 | | Initialization method | Bean初始化回调方法名称。 | | Destruction method | Bean销毁回调方法名称。 | ### 1.2. BeanDefinition构建 - 通过BeanDefinitionBuilder。 - 通过 AbstractBeanDefinition 以及派生类。 ```java // 1: 通过 BeanDefinitionBuilder 构建 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class); // 通过属性设置 beanDefinitionBuilder .addPropertyValue("id", 10) .addPropertyValue("name", "张三"); // 获取 BeanDefinition 实例 BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition(); // BeanDefinition 并非 Bean 最终状态, 可以自定义修改 // 2. 通过 AbstractBeanDefinition 以及派生类 GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition(); // 设置 Bean 类型 genericBeanDefinition.setBeanClass(User.class); // 通过 MutablePropertyValues 批量操作属性 MutablePropertyValues propertyValues = new MutablePropertyValues(); // propertyValues.addPropertyValue("id", "80"); // propertyValues.addPropertyValue("name", "李四"); // 和上面 addPropertyValue() 方法效果一样 propertyValues .add("id", "80") .add("name", "李四"); // 通过 set MutablePropertyValues 批量操作属性 genericBeanDefinition.setPropertyValues(propertyValues); ``` ## 2. Bean别名简单使用 Bean 别名(Alias)的价值: - 复用现有的 BeanDefinition 。 - 更具有场景化的命名方法。 配置 Bean 的信息和别名: ```xml ``` IoC 容器中根据 Alias 查找 Bean: ```java // 1: 创建 IoC 容器 加载配置元信息 String configLocation = "classpath:META-INFO/bean-definitions-context.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation); // 2: 依赖查找 // 通过 Bean Name 查找 User user = (User) applicationContext.getBean("user"); // 通过 Bean Alias 查找 User ringo = (User) applicationContext.getBean("ringo-user"); System.out.println("user: " + user); System.out.println("ringo: " + ringo); System.out.println("user == ringo ? " + (user == ringo)); // true ``` ## 3. BeanDefinition注册到IoC容器 BeanDefinition注册到IoC容器: - XML配置元信息: - ``。 - Java 注解配置元信息: - @Bean。 - @Component。 - @Import。 - Java API配置元信息 - 命名方式:`BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)`。 - 非命名方式:`BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition, BeanDefinitionRegistry)`。 - 配置方式:`AnnotatedBeanDefinitionReader#Register(Class...)`。 注意:Java API 的【配置方式】实现是 `AnnotationConfigApplicationContext` 的 `registter(Class...)`方法。 ### 3.1. Java 注解注册Bean ```java /** * 注解 {@link org.springframework.beans.factory.config.BeanDefinition} 示例 * * @author Ringo * @date 2021/8/23 23:19 */ // 3: 通过 @Import 来进行导入 // @Import 配合 @Configuration 一起使用, 将标记的类加入到 IoC 容器 @Import(AnnotationBeanDefinitionDemo.Config.class) @Configuration public class AnnotationBeanDefinitionDemo { public static void main(String[] args) { // 创建 IoC 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 注册 Configuration Class(配置类) // 等同于在 Config 类上添加 @Configuration 注解 // applicationContext.register(AnnotationBeanDefinitionDemo.class); // 这里使用包扫描 applicationContext.scan("com.ymy.thinking.in.spring.bean.definition.**"); // 启动 Spring 应用上下文 applicationContext.refresh(); // 依赖查找 Map configBeans = applicationContext.getBeansOfType(Config.class); Map userBeans = applicationContext.getBeansOfType(User.class); System.out.println("Config 类型所有的 Beans :" + configBeans); System.out.println("User 类型所有的 Beans :" + userBeans); // 显示关闭 Spring 应用上下文 applicationContext.close(); } // 2. 通过 @Component 方式定义 Bean // 使用了 @Import 方式, 就不使用 @Component 了(两种方式 任选其一) // @Component public static class Config { // 1: 通过 @Bean 的方式定义 Bean /** * 通过 Java 注解的方式, 定义了一个 Bean. * 配置 Bean 的别名. */ @Bean(name = {"user", "ringo-user"}) public User user() { User user = new User(); user.setId(8848L); user.setName("李四"); return user; } } } ``` ### 3.2. Java API注册Bean ```java /** * Java API 注册 {@link org.springframework.beans.factory.config.BeanDefinition} 到 IoC 容器 * * @author Ringo * @date 2021/8/24 22:22 */ public class JavaAPIRegistryBeanDefinitionDemo { public static void main(String[] args) { // 创建 IoC 容器 // AnnotationConfigApplicationContext 实现了 BeanDefinitionRegistry接口 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 包扫描 applicationContext.scan("com.ymy.thinking.in.spring.bean.definition.**"); // 命名注册 registerBeanDefinition(applicationContext, "zs-user"); // 非命名注册 registerBeanDefinition(applicationContext); // 开启 Spring 应用上下文 applicationContext.refresh(); // 依赖查找 Map users = applicationContext.getBeansOfType(User.class); System.out.println("IoC 容器中所有 User 类型的 Bean: "); users.forEach((k, v) -> System.out.println(k + "=" + v)); // 关闭 Spring 应用上下文 applicationContext.close(); } // 命名方式 注册 public static void registerBeanDefinition(BeanDefinitionRegistry registry, String beanName) { BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(User.class) .addPropertyValue("id", 3500L) .addPropertyValue("name", "张三"); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); if (StringUtils.hasText(beanName)) { // 命名注册 registry.registerBeanDefinition(beanName, beanDefinition); } else { // 非命名注册 BeanDefinitionReaderUtils .registerWithGeneratedName(beanDefinition, registry); } } // 非命名方式 注册 public static void registerBeanDefinition(BeanDefinitionRegistry registry) { registerBeanDefinition(registry, null); } // 配置方式 注册 @Configuration public static class Config { @Bean(name = "lisi-user") public User user() { User user = new User(); user.setName("里斯"); user.setId(89L); return user; } } } ``` 输出结果: ![Bean注册](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.78dlbn9nqkw0.png) ## 4. 实例化 Spring Bean Bean实例化(Instantiation): - 常规方式 - 通过构造器(配置元信息:XML 、Java 注解 和 Java API)。 - 通过静态工厂方法(配置元信息:XML 和 Java API)。 - 通过 Bean 工厂方法(配置元信息:XML 和 Java API)。 - 通过 `FactoryBean`(配置元信息:XML、Java 注解 和 Java API)。 - 特殊方式: - 通过 `ServiceLoaderFactoryBean` (配置元信息、XML、Java 注解 和 Java API)。 - 通过 `AutowireCapableBeanFactory#createBean(Class, boolean)` 。 - 通过 `BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)`。 ### 4.1. 常规方式 #### 4.1.1. 静态方法 创建 Person 类,声明一个静态方法,用于创建对象。 ```java public class Person { private String name; private Integer age; public static Person create() { Person person = new Person(); person.setName("Ringo"); person.setAge(18); return person; } // get set... } ``` 在 XML 配置元信息中声明。 ```xml ``` #### 4.1.2. 实例方法 创建 PersonFactory 和默认的实现类。 ```java public interface PersonFactory { default Person create() { return Person.create(); } } // 默认实现类 public class DefaultPersonFactory implements PersonFactory { } ``` 在 XML 配置元信息中声明。 ```xml ``` #### 4.1.3. FactoryBean 创建 PersonFactoryBean 实现 `FactoryBean` 接口。 ```java public class PersonFactoryBean implements FactoryBean { @Override public Person getObject() throws Exception { return Person.create(); } @Override public Class getObjectType() { return Person.class; } } ``` 在 XML 配置元信息中声明。 ```xml ``` #### 4.1.4. 展示以上三种方式结果 ```java // 1: 创建 IoC 容器 String configLocation = "classpath:META-INFO/bean-instance-context.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation); // 2: 依赖查找 // 静态方法 实例化Bean Person personByStaticMethod = applicationContext.getBean("person-by-static-method", Person.class); // 实例方法 实例化Bean Person personByInstanceMethod = applicationContext.getBean("person-by-instance-method", Person.class); // FactoryBean 实例化Bean // 注意:这里类型是写 Person.class Person personByFactoryBean = applicationContext.getBean("person-by-factory-bean", Person.class); System.out.println(personByStaticMethod); System.out.println(personByInstanceMethod); System.out.println(personByFactoryBean); System.out.println(personByStaticMethod == personByInstanceMethod); // false System.out.println(personByStaticMethod == personByFactoryBean); // false ``` ![输出结果](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.3vmzwix3nyi0.png) ### 4.2. 特殊方式 Java 提供了 `ServiceLoader` 来加载 接口 和 实现类。 `ServiceLoader` 源码中设置了路径: ```java private static final String PREFIX = "META-INF/services/"; ``` 所以在 classpath 下创建这个路径: ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.2zjpw10z4fe0.png) 使用 `ServiceLoader` 来加载 实现类: ```java // 加载 接口 和 实现类 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ServiceLoader serviceLoader = ServiceLoader.load(PersonFactory.class, classLoader); // 迭代输出实现类 // 定义多个相同的实现类会自动去重 // 有多个不同的实现类会逐一输出 Iterator iterator = serviceLoader.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } ``` 注意: - 定义多个相同的实现类,最后只会加载一个! - 有多个不同的实现类会逐一输出。 输出结果,如下所示: ```shell com.ymy.thinking.in.spring.bean.factory.DefaultPersonFactory@7ea987ac ``` #### 4.2.1. ServiceLoaderFactoryBean `PersonFactory` 有多个实现类。 ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.5f5tebhujjs0.png) `ServiceLoaderFactoryBean` 在 XML 配置元信息。 ```xml ``` `ServiceLoaderFactoryBean` 既然是 FactoryBean就有 FactoryBean 的特性。 根据源码可知,从 Ioc 容器获取到 `ServiceLoaderFactoryBean` ,拿到的结果是 `ServiceLoader`! ```java public class ServiceLoaderFactoryBean extends AbstractServiceLoaderBasedFactoryBean implements BeanClassLoaderAware { @Override protected Object getObjectToExpose(ServiceLoader serviceLoader) { return serviceLoader; } @Override public Class getObjectType() { return ServiceLoader.class; } } ``` 测试代码示例: ```java public class BeanSpecialInstanceDemo { public static void main(String[] args) { // 创建 IoC 容器 String configLocation = "classpath:META-INF/special-bean-instance-context.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation); // 依赖查找 // 注意:这里 getBean 要写 ServiceLoader.class ServiceLoader serviceLoader = applicationContext.getBean("personFactoryServiceLoader", ServiceLoader.class); System.out.println("== Java ServiceLoader =="); display(serviceLoader); System.out.println("== Spring ServiceLoaderFactoryBean =="); serviceLoaderDemo(); } public static void serviceLoaderDemo() { // 加载 接口 和 实现类 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ServiceLoader serviceLoader = ServiceLoader.load(PersonFactory.class, classLoader); display(serviceLoader); } public static void display(ServiceLoader serviceLoader) { // 迭代输出实现类 // 定义多个相同的实现类会自动去重 Iterator iterator = serviceLoader.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } } ``` 输出结果: ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.1272dd3vimi8.png) ## 5. 初始化 Spring Bean Bean 初始化(Initialization) - `@PostConstruct` 标注方法(Java 提供)。 - 实现 `InitializationBean` 接口。 - 自定义初始化方法: - XML 配置:``。 - Java 注解:`@Bean(initMethod="init")`。 - Java API:`AbstractBeanDefinition#setInitMethodName(String)`。 ### 5.1. 注解和接口初始化Bean `PersonFactory` 的默认实现类 `DefaultPersonFactory`。 以下是三种实现方法: ```java public class DefaultPersonFactory implements PersonFactory, InitializingBean { // 1: 基于 @PostConstructor 注解(Java提供) // 注意:该方法不能有参数 @PostConstruct public void init() { System.out.println("@PostConstruct 初始化Bean: PersonFactory init..."); } // 2: 自定义初始化方法 // 这个方法不常用 public void initPersonFactory() { System.out.println("自定义初始化方法: PersonFactory init..."); } // 3: afterPropertiesSet 初始化 @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet 初始化方法: PersonFactory init..."); } } ``` 编写测试用例: ```java /** * Bean 初始化示例 * * @author Ringo * @date 2021/8/26 22:41 */ // IoC 容器会扫描到这个类 @Configuration public class BeanInitializationDemo { public static void main(String[] args) { // 创建 IoC 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 设置包扫描 applicationContext.scan("com.ymy.thinking.in.spring.bean.initialization.**"); // 启动 Spring 应用上下文 applicationContext.refresh(); // 依赖查找 PersonFactory personFactory = applicationContext.getBean(PersonFactory.class); // 关闭 Spring 应用上下文 applicationContext.close(); } @Bean(initMethod = "initPersonFactory") public PersonFactory personFactory() { return new DefaultPersonFactory(); } } ``` 运行结果: ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.1ium2ozdt7i8.png) ### 5.2. Bean延迟初始化 Bean 延迟初始化(Lazy Initialization) - XML 配置:``。 - Java 注解:`@Lazy`。 思考:当某个 Bean 定义为延迟初始化,那么,Spring 容器返回的对象与非延迟的对象存在怎样的差异? 非延迟加载是在 Spring Application Context 初始化完成之前,就将 Bean 加载到 IoC 容器。 延迟加载是 Spring Application Context 初始化完成之后,由于依赖查找或者依赖注入,触发 Bean 的加载。 `@Lazy` 注解要配合 `@Componet、@Bean` 等注解使用,详情看 @Lazy 注解的说明即可。 ## 6. Bean 销毁(Destroy) Bean 销毁(Destroy) - `@PreDestroy` 标注方法(Java 提供)。 - 实现 `DisposableBean` 接口的 destroy() 方法。 - 自定义销毁方法: - XML 配置:``。 - Java 注解:`@Bean(destroy="destroyMethod")`。 - Java API:`AbstractBeanDefinition#setDestroyMethodName(String)`。 写法参考 Bean 的初始化。 三种方法的顺序: ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.4ejdt4rl0m80.png) ```java // 关闭 Spring 应用上下文才会触发 Bean 销毁的操作! applicationContext.close(); ``` ## 7. GC Spring Bean Bean 垃圾回收(GC) 1. 关闭 Spring 容器(Application Context)。 2. 执行 GC。 3. Spring Bean 覆盖的 `finalize()` 方法被回调。 创建 `PersonFactory` 实现类。 ```java public class RewriteFinalizePersonFactory implements PersonFactory, DisposableBean { @Override protected void finalize() throws Throwable { System.out.println("当前对象 RewriteFinalizePersonFactory 正在被回收..."); } @Override public void destroy() throws Exception { System.out.println("当前对象 RewriteFinalizePersonFactory 正在被销毁..."); } } ``` 测试程序: ```java /** * Bean 垃圾回收(GC)示例 * * @author Ringo * @date 2021/8/27 22:52 */ public class BeanGarbageCollectionDemo { public static void main(String[] args) throws IOException { // 创建 IoC 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 注册 Bean. BeanGarbageCollectionDemo 相当于加了 @Configuration 注解 applicationContext.register(BeanGarbageCollectionDemo.class); // 开启 Spring 应用上下文 applicationContext.refresh(); // 关闭 Spring 应用上下文 applicationContext.close(); // 强制触发 GC System.gc(); // 阻塞程序, 防止 gc 还没完成程序就结束了! System.in.read(); @Bean public PersonFactory personFactory() { return new RewriteFinalizePersonFactory(); } } ``` 输出结果: ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.ko0d1n874mo.png) ## 8. 面试题 ### 8.1. 如何注册 Spring Bean? 通过 BeanDefinition 和 外部单体对象 来注册。 注册 外部单体对象 ? ```java // 创建 IoC 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 创建一个外部 PersonFactory 对象 PersonFactory personFactory = new DefaultPersonFactory(); SingletonBeanRegistry beanFactory = applicationContext.getBeanFactory(); // 注册外部单例对象 beanFactory.registerSingleton("personFactory", personFactory); // 开启 Spring Application Context applicationContext.refresh(); // 依赖查找 PersonFactory externalRegistryBean = applicationContext.getBean(PersonFactory.class); PersonFactory lookUpBean = applicationContext.getBean(PersonFactory.class); System.out.println(externalRegistryBean); System.out.println(lookUpBean); // 结果是 true System.out.println("externalRegistryBean == lookUpBean? " + (externalRegistryBean == lookUpBean)); // 关闭 Spring Application Context applicationContext.close(); ``` 输出结果: ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.5xn8iazdh740.png) ### 8.2. 什么是 BeanDefinition? 回顾 "定义 Spring Bean" 和 "BeanDefinition 元信息" 。 `BeanbDefition` 接口表示的是关于 Bean 的定义的元信息,允许我们以 Get/Set 的方式来修改 Bean 定义元信息。 ```java public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { // 作用域:单例还是原型 String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; void setScope(@Nullable String scope); String getScope(); // 设置当前 bean 定义的父级 bean 的名称(如果有)。 void setParentName(@Nullable String parentName); String getParentName(); // 是否是懒加载 void setLazyInit(boolean lazyInit); boolean isLazyInit(); // @Primary // 多个同类型Bean的时候, 注入优先级 void setPrimary(boolean primary); boolean isPrimary(); // ... } ``` ### 8.3. Spring 容器怎样管理注册 Bean? # 三、Spring IoC 依赖查找 ## 1. 单一类型依赖查找 单一类型依赖查找接口 — BeanFactory - 根据 Bean 名称查找 - `getBean(String)`。 - Spring 2.5 覆盖默认参数:`getBean(String, Object...)`。(尽量不用!) - 根据 Bean 类型查找 - Bean 实时查找 - Spring 3.0 `getBean(Class)`。 - Spring 4.1 覆盖默认参数: `getBean(Class, Object...)`。(尽量不用!) - Spring 5.1 Bean 延迟查找 - `getBeanProvider(Class)`。 - `getBeanProvider(ResolvableType)`。 - 根据 Bean 名称 + 类型查找:`getBean(String, Class)`。 ```java // BeanFactory 延迟查找 ObjectProvider getBeanProvider(Class requiredType); ObjectProvider getBeanProvider(ResolvableType requiredType); // ObjectProvider 继承自 ObjectFactory public interface ObjectProvider extends ObjectFactory, Iterable { // ... 省略方法 } ``` `getBeanProvider(Class)` 的基本使用: ```java /** * {@link ObjectProvider} 延迟依赖查找示例 * * @author Ringo * @date 2021/8/31 11:31 */ public class ObjectProviderDemo { public static void main(String[] args) { // 创建 IoC 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 包扫描 applicationContext.scan("com.ymy.thinking.in.spring.dependency.lookup.**"); // 开启 Spring 应用上下文 applicationContext.refresh(); // 通过 ObjectProvider 实现延迟依赖查找 lookupByObjectProvider(applicationContext, String.class); // 关闭 Spring 应用上下文 applicationContext.close(); } private static void lookupByObjectProvider(BeanFactory beanFactory, Class requiredType) { ObjectProvider objectProvider = beanFactory.getBeanProvider(requiredType); // 该方法来自于 ObjectFactory#getObject() 方法 // ObjectFactory 接口可以实现 延迟依赖查找 T object = objectProvider.getObject(); System.out.println(object); } /** * 配置类, 配合包扫描就能解析这个注解 */ @Configuration public static class Config { @Bean public String hello() { return "Hello World"; } } } ``` ## 2. 集合类型依赖查找 集合类型依赖查找接口 — ListableBeanFactory - 根据 Bean 类型查找 - 获取同类型 Bean 名称列表。 - `getBeanNamesForType(Class)`。 - Spring 4.2 `getBeanNamesForType(ResolvableType)`。 - 获取同类型 Bean 实例列表。 - `getBeansOfType(Class)` 以及重载方法。 - 通过注解类型查找 - Spring 3.0 获取标注注解类型 Bean 名称列表。 - `getBeanNamesForAnnotation(Class)`。 - Spring 3.0 获取标注标注类型 Bean 实例列表。 - `getBeansWithAnnotation(Class)`。 - Spring 3.0 获取指定名称 + 标注类型 Bean 实例。 - `findAnnotationOnBean(String, Class)`。该方法会返回 Bean 上标注的注解。 创建自定义注解: ```java // RUNTIME 保证在运行时可以被反射到 @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE, ElementType.METHOD}) public @interface Fine { } ``` 集合查找 Bean 示例: ```java public class DependencyLookupByCollectionTypeDemo { public static void main(String[] args) { // 创建 IoC 容器 // ApplicationContext 已经实现了 ListableBeanFactory AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.scan("com.ymy.thinking.in.spring.dependency.lookup.**"); // 开启 Spring 应用上下文 applicationContext.refresh(); // 1: 获取同类型 Bean 名称列表 String[] beanNamesForType = applicationContext.getBeanNamesForType(String.class); System.out.println("获取同类型Bean名称列表: " + Arrays.toString(beanNamesForType)); // 2: 获取同类型 Bean 实例列表 Map beansOfTypeMap = applicationContext.getBeansOfType(String.class); System.out.println("获取同类型 Bean 实例列表: " + beansOfTypeMap); // 3: 获得标注注解类型 Bean 名称列表 String[] beanNamesForAnnotation = applicationContext.getBeanNamesForAnnotation(Fine.class); System.out.println("获得标注注解类型 Bean 名称列表: " + Arrays.toString(beanNamesForAnnotation)); // 4: 获得标注注解类型 Bean 实例列表 Map beansWithAnnotation = applicationContext.getBeansWithAnnotation(Fine.class); System.out.println("获得标注注解类型 Bean 实例列表: "); beansWithAnnotation.forEach((k, v) -> System.out.println(k + "=" + v)); // 关闭 Spring 应用上下文 applicationContext.close(); } @Fine @Configuration public static class Config { @Fine @Bean public String hello() { return "hello"; } @Fine @Bean public String world() { return "world"; } } } ``` 集合类型的 Bean 依赖查找结果: ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.1fljf6km7t7k.png) ## 3. 层次性依赖查找(IoC也能继承?) 层次性依赖查找接口 — HierarchicalBeanFactory - 获得父级 BeanFactory`BeanFactory getParentBeanFactory()`。 - 层次性查找 - 根据 Bean 名称查找: - 基于 `containsLocalBean` 方法实现。(该方法只会查找当前 IoC 容器中的 Bean,不会查询父级 BeanFactory 中的 Bean)。 - 根据 Bean 类型查找实例列表: - 单一类型:`BeanFactoryUtils#beanOfTypeIncludingAncestors`。 - 集合类型:`BeanFactoryUtils#beansOfTypeIncludingAncestors`。 - 根据 Java 注解查找名称列表: - `BeanFactoryUtils#beanNamesForTypeIncludingAncestors`。 创建 parent BeanFactory: ```xml ``` 测试案例: ```java /** * {@link HierarchicalBeanFactory} 层次性依赖查找 Bean 示例 * * @author Ringo * @date 2021/8/31 16:50 */ public class HierarchicalDependencyLookupDemo { public static void main(String[] args) { // 创建 IoC 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 注册为 Bean applicationContext.register(HierarchicalDependencyLookupDemo.class); // 1: 获取 HierarchicalBeanFactory // ConfigurableListableBeanFactory 继承了 HierarchicalBeanFactory ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); // 这里返回 null System.out.println("当前 BeanFactory 的 Parent BeanFactory:" + beanFactory.getParentBeanFactory()); // 2. 设置 Parent BeanFactory HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory(); beanFactory.setParentBeanFactory(parentBeanFactory); System.out.println("当前 BeanFactory 的 Parent BeanFactory:" + beanFactory.getParentBeanFactory()); // 开启 Spring 应用上下文 applicationContext.refresh(); // 查看当前 BeanFactory 是否包含 hello 这个 Bean displayContainsLocalBean(beanFactory, "hello"); // false displayContainsLocalBean(parentBeanFactory, "hello"); // true // 先查子容器 再查父容器 displayContainsBean(beanFactory, "hello"); // true displayContainsBean(parentBeanFactory, "hello"); // true // 关闭 Spring 应用上下文 applicationContext.close(); } // 既查当前 BeanFactory 又查父级 BeanFactory private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) { System.out.println(containsBean(beanFactory, beanName)); } // 递归查询(我们自己写的 containsBean的实现) public static boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) { // 拿到父级的 IoC 容器 BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory(); // 父级 IoC 容器是 HierarchicalBeanFactory if (parentBeanFactory instanceof HierarchicalBeanFactory) { HierarchicalBeanFactory parentHierarchicalBeanFactory = (HierarchicalBeanFactory) parentBeanFactory; if (containsBean(parentHierarchicalBeanFactory, beanName)) { return true; } } // 父级 IoC 容器不是 HierarchicalBeanFactory, 就只查询当前容器 // containsLocalBean 只返回当前 IoC 容器中的 Bean // 不会去查询父级的 IoC 容器 return beanFactory.containsLocalBean(beanName); } // 只查本地 BeanFactory private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) { System.out.println(beanFactory.containsLocalBean(beanName)); } /** * 创建父级 {@link BeanFactory} */ private static HierarchicalBeanFactory createParentBeanFactory() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); String location = "classpath:META-INF/hierarchical-dependency-lookup-context.xml"; reader.loadBeanDefinitions(location); return beanFactory; } } ``` ## 4. 延迟依赖查找(有用?) Bean 延迟依赖查找接口: - `org.springframework.beans.factory.ObjectFactory`。 - `org.springframework.beans.factory.ObjectProvider`。 - Spring 5 对 Java 8 特性的扩展: - 函数式接口: - `getIfAvailable(Supplier)`。 - `ifAvailable(Consumer)`。 - Stream 扩展 - `stream()`。 ObjectProvider接口API: ```java // ObjectProvider 接口 // 1: 容器中有 Bean 就获取, 没有就返回 null。 @Nullable T getIfAvailable() throws BeansException; // 2: 容器中有 Bean 就获取, 没有就返回指定对象。 default T getIfAvailable(Supplier defaultSupplier) throws BeansException { T dependency = getIfAvailable(); return (dependency != null ? dependency : defaultSupplier.get()); } ``` ObjectProvider接口的使用: ```java public class ObjectProviderIfAvailableDemo { public static void main(String[] args) { // 创建 IoC 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 注册当前类到 IoC 容器 applicationContext.register(ObjectProviderIfAvailableDemo.class); // 开启 Spring 应用上下文 applicationContext.refresh(); // 依赖查找 lookupIfAvailable(applicationContext); System.out.println("======================="); lookupByStream(applicationContext); // 关闭 Spring 应用上lose(); } // Stream 流式查找 private static void lookupByStream(ApplicationContext applicationContext) { ObjectProvider objectProvider = applicationContext.getBeanProvider(String.class); // Stream 方式 objectProvider.stream().forEach(System.out::println); // 迭代器方式 objectProvider.iterator().forEachRemaining(System.out::println); } // IfAvailable 的方式查找 private static void lookupIfAvailable(ApplicationContext applicationContext) { ObjectProvider objectProvider = applicationContext.getBeanProvider(String.class); // 如果容器中有 Bean 就获取, 没有就返回 null String beanIfAvailable = objectProvider.getIfAvailable(); // 如果容器中有 Bean 就获取, 没有就返回指定的值 String ifAvailable = objectProvider.getIfAvailable(() -> "aabb"); System.out.println(beanIfAvailable); System.out.println(ifAvailable); } @Bean @Primary public String fine() { return "fine"; } @Bean public String message() { return "message"; } } ``` 输出结果: ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.x896sjx6o74.png) ## 5. 安全依赖查找 依赖查找安全性对比: | 依赖查找类型 | 代表实现 | 是否安全 | | ------------ | ---------------------------------- | -------- | | 单一类型查找 | BeanFactory#getBean | 否 | | | ObjectFactory#getObject | 否 | | | ObjectProvider#getIfAvailable | 是 | | 集合类型查找 | ListableBeanFactory#getBeansOfType | 是 | | | ObjectProvider#stream | 是 | 注意:层次性依赖查找的安全性取决于其扩展的单一或集合类型的 BeanFactory 接口。 ## 6. IoC内建可查找依赖 `AbstractApplicationContext` 内建可查找的依赖。 | Bean名称 | Bean实例 | 使用场景 | | --------------------------- | -------------------------------- | ----------------------- | | environment | Environment 对象 | 外部化配置以及 Profiles | | systemProperties | java.util.Properties 对象 | Java 系统属性 | | systemEnvironment | java.util.map 对象 | 操作系统环境变量 | | messageSource | MessageSource 对象 | 国际化文案 | | lifecycleProcessor | LifecycleProcessor 对象 | Lifecycle Bean 处理器 | | applicationEventMulticaster | ApplicationEventMulticaster 对象 | Spring 事件广播器 | ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.48w5jg6axxu0.png) ## 7. 依赖查找中的经典异常 | 异常类型 | 触发条件(举例) | 场景举例 | | ------------------------------- | ------------------------------------------------ | ----------------------------------------------- | | NoSuchBeanDefinitionException | 当查找的 Bean 不存在于 IoC 容器时 | BeanFactory#getBean() ObjectFactory#getObject() | | NoUniqueBeanDefinitionException | 按类型依赖查找时,IoC 容器中存在多个 Bean 的实例 | BeanFactory#getBean(Class) | | BeanInstantiationException | 当 Bean 所对应的类型非具体类时 | BeanFactory#getBean() | | BeanCreationException | 当 Bean 初始化过程中 | Bean 初始化方法执行异常 | | BeanDefinitionStoreException | 当 BeanDefition 配置元信息非法时 | XML 配置资源无法打开时 | ## 8. 面试题 ### 8.1. ObjectFactory和BeanFactory区别? `ObjectFactory、BeanFactory` 都提供依赖查找的能力。 不过 `ObjectFactory` 仅仅关注一个或者一种类型的 Bean 依赖查找,并且自身不具备依赖查找的能力,能力则由 BeanFactory 输出。 `BeanFactory` 和其字类则提供了单一类型、集合类型以及层次性等多种依赖查找方式。 ## 8.2. BeanFactory.getBean 是否线程安全? 线程安全(原因不清楚...) # 四、Spring IoC依赖注入 ## 1. 依赖注入的模式和类型 手动模式 — 配置或者编程的方式,提前安排注入规则 - XML 资源配置元信息。 - Java 注解配置元信息。 - API 配置元信息。 自动模式 — 实现方提供依赖自动关联的方式,按照内建的注入规则 - Autowiring(自动绑定)。 --- ![依赖注入类型](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/依赖注入类型.2r1oy7hm27k0.png) ### 1.1. Setter 方法注入 手动模式: - XML 资源配置元信息。 - Java 注解配置元信息。 - API 配置元信息。 自动模式: - byName。 - byType。 下面举一个 Java 注解注入的例子: ```java /** * 基于 Java 注解的依赖注入 * * @author Ringo * @date 2021/9/29 0:29 */ public class AnnotationDependencySetterInjectionDemo { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationDependencySetterInjectionDemo.class); System.out.println(applicationContext.getBean(UserHolder.class)); } @Bean public User user() { User user = new User(); user.setName("李白"); user.setAge(19); return user; } @Bean public UserHolder userHolder(User user) { return new UserHolder(user); } } ``` ### 1.2. 构造器注入:官方推荐? 手动模式: - XML 资源配置元信息。 - Java 注解配置元信息。 - API 配置元信息。 自动模式: - constructor 构造器注入: ```java @Controller public class FooController { private final FooService fooService; @Autowired public FooController(FooService fooService) { this.fooService = fooService; } //使用方式上同,略 } ``` **如果使用构造器注入,在spring项目启动的时候,就会抛出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?从而提醒你避免循环依赖,如果是field注入的话,启动的时候不会报错,在使用那个bean的时候才会报错**。 使用构造器注入的好处: 1. 保证依赖不可变(final关键字) 2. 保证依赖不为空(省去了我们对其检查) 3. 保证返回客户端(调用)的代码的时候是完全初始化的状态 4. 避免了循环依赖 5. 提升了代码的可复用性 ### 1.3. 字段注入 手动模式:Java注解配置元信息 - @Autowired。 - @Resource。 - @Inject(可选)。 注意:`@Autowired` 会忽略静态字段。 ```java @Component public class Test { // @Autowired 主要处理实例字段 // @Autowired 会忽略静态字段,无法自动注入。 @Autowired private static User user; } ``` ### 1.4. 方法注入:@Autowired用在方法上? ```java /** * {@link Autowired} {@link Resource} 基于方法的依赖注入 * * @author Ringo * @date 2021/9/30 12:03 */ public class AnnotationDependencyMethodInjectionDemo { private UserHolder userHolder; private UserHolder userHolder2; // ============================================================= // @Autowired @Resource @Bean 都可以实现方法注入 @Autowired public void init1(UserHolder userHolder) { this.userHolder = userHolder; } @Resource public void init2(UserHolder userHolder) { this.userHolder2 = userHolder; } @Bean public UserHolder userHolder(User user) { return new UserHolder(user); } // ============================================================= @Bean public User user() { return new User("zsf", 18); } public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationDependencyMethodInjectionDemo.class); AnnotationDependencyMethodInjectionDemo demo = applicationContext.getBean(AnnotationDependencyMethodInjectionDemo.class); System.out.println(demo.userHolder); System.out.println(demo.userHolder2); System.out.println(demo.userHolder == demo.userHolder2); // true } } ``` ### 1.5. 接口回调注入 ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/Aware.7003fuwuvcg0.png) ```java /** * 基于 {@link Aware} 接口回调的依赖注入示例 * * @author Ringo * @date 2021/9/30 13:06 */ public class AwareInterfaceDependencyInjectionDemo implements BeanFactoryAware, ApplicationContextAware { private static BeanFactory beanFactory; private static ApplicationContext applicationContext; public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareInterfaceDependencyInjectionDemo.class); System.out.println(beanFactory == context.getBeanFactory()); // true System.out.println(applicationContext == context); // true } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { AwareInterfaceDependencyInjectionDemo.beanFactory = beanFactory; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { AwareInterfaceDependencyInjectionDemo.applicationContext = applicationContext; } } ``` ### 1.6. 集合类型注入 ![集合类型注入](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.3n9zqs7kd6y0.png) ### 1.7. 限定注入@Qualifier:支持分组? ![限定注入](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/限定注入.6oszf6k0hlg0.png) `@Qualifier` 不仅可以用在 Bean 的依赖注入上,还可以用在 Bean 的声明上。 `@Qualifier` 也可以用在注解上,派生新的注解,是对 `@Qualifier` 注解的扩展。 有没有 `@Qualifier` 可以对 Bean 进行分组。 ```java /** * 用户组注解, 扩展 {@link Qualifier} * * @author Ringo * @date 2021/9/30 14:37 */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Qualifier // 可以标注在 Annotation 上 public @interface UserGroup { } ``` ```java /** * {@link Qualifier} 限定注入案例 * * @author Ringo * @date 2021/9/30 14:13 * @see Qualifier * @see UserGroup */ public class QualifierAnnotationDependencyInjectionDemo { @Autowired private User user; // user2 -> @Primary @Autowired @Qualifier("user1") // 指定 Bean name private User namedUser; // 整体应用上下文存在 4 个 User 类型的 Bean // user1 // user2 // user3 -> @Qualifier // user4 -> @Qualifier @Autowired private List allUsers; // 注入所有的 User 类型的 Bean @Autowired @Qualifier private List qualifiedUsers; // 4 Beans = user1 + user2 + user3 + user4 @Autowired @UserGroup private List groupedUsers; // 2 Bean = user3 + user4 public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(QualifierAnnotationDependencyInjectionDemo.class); applicationContext.refresh(); QualifierAnnotationDependencyInjectionDemo demo = applicationContext.getBean(QualifierAnnotationDependencyInjectionDemo.class); System.out.println("demo.user = " + demo.user); System.out.println("demo.namedUser = " + demo.namedUser); System.out.println("demo.allUsers = " + demo.allUsers); System.out.println("demo.qualifiedUsers = " + demo.qualifiedUsers); System.out.println("demo.groupedUsers = " + demo.groupedUsers); } @Bean @Qualifier public User user1() { return new User("zs", 19); } @Bean @Primary @Qualifier public User user2() { return new User("ls", 20); } @Bean @UserGroup // 进行逻辑分组 public User user3() { return createUser("007"); } @Bean @UserGroup // 进行逻辑分组 public User user4() { return createUser("008"); } // 工厂方法 public static User createUser(String name) { User user = new User(); user.setName(name); return user; } } ``` ### 1.8. 延迟注入 ![延迟依赖注入](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/延迟依赖注入.46tpqsy69bk0.png) ```java @Autowired private User user; // 实时注入 @Autowired private ObjectProvider userObjectProvider; // 延迟注入 @Autowired private ObjectFactory userObjectFactory; // ObjectFactory 延迟注入 ``` ### 1.9. 依赖处理过程 ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.406n7axma8q0.png) ### 1.10. @Autowired注入规则和原理 `AutowiredAnnotationBeanPostProcessor` `@Autowired`注入过程: - 元信息解析。 - 依赖查找。 - 依赖注入(字段、方法)。 ### 1.11. Java 通用注解 `CommonAnnotationBeanPostProcessor`。 注入注解: - javax.xml.ws.WebServiceRef。 - javax.ejb.EJB。 - javax.annotation.Resource。 生命周期注解: - javax.annotation.PostConstruct。 - javax.annotation.PreDestroy。 ### 1.12. 自定义DI注解 ```java /** * 扩展DI的最优方式: 这样实现新旧注解都可以使用 *

* static 静态注入会对 Bean 注入优先级做提升 */ @Bean @Order(Ordered.LOWEST_PRECEDENCE - 3) public static AutowiredAnnotationBeanPostProcessor beanPostProcessor() { AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor(); // 替换原有的注解处理, 使用新注解 @Injected beanPostProcessor.setAutowiredAnnotationType(Injected.class); return beanPostProcessor; } ``` ### 1.13. 面试题 #### 13.1. 多少种依赖注入方式 构造器注入、Setter注入、字段注入、方法注入、接口回调注入。 #### 13.2. 你偏好构造器注入还是 Setter 注入 两种依赖注入的方式都可以用,如果必须依赖的话,那么推荐构造器注入,Setter 注入用于可选依赖。 ### 13.3. Spring依赖注入来源 # 五、Spring IoC 依赖来源 ## 1. 依赖查找的来源 Spring BeanDefinition:配置元数据 - ``。 - `@Bean public class User user() {}`。 - `BeanDefinitionBuilder`。 单例对象:`API` 实现。 ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.54rjz8p5gl00.png) ## 2. 依赖注入来源 ![](https://cdn.jsdelivr.net/gh/RingoTangs/image-hosting@master/spring5/image.19gs743p81pc.png) # 九、Spring Bean生命周期