单一类型查找
在ioc概述演示过demo…
集合类型依赖查找
注: 在spring中的bean可以被二次定义覆盖
ListableBeanFactory可以通过某个类型去查找一个集合的列表,集合列表可能有两种情况,一种是查询Bean的名称、还有一种是查询Bean的实例。推荐使用Bean的名称来判断Bean是否存在,这种方式可以避免提早初始化Bean,产生一些不确定的因素。
层次性依赖查找
如下图的层次结构, 可以看到ConfigurableListableBeanFactory具有层次性的以及可罗列的依赖查找的能力
层次性的依赖查找的实现与下面代码类似, 是通过递归查找实现的
public class HierarchicalDependencyLookupDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 1. 通过 @Bean方式定义, 注意, 注解尽管多种方式, 但是不会重复注册
applicationContext.register(ObjectProviderDemo.class);
applicationContext.refresh();
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
// System.out.println("当前的beanFactory的parent: " + beanFactory.getParentBeanFactory());
HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
beanFactory.setParentBeanFactory(parentBeanFactory);
// System.out.println("当前的beanFactory的parent: " + beanFactory.getParentBeanFactory());
displayContainsLocalBean(beanFactory, "user");
displayContainsLocalBean(parentBeanFactory, "user");
displayContainsBean(beanFactory,"user");
displayContainsBean(parentBeanFactory,"user");
applicationContext.close();
}
private static HierarchicalBeanFactory createParentBeanFactory() {
// 创建BeanFactory容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 加载配置
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("classpath:META-INF/dependency-lookup-context.xml");
return beanFactory;
}
private static boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) {
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hierarchicalBeanFactory = (HierarchicalBeanFactory) parentBeanFactory;
return containsBean(hierarchicalBeanFactory, beanName);
}
return beanFactory.containsLocalBean(beanName);
}
private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前BeanFactory[%s]是否包含Bean[%s] : %s\n", beanFactory, beanName,
containsBean(beanFactory, beanName));
}
private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前BeanFactory[%s]是否包含localBean[%s] : %s\n", beanFactory, beanName,
beanFactory.containsLocalBean(beanName));
}
}
"C:\Program Files\Java\jdk1.8.0_221\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.2\lib\idea_rt.jar=13889:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_221\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\rt.jar;D:\geekbang-spring-learning\dependency-lookup\target\classes;D:\geekbang-spring-learning\ioc-container-overview\target\classes;C:\XZrepository\org\springframework\spring-context\5.2.2.RELEASE\spring-context-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-aop\5.2.2.RELEASE\spring-aop-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-beans\5.2.2.RELEASE\spring-beans-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-core\5.2.2.RELEASE\spring-core-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-jcl\5.2.2.RELEASE\spring-jcl-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-expression\5.2.2.RELEASE\spring-expression-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-jdbc\5.2.2.RELEASE\spring-jdbc-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-tx\5.2.2.RELEASE\spring-tx-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-webmvc\5.2.2.RELEASE\spring-webmvc-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-web\5.2.2.RELEASE\spring-web-5.2.2.RELEASE.jar;C:\XZrepository\org\springframework\spring-webflux\5.2.2.RELEASE\spring-webflux-5.2.2.RELEASE.jar;C:\XZrepository\io\projectreactor\reactor-core\3.3.1.RELEASE\reactor-core-3.3.1.RELEASE.jar;C:\XZrepository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar" top.xinzhang0618.dependency.lookup.HierarchicalDependencyLookupDemo
当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@51565ec2: 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,objectProviderDemo,helloWorld]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@7e0e6aa2]是否包含localBean[user] : false
当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@7e0e6aa2: defining beans [user,objectFactory,superUser]; root of factory hierarchy]是否包含localBean[user] : true
当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@51565ec2: 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,objectProviderDemo,helloWorld]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@7e0e6aa2]是否包含Bean[user] : true
当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory@7e0e6aa2: defining beans [user,objectFactory,superUser]; root of factory hierarchy]是否包含Bean[user] : true
基本都是单一查找,依赖查找可以在LB这种场景应用,但这个分层查找有啥用?
比如 Spring MVC 中,Biz 组件放在 Root ApplicationContext,而 Web 组件放在 DispatcherServlet 的 ApplicationContext,后者是前者的子 ApplicationContext,所以,子 ApplicationContext 可以读取父 ApplicationContext
请问小马哥,个人还是不太明白这个容器的层次关系的具体应用场景,在什么情况下才有必要构建这个树结构?
作者回复: 容器的层次关系主要的目的是实现 Bean 复用,假设一个应用存在一个 Root ApplicationContext,内部的 Bean 来自于一个 jar 包,那么,这个jar 包可能被两个不同的 Servlet 应用使用,这时,ServletContext A 和 ServletContext B 同时复用了这个 parent ApplicationContext,而它自己也有 ApplicationContext,这也是 Spring Web MVC 所涉及的应用上下文架构。
延迟依赖查找
【学】getIfAvailable写法
如下ObjectProvider接口, 提供了getIfAvailable()的方法, 并提供了default实现, 可以传入一个Supplier做保底
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
@Nullable
T getIfAvailable() throws BeansException;
default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {
T dependency = getIfAvailable();
return (dependency != null ? dependency : defaultSupplier.get());
}
default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
T dependency = getIfAvailable();
if (dependency != null) {
dependencyConsumer.accept(dependency);
}
}
}
-------------------------使用示例
ObjectProvider<String> beanProvider = applicationContext.getBeanProvider(String.class);
String ifAvailable = beanProvider.getIfAvailable(String::new);
安全依赖查找
其实这里讲的就是bean没有或者初始化不成功等场景时, 下列获取bean的方法安不安全, 会不会抛异常
很显然, ObjectProvider#getIfAvailable()就不会抛异常嘛
【学】异常统一捕获处理
public static void displayBeanFactoryGetBean(BeanFactory beanFactory) {
printBeansException("displayBeanFactoryGetBean", () -> beanFactory.getBean(User.class));
}
// 这里其实利用了Runnable这种函数式编程接口, 调用的时候直接写lambda表达式
private static void printBeansException(String source, Runnable runnable) {
System.err.println("==========================================");
System.err.println("Source from :" + source);
try {
runnable.run();
} catch (BeansException exception) {
exception.printStackTrace();
}
}
DefaultListableBeanFactory
DefaultListableBeanFactory是spring/springboot的BeanFactory接口的兜底实现, 它实现了ConfigurableListableBeanFactory
- ConfigurableListableBeanFactory继承了ListableBeanFactory, 因此具有集合类型依赖查找的功能
- ListableBeanFactory继承了BeanFactory, 因此具有了单一类型的依赖查找的功能
- ConfigurableListableBeanFactory继承了ConfigurableBeanFactory, ConfigurableBeanFactory又继承了HierarchicalBeanFactory, 因此具有了层次性依赖查找的功能
spring内建可查找的依赖
依赖查找中的典型异常
面试题
ObjectFactory与BeanFactory的区别?
均提供依赖查找的能力
- ObjectFactory仅关注一个或一种类型的Bean依赖查找, 并且本身不具备依赖查找的能力, 能力则由BeanFactory输出
- BeanFactory提供了单一类型, 集合类型以及层次性等多种依赖查找的方式
BeanFactory.getBean操作是否线程安全?
是的, 操作过程会加互斥锁