前面的博客我们介绍了Spring的总览,今天我们来了解下Spring的依赖查找的相关的内容,我们会从它的前世今生来带你了解下,以及各种类型的查找的方式,同时介绍对应的相对比较安全的查找的方式。以及会介绍一些比较常见的面试题,供大家参考。
那么提到依赖查找,很多人第一个想到的就是Spring中的依赖查找,但是在Spring出来之前,Java中其实已经就有了依赖查找的API了,具体的如下,同时Spring也参考其中的一部分。
单一类型依赖查找
集合类型依赖查找
层次性依赖查找
单一类型的依赖查找
那么我这儿先带大家看下对应的源码,后面再带着大家写几个例子,首先我们先看对应的Java
中的Context
类,具体的如下:
上面的两个方法都是javax.naming.Context
提供的Bean的查找的方法。
接下来我们继续看一下BeanContext
,这个BeanContext
是Spring
参考对应的实现的方式。具体的内容如下:
可以看到这接口继承的了Collection
接口,我们可以知道这个接口下面所有的Bean
都是存在这个集合下面的,对这个集合进行增删改查。
集合类型依赖查找
集合类型查找是根据一个key
去查找出来一个集合的类型。
对应的方法如上图所示,返回的是一个object对象,也就是集合的对象也是可以返回的
层次性依赖查找BeanContext的源码写的比较混乱,这儿我们不做介绍了。
单一类型依赖查找接口 - BeanFactory
上面的有些的查找的方式我们前面的博客已经介绍过了,上面的覆盖默认参数的方式,我们只要知道有这个方法就行了,不需要做过多的了解,因为这个方法比较危险,它会覆盖默认的参数。
我们看一下对应的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 objectProvider = applicationContext.getBeanProvider(String.class);System.out.println(objectProvider.getObject());}// 方法的名称就是Bean的名称, Bean的名称helloWorld@Beanpublic String helloWorld(){return "Hello World";}
}
还有一个getBeanProvider(ResolvableType)
这个方法我们后面再说,ResolvableType
这个类型主要是用来处理泛型的。等到后面我们介绍泛型的时候,我们再来详细的介绍。
集合类型依赖查找接口 - ListableBeanFactory
getBeanNamesForType(Class)
Spring 4.2 getBeanNamesForType(ResolvableType)
getBeansOfType(Class)
以及重载方法getBeanNamesForAnnotation(Class extends Annotation>)
getBeansWithAnnotation(Class extends Annotation>)
findAnnotationOnBean(String,Class extends Annotation>)
上面的API我前面都有或多或少的介绍过,我这儿不做赘述了,从上面的API我们可以得到一个结论就是ListableBeanFactory
主要用来查找集合的。而方式有两种一种是通过Bean的类型查找,一种是通过注解的类型去查找。集合的列表主要有两种情况,一种查询Bean的名称的集合,一种是查询Bean的实例的集合。
层次性依赖查找接口 - HierarchicalBeanFactory
将单一类型的查找和集合类型查找合并到一起去
containsLocalBean
方法实现BeanFactoryUtils#beanOfType
BeanFactoryUtils#beansOfTypeIncludingAncestors
BeanFactoryUtils#beanNamesForTypeIncludingAncestors
具体的代码如下:
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 <- ConfigurableListableBeanFactoryConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();System.out.println("当前BeanFactory 的 Parent BeanFactory:" + beanFactory.getParentBeanFactory());// 2.设置ParentBeanFactoryHierarchicalBeanFactory 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
有相应的实现,具体的的代码如下:
Bean 延迟依赖查找接口
具体的代码如下:
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 objectProvider = applicationContext.getBeanProvider(String.class);Iterable stringIterable = objectProvider;for (String s : stringIterable) {System.out.println(s);}objectProvider.stream().forEach(System.out::println);}private static void lookupByAvailable(AnnotationConfigApplicationContext applicationContext) {ObjectProvider userObjectProvider = applicationContext.getBeanProvider(User.class);User user = userObjectProvider.getIfAvailable(User::createUser);System.out.println("当前 User 对象:" + user);}@Bean@Primarypublic String helloWorld() {return "Hello World";}@Beanpublic String message() {return "Message";}
}
依赖查找安全性对比
注意: 层次性依赖查找的安全性取决于其扩展的单一或集合类型的 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 ObjectFactoryObjectFactory userObjectProvider = applicationContext.getBeanProvider(User.class);printBeansException("displayObjectFactoryGetObject", userObjectProvider::getObject);}private static void displayObjectProviderGetIfAvailable(AnnotationConfigApplicationContext applicationContext) {ObjectProvider 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 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();}}}
运行结果如下:
可以看出与上面的表格是一样的。
AbstractApplicationContext
内建可查找的依赖
注解驱动 Spring 应用上下文内建可查找的依赖( 部分)
注解驱动 Spring 应用上下文内建可查找的依赖( 续)
那么这些类在什么地方注册的呢?具体的如下:
后面在初始化的流程中,我们会详细的分析,上面的代码是AnnotationConfigUtils
类中的
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();}@Beanpublic String bean1() {return "bean1";}@Beanpublic 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 {@Overridepublic void afterPropertiesSet() throws Exception {throw new Exception("For purposes....");}}
}
ObjectFactory 与 BeanFactory 均提供依赖查找的能力。
不过 ObjectFactory 仅关注一个或一种类型的 Bean 依赖查找, 并且自身不具备依赖查找的能力, 能力则由 BeanFactory 输出。
BeanFactory 则提供了单一类型、 集合类型以及层次性等多种依赖查找方式。
BeanFactory.getBean 方法的执行是线程安全的, 操作过程中会增加互斥锁