所谓依赖搜索,简单来说,依赖搜索就是通过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容器也可以有上下级,层次依赖搜索意味着搜索可以在当前容器的上级或上级进行递归
定义统一注入实体:
public class User { private String name; private Integer age; // 省略get、set和tosring方法}// 继承Userpublic 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) { // ClaspathXmlaplicationContext实现了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对象是在ClasspathXmlaplicationcontext中定义的 public static void main(String[] args) { // 1、通过Annnotationconfigaplicationcontext创建上下文对象 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(DependencyHierarchicalLookupDemo.class); annotationConfigApplicationContext.refresh(); // 在Annnotationconfigaplicationcontext上下文中获得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对象是在ClasspathXmlaplicationcontext中定义的 public static void main(String[] args) { // 1、通过Annnotationconfigaplicationcontext创建上下文对象 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); // 通过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的Beang ObjectProvider<String> provider = beanFactory.getBeanProvider(String.class); String str = provider.getIfAvailable(() -> “这是默认值”); System.out.println(“获得默认结果为:” + str); } // 输出结果:默认结果为:这是默认值 private static void findInLazyBySafetyObjectProvider(BeanFactory beanFactory) { // 目前上下文中没有String的Beang ObjectProvider<String> provider = beanFactory.getBeanProvider(String.class); String str = provider.getIfAvailable(); System.out.println(“安全获取结果为:” + str); }}
Spring内部建设依赖于Abstractaplicationcontext内部建设可以找到的依赖
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(“LifecycleProcesor对象为:”+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(“ConfigurationClaspostProcesor对象为:”+beanFactory.getBean("org.springframework.context.annotation.internalConfigurationAnnotationProcessor")); System.out.println(“AutowiredAnotationBeanPostProcesor对象为:”+beanFactory.getBean("org.springframework.context.annotation.internalAutowiredAnnotationProcessor")); System.out.println(“CommonannnotationBeanPostProcesor对象为:”+beanFactory.getBean("org.springframework.context.annotation.internalCommonAnnotationProcessor")); System.out.println(“EventlistenermethodProcesor对象为:”+beanFactory.getBean("org.springframework.context.event.internalEventListenerProcessor")); System.out.println(“EventlistenermethodProcesor对象为:”+beanFactory.getBean("org.springframework.context.event.internalEventListenerFactory")); // 需要加入orm等相关依赖来支持JPA // System.out.println(“PersistenceanotationBeanPostProcesor对象为:”+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
当配置元信息非法时