1. 依赖查找的来源:除容器内建和自定义Spring Bean之外,还有其他来源提供依赖查找吗?
-
查找来源
-
Spring 內建 BeanDefintion
-
Spring 內建单例对象
-
当spring在注解环境下面, 这个 registerAnnotationConfigProcessors API会被调用, 它会被显示的去调用, 这个显示调用会有几个场景
第一个就是xml配置Annotation驱动
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/beans/spring-context.xsd "> <!-- 加载注解驱动所需要的 Spring 基础组件 --> <context:annotation-config /> <!-- 扫描 @Component Class --> <context:component-scan base-package="org.xiaoge" /> <!-- 实施加载bean--> <bean id="user" class="org.xiaoge.thinking.in.spring.ioc.overview.domain.User"> <property name="id" value="1" /> <property name="name" value="xiaoge"/> <property name="city" value="WUHAN"/> <property name="configFileLocation" value="classpath:/META-INF/dependency-lookup-context.xml"/> <property name="workCities" value="WUHAN, HANGZHOU"/> <!-- <property name="lifeCities" value="WUHAN, BEIJING"/>--> <property name="lifeCities"> <list> <value>WUHAN</value> <value>BEIJING</value> </list> </property> <property name="map"> <map> <entry key="one" value="1" /> <entry key="two" value="2" /> </map> </property> </bean> <!-- 延迟bean 是沟通objectFactory简介创建的 --> <bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName" value="user" /> </bean> <!-- parent继承 --> <bean id="superUser" class="org.xiaoge.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user" primary="true"> <property name="address" value="武汉" /> </bean> </beans>
annotation-config/component-scan都是注解驱动着一个XML元素的配置, 这些元素触发如下API调用:
看一下它到底被谁给调用了
它在xml处理的时候会被调用, 同时也会被, 同时ComponentScan里面进行调用了
包括AnnotatedBeanDefinitionReader它实际上是在AnnotationConfigApplicationContext关联的当它load的时候吧对应的信息读取进来
最终调用 registerAnnotationConfigProcessors API把这些信息注入到应用上下文中, @Configuration @Autowired @Resource等
这些Bean都会放到应用上下文中来进行注册, 这些东西都是称之为一个常规的Bean, 这些常规的Bean是在 AbstractApplicationContext#prepareBeanFactory 地方调用的, 这里头会注册非常多的一些信息, 包括上面的内建单例对象等
那么当有BeanDefinition初始化完成之后, 它会调用 invokeBeanFactoryPostProcessors API, 通常我们AnnotationConfigUtils注册的这个类基本上BeanFactoryPostProcessor实现或者FactoryBean的实现, 因此在实现来说注册和调用它会激活它里面的生命周期。
2. 依赖注入的来源:难道依赖注入的来源与依赖查找的不同吗?
-
注入来源
从这里可以看出它用了类型和对应做了个映射关系, 这种关系能够帮助我们进行依赖注入, 它这里还写了个相关的 API 它说这个地方是用于 autowiring 自动绑定时候所运用来找到, 那么这种方式也始终依赖注入的方式。那么相较于我们的依赖查找, 这里增加了种新的来源, 这几个对象在DefaultListableBeanFactory#findAutowireCandidates依赖查找中运用到
从this.resolvableDependencies值中可以看出我们锁保存的resolvableDependencies实际上就是我们刚刚注入或注册的由框架内部来进行注册的这些信息, 为了证明这个猜想回过头来看它里面的一个实现, 可以看到实际上就是这样做的, 它就是把东西方法ConcurrentHashMap里面去, 前面有四个注入的对象, 因此, 这个时候通过不同四个类型注入两个不同对象, 一个是BeanFactory, 一个是三个相同的List就是当前的应用上下文
package org.xiaoge.thinking.in.spring.ioc.dependency.source; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.io.ResourceLoader; import javax.annotation.PostConstruct; /** * 依赖来源 示例 * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ public class DependencySourceDemo { // 注入在 postProcessProperties 方法执行。早于 setter注入,也早于@PostConstruct @Autowired private BeanFactory beanFactory; @Autowired private ResourceLoader resourceLoader; @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ApplicationContext applicationContext; @PostConstruct public void initByInjection() { System.out.println("beanFactory == applicationContext " + (beanFactory == applicationContext)); System.out.println("beanFactory == applicationContext.getBeanFactory() " + (beanFactory == applicationContext.getAutowireCapableBeanFactory())); System.out.println("resourceLoader == applicationContext " + (resourceLoader == applicationContext)); System.out.println("applicationEventPublisher == applicationContext " + (applicationEventPublisher == applicationContext)); } @PostConstruct public void initByLookup() { getBean(BeanFactory.class); getBean(ResourceLoader.class); getBean(ApplicationEventPublisher.class); getBean(ApplicationContext.class); } private <T> T getBean(Class<T> beanType) { try { return beanFactory.getBean(beanType); } catch (NoSuchBeanDefinitionException e) { System.err.println("当前类型" + beanType.getName() + "无法在 BeanFactory 中查找!"); } return null; } public static void main(String[] args) { // 创建 ApplicationContext 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 注册 Configuration Class 配置类 applicationContext.register(DependencySourceDemo.class); // 启动应用上下文 applicationContext.refresh(); // 关闭应用上下文 applicationContext.close(); } } // 运行结果 beanFactory == applicationContext false beanFactory == applicationContext.getBeanFactory() true resourceLoader == applicationContext true applicationEventPublisher == applicationContext true 当前类型org.springframework.beans.factory.BeanFactory无法在 BeanFactory 中查找! 当前类型org.springframework.core.io.ResourceLoader无法在 BeanFactory 中查找! 当前类型org.springframework.context.ApplicationEventPublisher无法在 BeanFactory 中查找! 当前类型org.springframework.context.ApplicationContext无法在 BeanFactory 中查找!
总结:
Spring IOC的三种依赖来源,自定义注册的Spring bean、内建的Spring bean以及内建的可注入的依赖,其中自定义注册的Spring bean基本上是通过xml、注解或者api注册BeanDefination创建的,内建的Spring bean是通过registerSingleton()创建的,内建的可注入的依赖是通过registerResolveDependency()创建的,后续如果我们需要往Spring容器里放入一些非Spring托管的bean但又可以被依赖注入的, 可以通过registerResolveDependency() API实现
3. Spring容器管理和游离对象:为什么会有管理对象和游离对象?
-
依赖对象
4. Spring Bean Definition作为依赖来源:Spring Bean的来源
-
要素
-
元数据:BeanDefinition
-
注册:BeanDefinitionRegistry#registerBeanDefinition
-
类型:延迟和非延迟
-
顺序:Bean 生命周期顺序按照注册顺序
BeanDefinitionRegistry它是个注册中心,它里面包含 增/删/查 没有修改, 修改我们可以通过生命周期postProcessMergedBeanDefinition这么一个回调方法来进行操作
BeanDefinitionRegistry它的一个标准的实现我们只需要看DefaultListableBeanFactory这个类
-
5. 单例对象作为依赖来源:单体对象与普通Spring Bean存在哪些差异?
-
要素
- 来源:外部普通 Java 对象(不一定是 POJO )
- 注册:SingletonBeanRegistry#registerSingleton
-
限制
- 无生命周期管理
- 无法实现延迟初始化 Bean
因为它是一个单例对象, 也是来源于外部, 因此它的生命周期不由spring上下文来进行托管, 第二方面它没有办法实现所有对的延迟初始化, 道理非常简单, 由于单一对象没有在 Spring Ioc 容器里面进行托管, 所以它没有办法执行我们的生命周期管理, 同理可得, 它的延迟加载也是没有办法进行处理的, 所以这里是它的一个限制, 但是它可以用于依赖查找, 也可以用于依赖注入
注册
依赖查找
而不是走的 else BeanDefinition 那条路, BeanDefinition 相对来说是比较复杂的, 因为它查找 BeanDefinition 之后, 它需要把BeanDefinition 变成 Bean 那么就会激活所有 Bean 的生命周期, 那之后就会有个生命周期管理, 而 SingletonBean 它其实比较简单只把对象直接返回就行, 所以相对来说它的方式比较单一一点
总结:
有人就会问了为什么要采用BeanDefinition的方式在存储元数据,每次在getBean的时候都需要初始化bean,直接采用singletonObjects方式存储不是更简单吗? 因为BeanDefinition对象有完整的生命周期控制,比如初始化等,更便于你业务bean的扩展(就是为了提高框架的扩展点,便于自己的定制化操作)
6. 非Spring容器管理对象作为依赖来源:如何理解ResolvableDependency?
-
要素
- 注册:ConfigurableListableBeanFactory#registerResolvableDependency
-
限制
- 无生命周期管理
- 无法实现延迟初始化 Bean
- 无法通过依赖查找
package org.xiaoge.thinking.in.spring.ioc.dependency.source; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import javax.annotation.PostConstruct; /** * ResolvableDependency 作为依赖来源 * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ public class ResolvableDependencysourceDemo { @Autowired private String value; @PostConstruct public void init() { System.out.println(value); } public static void main(String[] args) { // 创建 ApplicationContext 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 注册 Configuration Class 配置类 applicationContext.register(ResolvableDependencysourceDemo.class); // 回调, 它是在初始化之前回调 applicationContext.addBeanFactoryPostProcessor(beanFactory -> { // 注册 Resolvable Dependency beanFactory.registerResolvableDependency(String.class, "Hello world"); }); // 启动应用上下文 applicationContext.refresh(); // 关闭应用上下文 applicationContext.close(); } } // 运行结果 Hello world
7. 外部化配置作为依赖来源:@Value是如何将外部化配置注入Spring Bean的?
-
要素
- 类型:非常规 Spring 对象依赖来源
-
限制
- 无生命周期管理
- 无法实现延迟初始化 Bean
- 无法通过依赖查找
package org.xiaoge.thinking.in.spring.ioc.dependency.source; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.core.io.Resource; /** * 外部配置作为依赖来源 * * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a> * @since */ @Configuration @PropertySource(value = "META-INF/default.properties", encoding = "UTF-8") // 加载配置文件注解 public class ExternalConfiqurationDependencySourceDemo { @Value("${user.id:-1}") private Long id; @Value("${usr.name:xiaoge}") private String name; @Value("${user.resource:META-INF/default.properties}") private Resource resource; public static void main(String[] args) { // 创建 ApplicationContext 容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 注册 Configuration Class 配置类 applicationContext.register(ExternalConfiqurationDependencySourceDemo.class); // 启动应用上下文 applicationContext.refresh(); ExternalConfiqurationDependencySourceDemo demo = applicationContext.getBean(ExternalConfiqurationDependencySourceDemo.class); System.out.println("demo.id = " + demo.id); System.out.println("demo.name = " + demo.name); System.out.println("demo.resource = " + demo.resource); // 关闭应用上下文 applicationContext.close(); } } // 运行结果 demo.id = 1 demo.name = 啸哥 demo.resource = class path resource [META-INF/default.properties]
@Value的实现
从图中可以看出他可以操作@Value和@Qualifier直接
strVal是从resolveEmbeddedValue去出来的
8. 面试题精选
- 注入和查找的依赖来源是否相同?
- 答:否,依赖查找的来源仅限于 Spring BeanDefinition 以及单例 对象,而依赖注入的来源还包括 Resolvable Dependency 以及 @Value 所标注的外部化配置
- 单例对象能在 IoC 容器启动后注册吗?
- 答:可以的,单例对象的注册与 BeanDefinition 不同,BeanDefinition 会被 ConfigurableListableBeanFactory#freezeConfiguration() 方法影响,从而冻结注册,单例对象则没有这个限制。
- Spring 依赖注入的来源有哪些?
- 答:
- Spring BeanDefinition
- 单例对象
- Resolvable Dependency
- @Value 外部化配置
- 答: