依赖查找是什么
使用过Spring框架的同学应该都清楚,Spring会将我们所需要使用到的Bean按照一定规则存储到一个应用上下文中(ApplicationContext)。所谓的依赖查找就是根据规则从Spring容器中获取对应的Bean。
流程有点类似于下图:
客户端程序希望获取一个BeanA,那么就向Spring中发起一个请求,从容器中获取对应的BeanA。
其实在Spring发明之前,jdk内部也有类似依赖查找的这种功能。下边我们来看看这几个类的属性。
java.beans.beancontext.BeanContext java.beans.beancontext.BeanContextServices 复制代码
在这两个类里面存有着和Spring设计类似的一些管理bean的基本接口和功能,这一点上可以看出,其实Spring的依赖查找设计是有对Jdk内部实现做了一定借鉴的。
依赖查找归纳
按照自己的经验和一些资料帮助,下边大体将依赖查找划分为了三种类型:
- 单一类型查找
- 集合类型查找
- 层次类型查找
接下来便是相关代码的实战案例,通过代码实践来深入理解这些依赖查找。
Spring内部的单一类型查找
关于spring内部的单一查找这里我将常见的几种类型给大家罗列出来。
Object getBean(String name) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansExceptio 复制代码
在高版本的Spring中还提供了对于Spring内部Bean的延迟查找功能
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType); <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); 复制代码
ResolvableType解释下:因为jdk内部的类不仅仅只有Class类型,还有例如说Type类型,FIeld类型,所以Spring又增加了一种类型参数作为暴露的Api服务。
实践案例:
配置Bean:Pig
package org.idea.spring.look.up.factory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Author linhao * @Date created in 9:26 下午 2021/4/10 */ @Configuration public class Config { public class Pig{ int id; public Pig() { System.out.println("this is pig"); } @Override public String toString() { return "Pig{" + "id=" + id + '}'; } } @Bean(name = "pig") public Pig getPig(){ return new Pig(); } } 复制代码
测试入口:
package org.idea.spring.look.up; import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; /** * @Author linhao * @Date created in 9:02 下午 2021/4/10 */ public class SpringSingleLookUpDemo { @Bean public String HelloWorld(){ //bean 注解里面的name或者value如果没有定义的话,这里默认就是 "helloWorld" System.out.println("THIS IS INIT"); return "Hello.World"; } public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext =new AnnotationConfigApplicationContext(); //此时相当于声明当前这个类是一个配置类,所以不需要进行额外注解的声明 applicationContext.register(SpringSingleLookUpDemo.class); applicationContext.refresh(); //单一查找的一种实现方式 String bean = (String) applicationContext.getBean("Pig"); System.out.println(bean); //spring 4.1 高版本中的一种实现方式 这里只是支持单一查找类型 ObjectProvider<Config.Pig> objectProvider = applicationContext.getBeanProvider(Config.Pig.class); System.out.println(objectProvider.getObject()); applicationContext.close(); } } 复制代码
Spring内部的集合类型查找
根据 Bean 类型查找
获取同类型 Bean 名称列表
String[] getBeanNamesForType(@Nullable Class<?> type); String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit); 复制代码
获取同类型 Bean 实例列表
getBeansOfType(Class) 以及重载方法 //不建议使用 通过注解类型查找 //Spring 3.0 - 获取标注类型 Bean 名称列表 String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType); //Spring 3.0 - 获取标注类型 Bean 实例列表 Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException; //Spring 3.0 - 获取指定名称+标注类型 Bean 实例 <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) throws NoSuchBeanDefinitionException; 复制代码
实验案例:
package org.idea.spring.look.up; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import java.util.Map; /** * @Author linhao * @Date created in 10:03 下午 2021/4/10 */ class Text { int id; public Text(int id) { System.out.println("init"); this.id = id; } public Text(){ System.out.println("no arg init"); } @Override public String toString() { return "Text{" + "id=" + id + '}'; } } class A { void doJob(){ System.out.println("do Job A"); } } class B extends A { @Override void doJob() { System.out.println("do Job B"); } } public class SpringListableLookUpDemo { @Bean(name = "text") public Text getText() { return new Text(1); } public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(SpringListableLookUpDemo.class); annotationConfigApplicationContext.register(B.class); annotationConfigApplicationContext.register(A.class); annotationConfigApplicationContext.refresh(); ListableBeanFactory listableBeanFactory = annotationConfigApplicationContext; listableBeanFactory.getBeansWithAnnotation(Component.class); Text text = (Text) listableBeanFactory.getBean("text"); Map<String, Text> map = listableBeanFactory.getBeansOfType(Text.class); System.out.println(map); //如果查找的是一个父类,会顺便将其子类也查询出来 String[] beanNameArr = listableBeanFactory.getBeanNamesForType(A.class); for (String beanName : beanNameArr) { System.out.println(beanName); } } } 复制代码
在这段代码程序中,有几个点需要注意一下:bean的依赖查找一般是需要结合BeanDefinition来使用,这一步的操作是发生在了Spring容器的上下文启动之前进行的。
对于判断某个bean是否存在,建议可以使用(一般是借助BeanDefinition这种关于类的元信息辅助类判断,此时可以避免提早实现bean的初始化)
例如:
listableBeanFactory.getBeanNamesForType(Text.class); 复制代码
而不是以下方法(可能会涉及到bean的提前初始化操作)
listableBeanFactory.getBeansOfType(Text.class); 复制代码
getBeanNamesForType其中的底层逻辑是从BeanDefinitionNames 集合中去遍历查询,并不会触发bean的初始化步骤;
getBeansOfType的底层内部可能会涉及到bean的初始化操作。