所谓的依赖查找,简单来说,依赖查找就是从容器中通过api的方式获取我们想要的或者想得到的资源,在Spring中,IOC指的是容器,容器中存储着Bean对象,而所谓的依赖查找就是使用名称或者类型从这个IOC容器中获得我们想要读取的Bean对象的过程。
大纲:
Spring IOC依赖查找
概述
现在在大多数情况下,我们提到依赖查找第一时间都会想到Spring的IOC,但是其实依赖查找的思想最开始并不是Spring提出来的,也并不仅仅是Spring独有的,在Java原生Api中java.beans
以及JNDI
中都提供了依赖查找的相关实现。
- • JavaBeans: java.beans.beancontext.BeanContext
- • JNDI: javax.naming.Context#lookup(javax.naming.Name)
依赖查找的方式
依赖查找一般分为三种情况:
- • 单一类型依赖查找
- • 所谓的单一类型查找就是通过名称或者类型返回一个单一的一个对象
- • 集合类型依赖查找
• 集合类型查找指的是通过名称或者类型返回的是一个集合,这个集合可以是Collection类型的,也可以map类型的
• 层次性依赖查找
• 如同双亲委派一样,IOC容器也是可以存在上下级的,而层次性依赖查找就是指查找可以在当前容器的上级或者上上级一次递归查到
Spring的依赖查找
定义好统一的注入实体:
public class User {
private String name;
private Integer age;
// 省略get、set和toString方法
}
// 继承于User
public class SupperUser extends User{
private String phone;
// 省略get、set和toString方法
}
单一类型查找
Spring中单一类型查找的核心接口是BeanFactory
,他也是IOC容器的最顶级接口,内部提供了一堆实时查找以及延迟查找单一类型的接口方法。
image-20230319132502392
简单实用方式:
- • xml配置:
<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="user" class="cn.phshi.entity.User">
<property name="name" value="张三"/>
<property name="age" value="33"/>
</bean>
<!-- 设置supperUser为primary,避免按照类型查找时报错 -->
<bean name="supperUser" class="cn.phshi.entity.SupperUser"
parent="user" scope="prototype" primary="true">
<property name="phone" value="13311111111"/>
</bean>
</beans>
- • Java代码:
public class DependencySingleLookupDemo {
public static void main(String[] args) {
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
findByName(beanFactory);
// 通过类型查找 上下文存在多个相同对象时候会报错
findByType(beanFactory);
}
private static void findByName(BeanFactory beanFactory) {
// 为什么要有通过名称查找的方式?为了支持之前的jdk没有泛型
User user = (User) beanFactory.getBean("user");
System.out.println("通过名称查询的结果方式1:" + user);
// 指明要强转的对象
user = beanFactory.getBean("user", User.class);
System.out.println("通过名称查询的结果方式2:" + user);
}
private static void findByType(BeanFactory beanFactory) {
// 查找出来的是设置了primary="true"的对象
User user = beanFactory.getBean(User.class);
System.out.println("通过类型查询的结果:" + user);
}
}
集合类型查找
Spring中集合类型查找的核心接口是 ListableBeanFactory
,他继承了BeanFactory,内部提供了一通过名称或者类型返回Map对象的接口方法。
image-20230319134321896
简单实用方式:
- • xml配置:
<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="user" class="cn.phshi.entity.User">
<property name="name" value="张三"/>
<property name="age" value="33"/>
</bean>
<!-- SupperUser对象被Supper注解标注 -->
<bean name="supperUser" class="cn.phshi.entity.SupperUser"
parent="user" scope="prototype" primary="true">
<property name="phone" value="13311111111"/>
</bean>
</beans>
- • Java代码:
- • 前置工作
// 声明修饰注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Supper {
String name() default "";
}
// 标注在SupperUser上
@Supper(name = "我是SupperUser")
public class SupperUser extends User{
private String phone;
// 省略get、set和toString方法
}
- • 查找方式
public class DependencyCollectionLookupDemo {
public static void main(String[] args) {
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
// 查找集合
findCollectionByType(beanFactory);
// 通过指定注解加载
findByAnnotationType(beanFactory);
// 查找指定Bean上面的注解
findAnnotationByBean(beanFactory);
}
private static void findCollectionByType(BeanFactory beanFactory) {
// ClassPathXmlApplicationContext实现了ListableBeanFactory接口,所以可以强转
if (beanFactory instanceof ListableBeanFactory) {
Map<String, User> users = ((ListableBeanFactory) beanFactory).getBeansOfType(User.class);
System.out.println("查询到的User对象为:" + users);
}
}
private static void findByAnnotationType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
Map<String, Object> users = ((ListableBeanFactory) beanFactory).getBeansWithAnnotation(Supper.class);
System.out.println("查询到被Supper修饰的对象为:" + users);
}
}
private static void findAnnotationByBean(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
Supper supper = ((ListableBeanFactory) beanFactory).findAnnotationOnBean("supperUser", Supper.class);
System.out.println("supperUser对象的注解标注的name值是:" + supper.name());
}
}
}
层次性查找
Spring中层次性类型查找的核心接口是 HierarchicalBeanFactory
,他也是继承了BeanFactory,他存在一个接口getParentBeanFactory
用来获得他的父级容器对象。一个HierarchicalBeanFactory
只能存在唯一一个直接上级BeanFactory
image-20230319142051278
简单实用方式:
- • Java对象:
public class DependencyHierarchicalLookupDemo {
// 注意: SupperUser对象是在ClassPathXmlApplicationContext中定义的
public static void main(String[] args) {
// 1、通过AnnotationConfigApplicationContext创建一个上下文对象
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(DependencyHierarchicalLookupDemo.class);
annotationConfigApplicationContext.refresh();
// 获得AnnotationConfigApplicationContext上下文中的BeanFactory对象,
// 其实真正的BeanFactory是内部包装的DefaultListableBeanFactory,改步骤就是获取真正的BeanFactory对象
ConfigurableListableBeanFactory beanFactory = annotationConfigApplicationContext.getBeanFactory();
// 2、通过xml配置创建一个上下文对象
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
// 3、设置 Parent BeanFactory
beanFactory.setParentBeanFactory(classPathXmlApplicationContext);
// 先从本身判断,打印结果:当前BeanFactory[org.springframework.beans.factory.support.DefaultListableBeanFactory]是否包含[supperUser]对象:false
containsLocalBean(beanFactory, "supperUser");
// 从父对象中判断,打印结果:当前BeanFactory[org.springframework.context.support.ClassPathXmlApplicationContext]是否包含[supperUser]对象:true
containsLocalBean((HierarchicalBeanFactory) beanFactory.getParentBeanFactory(), "supperUser");
annotationConfigApplicationContext.close();
}
private static void containsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
System.out.printf("当前BeanFactory[%s]是否包含[%s]对象:%s\n", beanFactory.getClass().getName(), beanName, beanFactory.containsLocalBean(beanName));
}
}
从上面的判断结果可以看出,beanFactory.containsLocalBean
方法只能获得当前对象本身是否包含指定的对象,想要判断上级beanFactory
是否包含必须先从当前通过getParentBeanFactory
获得父级的beanFactory
再进行判断,所以通常通过递归的方式去校验当前是否包含指定的Bean,不包含再从父对象中判断。
在BeanFactoryUtils
工具类中提供了个递归查找的方式:
public class DependencyHierarchicalLookupDemo {
// 注意: SupperUser对象是在ClassPathXmlApplicationContext中定义的
public static void main(String[] args) {
// 1、通过AnnotationConfigApplicationContext创建一个上下文对象
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(DependencyHierarchicalLookupDemo.class);
annotationConfigApplicationContext.refresh();
ConfigurableListableBeanFactory beanFactory = annotationConfigApplicationContext.getBeanFactory();
// 2、通过xml配置创建一个上下文对象
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
// 3、设置 Parent BeanFactory
beanFactory.setParentBeanFactory(classPathXmlApplicationContext);
// 通过BeanFactoryUtils中的beansOfTypeIncludingAncestors方法获取对象
Map<String, SupperUser> userMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, SupperUser.class);
System.out.println("得到的对象为:" + userMap);
annotationConfigApplicationContext.close();
}
}
我们再来看一下这个方法的实现,便是实用递归来进行层次性查找的:
image-20230319150431308
延迟依赖查找
Spring中延迟依赖查找的核心接口是 ObjectFactory
,他属于延迟依赖查找的顶级接口,但是通常情况下我们使用延迟依赖查找并不会直接使用ObjectFactory
,而是使用他的一个子接口ObjectProvider
,ObjectProvider
实现了ObjectFactory
的同时,还实现了Iterable
接口,支持返回集合,并支持Java8的函数式编程,提供安全返回的方式。在Spring内部,该方式使用广泛。
image-20230319151343892
简单实用方式:
- • xml配置:
<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="user" class="cn.phshi.entity.User">
<property name="name" value="张三"/>
<property name="age" value="33"/>
</bean>
<bean name="supperUser" class="cn.phshi.entity.SupperUser"
parent="user" scope="prototype" primary="true">
<property name="phone" value="13311111111"/>
</bean>
<!-- 延迟加载 -->
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName" value="user"/>
</bean>
</beans>
- • Java代码:
public class DependencyLazyLookupDemo {
public static void main(String[] args) {
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
// 通过ObjectFactory延迟加载
findInLazyByObjectFactory(beanFactory);
// 通过ObjectFactory延迟加载
findInLazyByObjectProvider(beanFactory);
// 安全的延迟加载
findInLazyBySafetyObjectProvider(beanFactory);
findInLazyByDefaultObjectProvider(beanFactory);
}
// 输出结果:通过getBean延迟加载出来的对象为:User{name='张三', age=33}
private static void findInLazyByObjectFactory(BeanFactory beanFactory) {
// ObjectFactory对象需要在xml中配置
ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
User user = objectFactory.getObject();
System.out.println("通过getBean延迟加载出来的对象为:" + user);
}
// 输出结果:User{name='张三', age=33}
// SupperUser{phnotallow='13311111111'} User{name='张三', age=33}
private static void findInLazyByObjectProvider(BeanFactory beanFactory) {
// ObjectFactory对象需要在xml中配置
ObjectProvider<User> objectFactory = beanFactory.getBeanProvider(User.class);
// 继承了Iterable,所以可以使用流
objectFactory.forEach(System.out::println);
}
// 输出结果:安全获取结果为:null
private static void findInLazyByDefaultObjectProvider(BeanFactory beanFactory) {
// 当前上下文中不存在String的Bean
ObjectProvider<String> provider = beanFactory.getBeanProvider(String.class);
String str = provider.getIfAvailable(() -> "这是默认值");
System.out.println("获取得到默认结果为:" + str);
}
// 输出结果:获取得到默认结果为:这是默认值
private static void findInLazyBySafetyObjectProvider(BeanFactory beanFactory) {
// 当前上下文中不存在String的Bean
ObjectProvider<String> provider = beanFactory.getBeanProvider(String.class);
String str = provider.getIfAvailable();
System.out.println("安全获取结果为:" + str);
}
}
Spring内建依赖
AbstractApplicationContext内建可查找的依赖
Bean名称 | 实例 | 描述 |
environment | Environment 对象 | 外部化配置以及 Profiles |
systemProperties | java.util.Properties 对象 | Java 系统属性 |
systemEnvironment | java.util.Map 对象 | 操作系统环境变量 |
messageSource | MessageSource 对象 | 国际化文案 |
lifecycleProcessor | LifecycleProcessor 对象 | Lifecycle Bean 处理器 |
applicationEventMulticaster | ApplicationEventMulticaster 对象 | Spring 事件广播器 |
查找方式:
public class DependencyBuiltInLookupDemo {
public static void main(String[] args) {
// 创建一个上下文对象
AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext();
beanFactory.register(DependencyBuiltInLookupDemo.class);
beanFactory.refresh();
System.out.println("Environment对象为:"+beanFactory.getBean("environment"));
System.out.println("java.util.Properties对象为:"+beanFactory.getBean("systemProperties"));
System.out.println("java.util.Map对象为:"+beanFactory.getBean("systemEnvironment"));
System.out.println("MessageSource对象为:"+beanFactory.getBean("messageSource"));
System.out.println("LifecycleProcessor对象为:"+beanFactory.getBean("lifecycleProcessor"));
System.out.println("ApplicationEventMulticaster对象为:"+beanFactory.getBean("applicationEventMulticaster"));
beanFactory.close();
}
}
注解驱动 Spring 应用上下文内建可查找依赖
该对应的所有Bean注册信息可以在AnnotationConfigUtils
对象中找到。
Bean名称 | 实例 | 描述 |
org.springframework.context.annotation. internalConfigurationAnnotationProcessor | ConfigurationClassPostProcessor 对象 | 处理 Spring 配置类 |
org.springframework.context.annotation. internalAutowiredAnnotationProcessor | AutowiredAnnotationBeanPostProce ssor 对象 | 处理 @Autowired 以及 @Value 注解 |
org.springframework.context.annotation. internalCommonAnnotationProcessor | CommonAnnotationBeanPostProce ssor 对象 | (条件激活)处理 JSR-250 注解, 如 @PostConstruct 等 |
org.springframework.context. event.internalEventListenerProcessor | EventListenerMethodProcessor 对象 | 处理标注 @EventListener 的 Spring 事件监听方法 |
org.springframework.context. event.internalEventListenerFactory | DefaultEventListenerFactory 对象 | @EventListener 事件监听方法适 配为 ApplicationListener |
org.springframework.context.annotation. internalPersistenceAnnotationProcessor | PersistenceAnnotationBeanPostProce ssor 对象 | (条件激活)处理 JPA 注解场景 |
查找方式:
public class DependencyBuiltInLookupDemo {
public static void main(String[] args) {
// 创建一个上下文对象
AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext();
beanFactory.register(DependencyBuiltInLookupDemo.class);
beanFactory.refresh();
System.out.println("ConfigurationClassPostProcessor对象为:"+beanFactory.getBean("org.springframework.context.annotation.internalConfigurationAnnotationProcessor"));
System.out.println("AutowiredAnnotationBeanPostProcessor对象为:"+beanFactory.getBean("org.springframework.context.annotation.internalAutowiredAnnotationProcessor"));
System.out.println("CommonAnnotationBeanPostProcessor对象为:"+beanFactory.getBean("org.springframework.context.annotation.internalCommonAnnotationProcessor"));
System.out.println("EventListenerMethodProcessor对象为:"+beanFactory.getBean("org.springframework.context.event.internalEventListenerProcessor"));
System.out.println("EventListenerMethodProcessor对象为:"+beanFactory.getBean("org.springframework.context.event.internalEventListenerFactory"));
// 需要加入orm等相关依赖对JPA进行支持才会有该对象
// System.out.println("PersistenceAnnotationBeanPostProcessor对象为:"+beanFactory.getBean("org.springframework.context.annotation.internalPersistenceAnnotationProcessor"));
beanFactory.close();
}
}
AnnotationConfigUtils
中对内建依赖注入的部分代码截图:
image-20230319161539276
依赖查找中的经典异常
Spring中所有依赖查找中可能会抛出的异常所有都是BeansException
子类,并且属于uncheck类型异常。
- • NoSuchBeanDefinitionException: 当查找 Bean 不存在于 IoC 容器 时
- • NoUniqueBeanDefinitionException: 类型依赖查找时,IoC 容器存在多 个 Bean 实例
- • BeanInstantiationException: 当 Bean 所对应的类型非具体类时
- • BeanCreationException: 当 Bean 初始化过程中
- • BeanDefinitionStoreException: 当
BeanDefinition
配置元信息非法时
参考文章
小马哥讲 Spring 核心编程思想