依赖查找的今世前生:Spring IoC容器从Java标准中学到了什么?
单一类型依赖查找:如何查找已知名称或类型的Bean对象?
public class ObjectProviderDemo { // @Configuration 是非必须注解
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 ObjectProviderDemo 作为配置类(Configuration Class)
applicationContext.register(ObjectProviderDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 依赖查找集合对象
lookupByObjectProvider(applicationContext);
// 关闭应用上下文
applicationContext.close();
}
@Bean
@Primary
public String helloWorld() { // 方法名就是 Bean 名称 = "helloWorld"
return "Hello,World";
}
@Bean
public String message() {
return "Message";
}
private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
System.out.println(objectProvider.getObject());
}
}
ObjectProvider 继承 ObjectFactory接口
public interface ObjectProvider extends ObjectFactory, Iterable {
集合类型依赖查找:如何查找已知类型多个Bean集合?
ListableBeanFactory是针对某个类型去查找的一个集合列表,集合列表有2中情况,一种是查询bean的名称,一种情况是查询Bean的实例.推荐使用bean的名称去判断这个bean是否存在,当然最重要的是方式是判断BeanDefinition是否存在,
这种方式会避免提早初始化bean,产生一些不确定的因素.
层次性依赖查找:依赖查找也有双亲委派?
public class HierarchicalDependencyLookupDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
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. 设置 Parent BeanFactory
HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
beanFactory.setParentBeanFactory(parentBeanFactory);
// System.out.println("当前 BeanFactory 的 Parent BeanFactory : " + beanFactory.getParentBeanFactory());
displayContainsLocalBean(beanFactory, "user");
displayContainsLocalBean(parentBeanFactory, "user");
displayContainsBean(beanFactory, "user");
displayContainsBean(parentBeanFactory, "user");
// 启动应用上下文
applicationContext.refresh();
// 关闭应用上下文
applicationContext.close();
}
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);
}
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 ConfigurableListableBeanFactory createParentBeanFactory() {
// 创建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// XML 配置文件 ClassPath 路径
String location = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载配置
reader.loadBeanDefinitions(location);
return beanFactory;
}
}
beanFactory之间的层次性关系
HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
HierarchicalBeanFactory ->层次性的beanFactory
ConfigurableBeanFactory -> 可编辑的可配置的beanFactory
ConfigurableListableBeanFactory -> 可编辑的集合类型的层次性的beanFactory
容器的层次关系主要的目的是实现 Bean 复用,假设一个应用存在一个 Root ApplicationContext,内部的 Bean 来自于一个 jar 包,那么,这个jar 包可能被两个不同的 Servlet 应用使用,这时,ServletContext A 和 ServletContext B 同时复用了这个 parent ApplicationContext,而它自己也有 ApplicationContext,这也是 Spring Web MVC 所涉及的应用上下文架构。
Spring Framework(包括 SpringMVC) 确实是两个应用上下文,当初的设计主要是为了复用,Root ApplicationContext 使用 Biz 组件,而 DispatcherServlet 关联的 ApplicationContext 则是管理 Web 组件。不过在 Spring Boot 场景下,收敛到同一个 ApplicationContext 中了
延迟依赖查找:非延迟初始化Bean也能实现延迟查找?
public class ObjectProviderDemo { // @Configuration 是非必须注解
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 ObjectProviderDemo 作为配置类(Configuration Class)
applicationContext.register(ObjectProviderDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 依赖查找集合对象
lookupByObjectProvider(applicationContext);
lookupIfAvailable(applicationContext);
lookupByStreamOps(applicationContext);
// 关闭应用上下文
applicationContext.close();
}
private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
// Iterable<String> stringIterable = objectProvider;
// for (String string : stringIterable) {
// System.out.println(string);
// }
// Stream -> Method reference
objectProvider.stream().forEach(System.out::println);
}
private static void lookupIfAvailable(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() { // 方法名就是 Bean 名称 = "helloWorld"
return "Hello,World";
}
@Bean
public String message() {
return "Message";
}
private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
System.out.println(objectProvider.getObject());
}
}
ObjectProvider 是间接的依赖查找,ObjectProvider 相当于一个代理,实际依赖查找发生在ObjectProvider#get 等方法时。
延迟指的就是查找的时候并没有查找到想要查找的那个bean而是查找到了,objectFactory或者objectProvider。并且Provider还比Factory多了能查找出多个的功能
安全依赖查找
public class TypeSafetyDependencyLookupDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 TypeSafetyDependencyLookupDemo 作为配置类(Configuration Class)
applicationContext.register(TypeSafetyDependencyLookupDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 演示 BeanFactory#getBean 方法的安全性
displayBeanFactoryGetBean(applicationContext);
// 演示 ObjectFactory#getObject 方法的安全性
displayObjectFactoryGetObject(applicationContext);
// 演示 ObjectProvider#getIfAvaiable 方法的安全性
displayObjectProviderIfAvailable(applicationContext);
// 演示 ListableBeanFactory#getBeansOfType 方法的安全性
displayListableBeanFactoryGetBeansOfType(applicationContext);
// 演示 ObjectProvider Stream 操作的安全性
displayObjectProviderStreamOps(applicationContext);
// 关闭应用上下文
applicationContext.close();
}
private static void displayObjectProviderStreamOps(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
printBeansException("displayObjectProviderStreamOps", () -> userObjectProvider.forEach(System.out::println));
}
private static void displayListableBeanFactoryGetBeansOfType(ListableBeanFactory beanFactory) {
printBeansException("displayListableBeanFactoryGetBeansOfType", () -> beanFactory.getBeansOfType(User.class));
}
private static void displayObjectProviderIfAvailable(AnnotationConfigApplicationContext applicationContext) {
ObjectProvider<User> userObjectProvider = applicationContext.getBeanProvider(User.class);
printBeansException("displayObjectProviderIfAvailable", () -> userObjectProvider.getIfAvailable());
}
private static void displayObjectFactoryGetObject(AnnotationConfigApplicationContext applicationContext) {
// ObjectProvider is ObjectFactory
ObjectFactory<User> userObjectFactory = applicationContext.getBeanProvider(User.class);
printBeansException("displayObjectFactoryGetObject", () -> userObjectFactory.getObject());
}
public 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 exception) {
exception.printStackTrace();
}
}
}
这里指的是异常安全,也就是就会异常分险
@Autowired
private ApplicationContext applicationContext;
可以直接注入ApplicationContext ,
通过实现BeanFactoryPostProcessor接口也可以注入,通过实现ApplicationContextAware接口也可以注入,在我们日常开发中,我们一般在使用上有什么区别,一般来说各种的使用场景是什么?
BeanFactoryPostProcessor 回调 BeanFactory 的时机较早,早于 ApplicationContextAware。BeanFactoryPostProcessor 属于 BeanFactory 生命周期,而 ApplicationContextAware 属于 Bean 生命周期。
BeanFactoryPostProcessor 传递的是 BeanFactory,而 ApplicationContextAware 则是 ApplicationContext,对象并非同一个,前面也提到了 ApplicationContext 会 Wrap BeanFactory 示例。
内建可查找的依赖:哪些Spring IoC容器内建依赖可供查找?
依赖查找中的经典异常:Bean找不到?Bean不是唯一的?Bean创建失败?
BeanCreationExceptionDemo
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 {
@PostConstruct // CommonAnnotationBeanPostProcessor
public void init() throws Throwable {
throw new Throwable("init() : For purposes...");
}
@Override
public void afterPropertiesSet() throws Exception {
throw new Exception("afterPropertiesSet() : For purposes...");
}
}
}
BeanInstantiationExceptionDemo
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();
}
}
NoUniqueBeanDefinitionExceptionDemo
public class NoUniqueBeanDefinitionExceptionDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 NoUniqueBeanDefinitionExceptionDemo 作为配置类(Configuration Class)
applicationContext.register(NoUniqueBeanDefinitionExceptionDemo.class);
// 启动应用上下文
applicationContext.refresh();
try {
// 由于 Spring 应用上下文存在两个 String 类型的 Bean,通过单一类型查找会抛出异常
applicationContext.getBean(String.class);
} catch (NoUniqueBeanDefinitionException e) {
System.err.printf(" Spring 应用上下文存在%d个 %s 类型的 Bean,具体原因:%s%n",
e.getNumberOfBeansFound(),
String.class.getName(),
e.getMessage());
}
// 关闭应用上下文
applicationContext.close();
}
@Bean
public String bean1() {
return "1";
}
@Bean
public String bean2() {
return "2";
}
@Bean
public String bean3() {
return "3";
}
}
面试题
ObjectFactoryCreatingFactoryBean中
@Override
protected ObjectFactory<Object> createInstance() {
BeanFactory beanFactory = getBeanFactory();
Assert.state(beanFactory != null, "No BeanFactory available");
Assert.state(this.targetBeanName != null, "No target bean name specified");
return new TargetBeanObjectFactory(beanFactory, this.targetBeanName);
}
private static class TargetBeanObjectFactory implements ObjectFactory<Object>, Serializable {
private final BeanFactory beanFactory;
private final String targetBeanName;
public TargetBeanObjectFactory(BeanFactory beanFactory, String targetBeanName) {
this.beanFactory = beanFactory;
this.targetBeanName = targetBeanName;
}
@Override
public Object getObject() throws BeansException {
return this.beanFactory.getBean(this.targetBeanName);
}
}
在TargetBeanObjectFactory中最终还是使用的beanFactory