1.写在前面
前面的博客我们介绍了Spring的总览,今天我们来了解下Spring的依赖查找的相关的内容,我们会从它的前世今生来带你了解下,以及各种类型的查找的方式,同时介绍对应的相对比较安全的查找的方式。以及会介绍一些比较常见的面试题,供大家参考。
2.依赖查找的今世前生
那么提到依赖查找,很多人第一个想到的就是Spring中的依赖查找,但是在Spring出来之前,Java中其实已经就有了依赖查找的API了,具体的如下,同时Spring也参考其中的一部分。
单一类型依赖查找
- JNDI - javax.naming.Context#lookup(javax.naming.Name)
- JavaBeans - java.beans.beancontext.BeanContext
集合类型依赖查找
- java.beans.beancontext.BeanContext
层次性依赖查找
- java.beans.beancontext.BeanContext
单一类型的依赖查找
那么我这儿先带大家看下对应的源码,后面再带着大家写几个例子,首先我们先看对应的Java
中的Context
类,具体的如下:
上面的两个方法都是javax.naming.Context
提供的Bean的查找的方法。
接下来我们继续看一下BeanContext
,这个BeanContext
是Spring
参考对应的实现的方式。具体的内容如下:
可以看到这接口继承的了Collection
接口,我们可以知道这个接口下面所有的Bean
都是存在这个集合下面的,对这个集合进行增删改查。
集合类型依赖查找
集合类型查找是根据一个key
去查找出来一个集合的类型。
对应的方法如上图所示,返回的是一个object对象,也就是集合的对象也是可以返回的
层次性依赖查找BeanContext的源码写的比较混乱,这儿我们不做介绍了。
3.单一类型依赖查找
单一类型依赖查找接口 - 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 实时查找
- 根据 Bean 名称 + 类型查找: getBean(String,Class)
上面的有些的查找的方式我们前面的博客已经介绍过了,上面的覆盖默认参数的方式,我们只要知道有这个方法就行了,不需要做过多的了解,因为这个方法比较危险,它会覆盖默认的参数。
我们看一下对应的5.1的延迟查找的API,具体的如下:
这儿我们可以看到返回的是ObjectProvider
,我们点开这个实现类的内容,具体的如下:
可以发现这个类是继承了ObjectFactory
类的,那么一切都那么好理解了。
我们写了一个简单的例子就行,具体的代码如下:
package org.learn.spring.dependency.lookup;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
// 通过 ObjectProvider 进行依赖查找
public class ObjectProviderDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类ObjectProviderDemo 作为配置类(configuration class)
applicationContext.register(ObjectProviderDemo.class);
// 启动应用上下文
applicationContext.refresh();
lookupByObjectProvider(applicationContext);
applicationContext.close();
}
private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
System.out.println(objectProvider.getObject());
}
// 方法的名称就是Bean的名称, Bean的名称helloWorld
@Bean
public String helloWorld(){
return "Hello World";
}
}
还有一个getBeanProvider(ResolvableType)
这个方法我们后面再说,ResolvableType
这个类型主要是用来处理泛型的。等到后面我们介绍泛型的时候,我们再来详细的介绍。
4.集合类型依赖查找
集合类型依赖查找接口 - ListableBeanFactory
- 根据 Bean 类型查找
- 获取同类型 Bean 名称列表
getBeanNamesForType(Class)
Spring 4.2 getBeanNamesForType(ResolvableType)
- 获取同类型 Bean 实例列表
getBeansOfType(Class)
以及重载方法
- 通过注解类型查找
- 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 名称列表
上面的API我前面都有或多或少的介绍过,我这儿不做赘述了,从上面的API我们可以得到一个结论就是ListableBeanFactory
主要用来查找集合的。而方式有两种一种是通过Bean的类型查找,一种是通过注解的类型去查找。集合的列表主要有两种情况,一种查询Bean的名称的集合,一种是查询Bean的实例的集合。
5.层次性依赖查找
层次性依赖查找接口 - HierarchicalBeanFactory
将单一类型的查找和集合类型查找合并到一起去
- 双亲 BeanFactory: getParentBeanFactory()
- 层次性查找
- 根据 Bean 名称查找
- 基于
containsLocalBean
方法实现
- 基于
- 根据 Bean 类型查找实例列表
- 单一类型:
BeanFactoryUtils#beanOfType
- 集合类型:
BeanFactoryUtils#beansOfTypeIncludingAncestors
- 单一类型:
- 根据 Java 注解查找名称列表
BeanFactoryUtils#beanNamesForTypeIncludingAncestors
- 根据 Bean 名称查找
具体的代码如下:
package org.learn.spring.dependency.lookup;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
// HierarchicalBeanFactory 层次查找依赖实例
public class HierarchicalDependencyLookup {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类ObjectProviderDemo 作为配置类(configuration class)
applicationContext.register(ObjectProviderDemo.class);
// 1.获取HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
System.out.println("当前BeanFactory 的 Parent BeanFactory:" + beanFactory.getParentBeanFactory());
// 2.设置ParentBeanFactory
HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
beanFactory.setParentBeanFactory(parentBeanFactory);
System.out.println("当前BeanFactory 的 Parent BeanFactory:" + beanFactory.getParentBeanFactory());
// 启动应用上下文
applicationContext.refresh();
displayContainsLocalBean(beanFactory, "user");
displayContainsLocalBean(parentBeanFactory, "user");
displayContainsBean(beanFactory, "user");
displayContainsBean(parentBeanFactory, "user");
applicationContext.close();
}
private static ConfigurableListableBeanFactory createParentBeanFactory() {
// 创建BeanFactory容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// XML 配置文件的路径
String location = "classpath:META-INF/dependency-lookup-context.xml";
// 加载配置
reader.loadBeanDefinitions(location);
return beanFactory;
}
private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前 BeanFactory[%s] 是否包含 Local Bean [name : %s] : %s\n", beanFactory, beanName, beanFactory.containsLocalBean(beanName));
}
private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前 BeanFactory[%s] 是否包含 Bean [name : %s] : %s\n", beanFactory, beanName, 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);
}
}
上面的代码就是实现了层次的查找,这个时候是有两个工厂,一个是父工厂,一个子工厂,containsBean
方法就是通过递归的方式来查找所有的工厂中包含的对应的Bean。
其实BeanFactoryUtils
有相应的实现,具体的的代码如下:
6.延迟依赖查找
Bean 延迟依赖查找接口
- org.springframework.beans.factory.ObjectFactory
- org.springframework.beans.factory.ObjectProvider
- Spring 5 对 Java 8 特性扩展
- 函数式接口
- getIfAvailable(Supplier)
- ifAvailable(Consumer)
- Stream 扩展 - stream()
具体的代码如下:
package org.learn.spring.dependency.lookup;
import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
// 通过 ObjectProvider 进行依赖查找
public class ObjectProviderDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类ObjectProviderDemo 作为配置类(configuration class)
applicationContext.register(ObjectProviderDemo.class);
// 启动应用上下文
applicationContext.refresh();
lookupByAvailable(applicationContext);
lookupByStreamOps(applicationContext);
applicationContext.close();
}
private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
Iterable<String> stringIterable = objectProvider;
for (String s : stringIterable) {
System.out.println(s);
}
objectProvider.stream().forEach(System.out::println);
}
private static void lookupByAvailable(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
User user = userObjectProvider.getIfAvailable(User::createUser);
System.out.println("当前 User 对象:" + user);
}
@Bean
@Primary
public String helloWorld() {
return "Hello World";
}
@Bean
public String message() {
return "Message";
}
}
7.安全依赖查找
依赖查找安全性对比
注意: 层次性依赖查找的安全性取决于其扩展的单一或集合类型的 BeanFactory 接口
具体的代码如下:
package org.learn.spring.dependency.lookup;
import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
// 类型安全查找的示例
public class TypeSafetyDependencyLookupDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类ObjectProviderDemo 作为配置类(configuration class)
applicationContext.register(ObjectProviderDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 演示BeanFactory#getBean方法的安全性 不安全的
displayBeanFactoryGetBean(applicationContext);
// 演示ObjectFactory#getObject方法的安全性 不安全的
displayObjectFactoryGetObject(applicationContext);
// 演示ObjectProvider#getIfAvailable方法的安全性 安全的
displayObjectProviderGetIfAvailable(applicationContext);
// 演示 ListableBeanFactory#getBeansOfType方法的安全性 安全的 也是会抛出BeansException 但是这个只有在Bean创建失败的时候抛出
displayListableBeanFactoryGetBeansOfType(applicationContext);
//演示ObjectProvider#StreamOps方法的安全性 安全的
displayObjectProviderStreamOps(applicationContext);
// 关闭应用上下文
applicationContext.close();
}
// 不安全的
public static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
}
private static void displayObjectFactoryGetObject(AnnotationConfigApplicationContext applicationContext) {
// ObjectProvider is ObjectFactory
ObjectFactory<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
printBeansException("displayObjectFactoryGetObject", userObjectProvider::getObject);
}
private static void displayObjectProviderGetIfAvailable(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
printBeansException("displayObjectProviderGetIfAvailable", userObjectProvider::getIfAvailable);
}
// 也是会抛出BeansException 但是这个只有在Bean创建失败的时候抛出
private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory beanFactory) {
printBeansException("displayListableBeanFactoryGetBeansOfType", () -> beanFactory.getBeansOfType(User.class));
}
private static void displayObjectProviderStreamOps(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
printBeansException("displayObjectProviderStreamOps", () -> userObjectProvider.stream().forEach(System.out::println));
}
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();
}
}
}
运行结果如下:
可以看出与上面的表格是一样的。
8.内建可查找的依赖
AbstractApplicationContext
内建可查找的依赖
注解驱动 Spring 应用上下文内建可查找的依赖( 部分)
注解驱动 Spring 应用上下文内建可查找的依赖( 续)
那么这些类在什么地方注册的呢?具体的如下:
后面在初始化的流程中,我们会详细的分析,上面的代码是AnnotationConfigUtils
类中的
9.依赖查找中的经典异常
BeansException 子类型
具体的代码如下:
演示NoUniqueBeanDefinitionException
异常
package org.learn.spring.dependency.lookup;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
// NoUniqueBeanDefinitionException 的异常举例
public class NoUniqueBeanDefinitionExceptionDemo {
public static void main(String[] args) {
// 创建BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// NoUniqueBeanDefinitionExceptionDemo 作为配置类(configuration class)
applicationContext.register(NoUniqueBeanDefinitionExceptionDemo.class);
// 启动应用上下文
applicationContext.refresh();
try {
// 由于应用上下文存在两个String类型的Bean,通过单一查找会抛出异常
applicationContext.getBean(String.class);
} catch (NoUniqueBeanDefinitionException e) {
System.err.printf("Spring 应用上下文存在 %d 个 %s 类型的Bean,具体的原因:%s", e.getNumberOfBeansFound(), e.getBeanType(), e.getMessage());
}
// 关闭应用上下文
applicationContext.close();
}
@Bean
public String bean1() {
return "bean1";
}
@Bean
public String bean2() {
return "bean2";
}
}
演示BeanInstantiationException
异常
package org.learn.spring.dependency.lookup;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
// BeanInstantiationException 异常的示例
public class BeanInstantiationExceptionDemo {
public static void main(String[] args) {
// 创建BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册BeanDefinition Bean Class 是CharSequence的接口
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CharSequence.class);
applicationContext.registerBeanDefinition("errorBean", beanDefinitionBuilder.getBeanDefinition());
// 启动应用上下文
applicationContext.refresh();
// 关闭应用上下文
applicationContext.close();
}
}
演示BeanCreationException
异常
package org.learn.spring.dependency.lookup;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
// BeanCreationException 异常示例
public class BeanCreationExceptionDemo {
public static void main(String[] args) {
// 创建BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册BeanDefinition Bean Class 是POJO的普通类,不过在初始化方法回调时抛出异常
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(POJO.class);
applicationContext.registerBeanDefinition("errorBean", beanDefinitionBuilder.getBeanDefinition());
// 启动应用上下文
applicationContext.refresh();
// 关闭应用上下文
applicationContext.close();
}
static class POJO implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
throw new Exception("For purposes....");
}
}
}
10.面试题
10.1 ObjectFactory 与 BeanFactory 的区别?
ObjectFactory 与 BeanFactory 均提供依赖查找的能力。
不过 ObjectFactory 仅关注一个或一种类型的 Bean 依赖查找, 并且自身不具备依赖查找的能力, 能力则由 BeanFactory 输出。
BeanFactory 则提供了单一类型、 集合类型以及层次性等多种依赖查找方式。
10.2 BeanFactory.getBean 操作是否线程安全?
BeanFactory.getBean 方法的执行是线程安全的, 操作过程中会增加互斥锁