夯实Spring系列|第七章:IoC 依赖查找(专题)
文章目录
本章说明
通过 第二章:IoC 依赖查找 ,我们已经简单了解到 Spring 中依赖查找的基本使用;本章会更加全面和具体的讨论 Spring IoC 依赖查找,并结合少量的源码分析。
1.项目环境
- jdk 1.8
- spring 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
- 本章模块:dependency-lookup
2.依赖查找的前世今生
在 Spring 之前,其实 JavaBeans 和 JNDI 也有相关的实现
单一类型依赖查找
- JNDI - javax.naming.Context#lookup(javax.naming.Name)
- JavaBeans - java.beans.beancontext.BeanContext
集合类型依赖查找
- java.beans.beancontext.BeanContext
层次性依赖查找
- java.beans.beancontext.BeanContext
具体的源码我们就不在此进行展开,Spring 中实现也是借鉴的他们的设计,而且比他们做的更好,我们将学习重点放在下面 Spring 中即可。
3.单一类型依赖查找
单一类型依赖查找接口 - BeanFactory
- 根据 Bean 名称查找
- getBean(String)
- Spring 2.5- getBean(String name, Object… args) 覆盖默认参数
- 根据 Bean 类型查找
- Bean 实时查找
- Spring 3.0 - getBean(Class requiredType)
- Spring 4.1 - getBean(Class requiredType, Object… args) 覆盖默认参数
- Spring 5.1 Bean 延迟查找
- getBeanProvider(Class requiredType)
- getBeanProvider(ResolvableType requiredType)
- Bean 实时查找
- 根据 Bean 名称 + 类型 查找:getBean(String name, Class requiredType)
覆盖参数是什么意思呢?
比如 getBean()
获取这个 Bean 对象,通过覆盖参数的形式可以在获取对象的同时覆盖掉对象的参数,建议大家不要去使用这种方式,比较危险。
如果 getBean()
返回的对象是一个单例对象,然后每调用一次,都会去覆盖,这种方式非常不可取,获得的对象的状态不可控。
3.1 根据 Bean 名称查找
第二章:IoC 依赖查找 4.3~4.4 小结
3.2 根据 Bean 类型查找
第二章:IoC 依赖查找 4.5~4.6 小结
3.3 根据 Bean 名称 + 类型 查找
第二章:IoC 依赖查找 4.7 小结
3.4 Spring 5.1 Bean 延迟查找
在第二章里面演示了 ObjectFactory
来进行延迟查找,我们这里用 Spring 5.1 里面的提供新 API
ObjectProvider
来演示。
注:示例中注释掉代码中是另一种写法
/**
* 通过{@link org.springframework.beans.factory.ObjectProvider} 进行依赖查找
*/
public class ObjectProviderDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类作为配置类{Configuration.class}
applicationContext.register(ObjectProviderDemo.class);
//启动应用上下文
applicationContext.refresh();
lookupByObjectProvider(applicationContext);
applicationContext.close();
}
private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> beanProvider = applicationContext.getBeanProvider(String.class);
// Iterable<String> stringIterable = beanProvider;
// for (String str : stringIterable) {
// System.out.println(str);
// }
beanProvider.stream().forEach(System.out::println);
}
@Bean
//如果没有定义name 那么方法名就是 Bean 的名称
public String helloWorld() {
return "Hello,World";
}
}
4.集合类型依赖查找
集合类型依赖查找接口 - ListableBeanFactory
- 根据 Bean 类型查找
- 获取同类型 Bean 名称列表
- getBeanNamesForType(Class)
- Spring 4.2 - getBeanNamesForType(ResolvableType)
- 主要应用于泛型上面的实现
- 获取同类型 Bean 实例列表
- getBeansOfType(Class) 以及重载方法
- 获取同类型 Bean 名称列表
- 通过注解类型查找
- Spring 3.0 - 获取标注类型 Bean 名称列表
- getBeanNamesForAnnotation(Class<? extends Annotation>)
- Spring 3.0 - 获取标注类型 Bean 实例列表
- getBeansWithAnnotation(Class<? extends Annotation>)
- Spring 3.0 - 获取指定名称+标注类型 Bean 实例
- findAnnotationOnBean(String,Class<? extends Annotation>)
- Spring 3.0 - 获取标注类型 Bean 名称列表
相关示例
第二章:IoC 依赖查找 4.6、4.8 小结
5.层次性依赖查找
层次性依赖查找接口 - HierarchicalBeanFactory
- 双亲 BeanFactory : getParentBeanFactory()
- 层次性查找
- 根据 Bean 名称查找
- 基于 containsLocalBean 方法实现
- 根据 Bean 类型查找实例列表
- 单一类型:BeanFactoryUtils#beanOfType
- 集合类型:BeanFactoryUtils#beansOfTypeIncludingAncestors
- 根据 Java 注解查找名称列表
- BeanFactoryUtils#beanNamesForTypeIncludingAncestors
- 根据 Bean 名称查找
5.1 源码分析
我们先看一个相关的类图
结合部分源码
HierarchicalBeanFactory#getParentBeanFactory() 方法表明了他具有层次性,其实是一种双亲委派模式的实现。
ConfigurableBeanFactory 继承了 HierarchicalBeanFactory,从而也具有了层次性
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {...
ConfigurableListableBeanFactory 又继承了 ConfigurableBeanFactory,通过这种组合的方式使得 ConfigurableListableBeanFactory
具有了可配置性(Configurable)、集合遍历性(Listable)、层次性(Hierarchical)等等,下面我们用这个层次性实现类来演示相关代码。
public interface ConfigurableListableBeanFactory
extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {...
5.2 ParentBeanFactory 示例
设置当前 BeanFactory 的 Parent BeanFactory
/**
* 层次性的依赖查找示例
*/
public class HierarchicalDependencyLookupDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类作为配置类{Configuration.class}
applicationContext.register(HierarchicalDependencyLookupDemo.class);
//1.获取 HierarchicalBeanFactory <- ConfigurableBeanFatory <- ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
System.out.println("当前 BeanFactory 的 Parent BeanFactory" + beanFactory.getParentBeanFactory());
//设置 Parent BeanFactory
beanFactory.setParentBeanFactory(createParentBeanFactory());
System.out.println("当前 BeanFactory 的 Parent BeanFactory" + beanFactory.getParentBeanFactory());
//启动应用上下文
applicationContext.refresh();
//关闭
applicationContext.close();
}
private static BeanFactory createParentBeanFactory() {
// 创建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 加载配置
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
String location = "classpath:/META-INF/dependency-injection-context.xml";
reader.loadBeanDefinitions(location);
return beanFactory;
}
}
执行结果
当前 BeanFactory 的 Parent BeanFactory:null
当前 BeanFactory 的 Parent BeanFactory:org.springframework.beans.factory.support.DefaultListableBeanFactory@3427b02d: defining beans [user,superUser,objectFactory,userRepository]; root of factory hierarchy
第一次输出的结果是 null ,因为我们还没有设置过当前 BeanFactory 的 Parent BeanFactory。
我们创建一个新的 BeanFactory,通过 beanFactory.setParentBeanFactory()
方法进行设置。
第二次输出的就是我们设置的 BeanFactory。
5.3 LocalBean 示例
判断当前 BeanFactory 是否包含这个 Bean
在上述例子中增加这个方法
private static void displayLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前 BeanFactory [%s] 是否包含 bean[name:%s] \n 结果: %s\n", beanFactory, beanFactory,
beanFactory.containsLocalBean(beanName));
}
main()
方法中调用displayLocalBean(beanFactory, "user");
执行结果
当前 BeanFactory [org.springframework.beans.factory.support.DefaultListableBeanFactory@32eff876: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@3427b02d] 是否包含 bean[name:org.springframework.beans.factory.support.DefaultListableBeanFactory@32eff876: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,hierarchicalDependencyLookupDemo]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@3427b02d]
结果: false
当然不存在 user 这个 Bean。因为这个 Bean 存在父 BeanFactory 中
在main()
方法中调用displayLocalBean((HierarchicalBeanFactory) beanFactory.getParentBeanFactory(), "user");
结果为 true;为了更优雅的调用,我们可以采用递归的方式进行查询。
private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前 BeanFactory [%s] 是否包含 bean[name:%s] \n 结果: %s\n", beanFactory, beanFactory,
containsBean(beanFactory,beanName));
}
private static boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) {
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory parentHierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
if (containsBean(parentHierarchicalBeanFactory,beanName)) {
return true;
}
}
return beanFactory.containsLocalBean(beanName);
}
5.4 BeanFactoryUtils
Spring 中也有类似的实现,也是通过递归的方式。
org.springframework.beans.factory.BeanFactoryUtils#beanNamesForTypeIncludingAncestors()
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, ResolvableType type) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForType(type);
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type);
result = mergeNamesWithParent(result, parentResult, hbf);
}
}
return result;
}
6.延迟依赖查找
Bean 延迟依赖查找接口
- org.springframework.beans.factory.ObjectFactory
- org.springframework.beans.factory.ObjectProvider
- spring 5 对 Java 8 特性扩展
- 函数式接口
- getIfAvailable(Supplier)
- ifAvailable(Consumer)
- Stream 扩展 - stream()
- 函数式接口
- spring 5 对 Java 8 特性扩展
6.1 getIfAvailable
我们先在代码中通过注解定义一个 User
@Bean
public User user() {
return User.createUser("ifAvailable-user");
}
下面方法中User::createUser
只是提供兜底实现(当获取的对象为空时),也可以不写。
private static void lookupGetIfAvailable(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
// User ifAvailable = beanProvider.getIfAvailable(()->User.createUser());
User ifAvailable = beanProvider.getIfAvailable(User::createUser);
System.out.println(ifAvailable);
}
6.2 ifAvailable
通过 Consumer 的方式消费掉,我们这里直接打印
private static void lookupIfAvailable(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
beanProvider.ifAvailable(System.out::println);
}
6.3 Stream 扩展
private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> beanProvider = applicationContext.getBeanProvider(String.class);
// Iterable<String> stringIterable = beanProvider;
// for (String str : stringIterable) {
// System.out.println(str);
// }
beanProvider.stream().forEach(System.out::println);
}
7.安全依赖查找
依赖查找安全性对比
依赖查找类型 | 代表实现 | 是否安全 |
---|---|---|
单一类型查找 | BeanFactory#getBean | 否 |
ObjectFactroy#getObject | 否 | |
ObjectProvider#getIfAvailable | 是 | |
集合类型查找 | ListableBeanFactory#getBeansOfType | 是 |
ObjectProvider#stream | 是 |
注意:层次性依赖查找的安全性取决于其扩展的单一或者集合类型的 BeanFactory 接口
完整示例 TypeSafetyDependencyLookupDemo
/**
* 类型安全 依赖查找示例
*/
public class TypeSafetyDependencyLookupDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类作为配置类{Configuration.class}
applicationContext.register(TypeSafetyDependencyLookupDemo.class);
//启动应用上下文
applicationContext.refresh();
//演示 BeanFactory#getBean 安全性
displayBeanFactoryGetBean(applicationContext);
//演示 ObjectFactory#getObject 安全性
displayObjectFactoryGetObject(applicationContext);
//演示 ObjectProvider#ifAvailable 安全性
displayObjectProviderIfAvailable(applicationContext);
//演示 ListableBeanFactory#getBeansOfType 安全性
displayListableBeanFactoryGetBeansOfType(applicationContext);
//演示 ObjectProvider#stream 安全性
displayObjectProviderStreamOps(applicationContext);
applicationContext.close();
}
private static void displayObjectProviderStreamOps(AnnotationConfigApplicationContext applicationContext) {
printBeansException("displayObjectProviderStreamOps",()->{
ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
beanProvider.stream().forEach(System.out::println);
});
}
private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory applicationContext) {
printBeansException("displayListableBeanFactoryGetBeansOfType",()->{
applicationContext.getBeansOfType(User.class);
});
}
private static void displayObjectProviderIfAvailable(AnnotationConfigApplicationContext applicationContext) {
printBeansException("displayObjectProviderIfAvailable", () -> {
ObjectProvider<User> beanProvider = applicationContext.getBeanProvider(User.class);
System.out.println(beanProvider.getIfAvailable(User::createUser));
});
}
private static void displayObjectFactoryGetObject(AnnotationConfigApplicationContext applicationContext) {
printBeansException("displayObjectFactoryGetObject", () -> {
ObjectFactory<User> beanProvider = applicationContext.getBeanProvider(User.class);
beanProvider.getObject();
});
}
private static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
}
private static void printBeansException(String source, Runnable runnable) {
System.err.println("===========================" );
System.err.println("Source from : " + source);
try {
runnable.run();
} catch (BeansException e) {
e.printStackTrace();
}
}
}
7.1 BeanFactory#getBean(不安全)
如果上下文中没有 User 这个Bean类型的定义,会抛出NoSuchBeanDefinitionException
输出结果:
Source from : displayBeanFactoryGetBean
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.huajie.thinking.in.spring.ioc.overview.domain.User' available
...
7.2 ObjectFactroy#getObject(不安全)
如果上下文中没有 User 这个Bean类型的定义,会抛出NoSuchBeanDefinitionException
输出结果:
Source from : displayObjectFactoryGetObject
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.huajie.thinking.in.spring.ioc.overview.domain.User' available
...
7.3 ObjectProvider#getIfAvailable(安全)
即使如果上下文中没有 User 这个Bean类型的定义,也不会报错
输出结果:
Source from : displayObjectProviderIfAvailable
7.4 ListableBeanFactory#getBeansOfType(安全)
即使如果上下文中没有 User 这个Bean类型的定义,也不会报错
输出结果:
Source from : displayListableBeanFactoryGetBeansOfType
7.5 ObjectProvider#stream(安全)
即使如果上下文中没有 User 这个Bean类型的定义,也不会报错
输出结果:
Source from : displayObjectProviderStreamOps
8.内建可查找的依赖
本章只是做介绍,后续的章节会做详细的讨论。
AbstractApplicationContext 内建可查找的依赖
Bean 名称 | Bean 实例 | 使用场景 |
---|---|---|
environment | Environment 对象 | 外部化配置以及 Profiles |
systemProperties | java.util.Properties 对象 | Java 系统属性 |
systemEnvironment | java.util.Map 对象 | 操作系统环境变量 |
messageSource | MessageSource 对象 | 国际化文案 |
lifecycleProcessor | LifecycleProcessor 对象 | Lifecycle Bean 处理器 |
applicationEventMulticaster | ApplicationEventMulticaster 对象 | Spring 事件广播 |
注解驱动 Spring 应用上下文内建可查找的依赖(部分)
相关细节:
org.springframework.context.annotation.AnnotationConfigUtils
- registerAnnotationConfigProcessors()
Bean 名称 | Bean 实例 | 使用场景 |
---|---|---|
org.springframework.context.annotation. internalConfigurationAnnotationProcessor | ConfigurationClassPostProcessor 对象 | 处理Spring配置类 |
org.springframework.context.annotation. internalAutowiredAnnotationProcessor | AutowiredAnnotationBeanPostProcessor对象 | 处理 @Autowired 以及 @Value 注解 |
org.springframework.context.annotation. internalCommonAnnotationProcessor | CommonAnnotationBeanPostProcessor对象 | (条件激活) 处理 JSR - 250 注解,比如 @PostConstruct |
org.springframework.context.event. internalEventListenerProcessor | EventListenerMethodProcessor对象 | 处理标准 @EventListener 的 Spring 事件监听方法 |
org.springframework.context.event. internalEventListenerFactory | DefaultEventlistenerFactory 对象 | @EvenListener 事件监听方法适配为 ApplicationListener |
org.springframework.context.annotation. internalPersistenceAnnotationProcessor | PersistenceAnnotationBeanPostProcessor 对象 | (条件激活)处理 JPA 注解场景 |
9.依赖查找中的经典异常
BeansException 子类型
异常类型 | 触发条件 | 场景举例 |
---|---|---|
NoSuchBeanDefinitionException | 当查找 Bean 不存在与 IOC 容器时 | BeanFactory#getBean ObjectFactory#getObject |
NoUniqueBeanDefinitionException | 类型依赖查找时,IOC 容器存在多个 Bean 实例 | BeanFactory#getBean(Class) |
BeanInstantiationException | 当 Bean 所对应的类型非具体类时 | BeanFactory#getBean |
BeanCreationException | 当 Bean 初始化过程中 | Bean 初始化方法执行异常时 |
BeanDefinitionStoreException | 当 BeanDefinition 配置元信息非法时 | XML 配置资源无法打开时 |
9.1 NoUniqueBeanDefinitionException
当前上下文中存在多个相同类型 Bean 的定义
/**
* {@link NoUniqueBeanDefinitionException}示例
*/
public class NoUniqueBeanDefinitionExceptionDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类作为配置类{Configuration.class}
applicationContext.register(NoUniqueBeanDefinitionExceptionDemo.class);
//启动应用上下文
applicationContext.refresh();
try {
applicationContext.getBean(String.class);
} catch (NoUniqueBeanDefinitionException e) {
System.err.printf(" Spring 应用上下文存在%d个 %s 类型的 Bean,具体原因:%s",
e.getNumberOfBeansFound(),
String.class.getName(),
e.getMessage());
}
applicationContext.close();
}
@Bean
private String bean1() {
return "1";
}
@Bean
private String bean2() {
return "2";
}
@Bean
private String bean3() {
return "3";
}
}
9.2 BeanInstantiationException
当 Bean 所对应的类型非具体类时,比如是一个接口CharSequence
/**
* {@link BeanInstantiationException}示例
*/
public class BeanInstantiationExceptionDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CharSequence.class);
applicationContext.registerBeanDefinition("exception-bean", beanDefinitionBuilder.getBeanDefinition());
//启动应用上下文
applicationContext.refresh();
applicationContext.close();
}
}
9.4 BeanCreationException
Bean 创建过程中发生异常
/**
* {@link BeanCreationException}示例
*/
public class BeanCreationExceptionDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
//在初始化的时候,抛出异常
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(POJO.class);
applicationContext.registerBeanDefinition("exception-bean", beanDefinitionBuilder.getBeanDefinition());
//启动应用上下文
applicationContext.refresh();
applicationContext.close();
}
static class POJO implements InitializingBean{
@Override
public void afterPropertiesSet() throws Exception {
throw new Exception("For purposes...");
}
}
}
10.面试题
1.ObjectFactory 与 BeanFactory 区别?
两者都是提供依赖查找的能力,ObjectFactory 是 Spring 早期的接口。
ObjectFactory 仅关注一个或者一种类型的 Bean 依赖查找,并且自身不具备依赖查找的能力,能力则由 BeanFactory 输出,例如 ObjectFactoryCreatingFactoryBean 类 通过 setTargetBeanName 方法设置 beanName 再通过 getObject 方法,实际上还是通过 BeanFactory 来查找,通过这种方式实现一个延迟查找、间接查找。
public Object getObject() throws BeansException {
return this.beanFactory.getBean(this.targetBeanName);
}
BeanFactory 提供了单一类型,集合类型以及层次性等多种依赖查找方式。
2.BeanFactory.getBean 操作是否线程安全?
是线程安全,操作过程中会增加互斥锁。
3.Spring 依赖查找与注入在来源上的区别?
这个问题后续章节会做解答,下一章将会讨论依赖注入相关的内容。
11.参考
- 极客时间-小马哥《小马哥讲Spring核心编程思想》