不一样的视角来学习Spring源码之容器与Bean---上
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了不一样的视角来学习Spring源码之容器与Bean---上相关的知识,希望对你有一定的参考价值。
Spring源码学习一之容器与Bean---上
1) 容器接口
-
BeanFactory 接口,典型功能有:
- getBean
-
ApplicationContext 接口,是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能,如:
- 国际化
- 通配符方式获取一组 Resource 资源
- 整合 Environment 环境(能通过它获取各种来源的配置信息)
- 事件发布与监听,实现组件之间的解耦
BeanFactory 与 ApplicationContext 的区别
-
到底什么是 BeanFactory
- 它是 ApplicationContext 的父接口
- 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory
-
BeanFactory 能干点啥
- 表面上只有 getBean
- 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
- 下面例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean
BeanFactory只是一个接口,我们需要的是它的实现类,BeanFactory默认实现类为DefaultListableBeanFactory
下面我们尝试反射调用该成员变量:
@SpringBootApplication
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException
ConfigurableApplicationContext context = SpringApplication.run(OneMain.class, args);
//默认单例bean的注册中心
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//获取在beanFactory实例上singletonObjects属性集合
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
//对Bean名称进行过滤,不然会有很多的单例bean
map.entrySet().stream().filter(e->e.getKey().startsWith("dhy")).forEach(e->
System.out.println(e.getKey()+" = "+e.getValue());
);
3.ApplicationContext 比 BeanFactory 多点啥
- ApplicationContext 组合并扩展了 BeanFactory 的功能
- 国际化、通配符方式获取一组 Resource 资源、整合 Environment 环境、事件发布与监听
- 新学一种代码之间解耦途径,事件解耦
国际化
国际化文件均在 src/resources 目录下
messages.properties(空)
messages_en.properties
hi=Hello
messages_zh.properties
hi=你好
注意
- ApplicationContext 中 MessageSource bean 的名字固定为 messageSource
- 使用 SpringBoot 时,国际化文件名固定为 messages
- 空的 messages.properties 也必须存在
@SpringBootApplication
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException
GenericApplicationContext context=new GenericApplicationContext();
//自定义一个bean注册到容器中
context.registerBean("messageSource", MessageSource.class, () ->
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
ms.setDefaultEncoding("utf-8");
ms.setBasename("messages");
return ms;
);
context.refresh();
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
System.out.println(context.getMessage("hi", null, Locale.CHINESE));
资源获取
@SpringBootApplication
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException
GenericApplicationContext context=new GenericApplicationContext();
//寻找多个资源,底层使用ResourcePatternResolver
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (int i = 0; i < resources.length; i++)
System.out.println(resources[i]);
//查找单个资源--底层使用DefaultResourceLoader
Resource resource = context.getResource("classpath:messages.properties");
System.out.println(resource);
环境
@SpringBootApplication
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException
GenericApplicationContext context=new GenericApplicationContext();
String java_home = context.getEnvironment().getProperty("java_home");
System.out.println(java_home);
事件
自定义一个事件发布对象:
public class UEvent extends ApplicationEvent
public UEvent(Object source)
super(source);
发布事件:
@SpringBootApplication
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException
ConfigurableApplicationContext applicationContext = SpringApplication.run(OneMain.class, args);
//我们只要获取到了context对象,就可以在应用程序执行期间发布事件
applicationContext.publishEvent(new UEvent("你好,我是自定义事件"));
监听器:
@Component
public class UListener
@EventListener
public void listener(UEvent event)
System.out.println(event.getSource());
2) 容器实现
Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考
- DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转和依赖注入功能,都是它来实现
- ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
- FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
- XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
- AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
- AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
- AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
- AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)
另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来
DefaultListableBeanFactory
使用演示:
@SpringBootApplication
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
//手动构建一个BeanDefinition
AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
//注册到bean工厂中
beanFactory.registerBeanDefinition("config",beanDefinition);
//获取当前bean工厂中存在的bean的定义
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (int i = 0; i < beanDefinitionNames.length; i++)
System.out.println(beanDefinitionNames[i]);
@Configuration
class Config
@Bean
public Bean1 bean1()return new Bean1();
@Bean
public Bean2 bean2()return new Bean2();
BeanFactory自己为什么不能解析bean里面的@Bean和 @Configuration注解呢?
为什么只有config被注册到了容器中,config里面的@Bean注解并没有被解析,显然BeanFactory并没有解析这些注解的能力,那么这些能力是who提供的呢?
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
//手动构建一个BeanDefinition
AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
//注册到bean工厂中
beanFactory.registerBeanDefinition("config",beanDefinition);
//给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
//这一步只是往容器中注册了一些后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//这一步才是让这些后置处理器开始工作
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
.values().stream().forEach(
//执行bean工厂后置处理器
beanPostProcessor->beanPostProcessor.postProcessBeanFactory(beanFactory);
System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
);
//获取当前bean工厂中存在的bean的定义
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (int i = 0; i < beanDefinitionNames.length; i++)
System.out.println(beanDefinitionNames[i]);
@Configuration
static class Config
@Bean
public Bean1 bean1()return new Bean1();
@Bean
public Bean2 bean2()return new Bean2();
Spring的BeanFactory原始功能并没有那么丰富,但是可以通过后置处理器来增强BeanFactory的功能
BeanFactory自己为什么不能从bean定义中扫描到@Autowired注解,并进行自动依赖注入呢?
上面添加的bean工厂的后置处理器是用来解析一些例如@Configuration,@Bean等注解
我们还需要让Bean的后置处理器开始工作,Bean的后置处理器一般工作在Bean的生命周期过程中
public class Bean2
@Autowired
private Bean1 bean1;
public Bean2()
System.out.println("Bean2构造中...");
public Bean1 getBean1()
return bean1;
public class Bean1
public Bean1()
System.out.println("Bean1构造中...");
@SpringBootApplication
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
//手动构建一个BeanDefinition
AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
//注册到bean工厂中
beanFactory.registerBeanDefinition("config",beanDefinition);
//给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
//这一步只是往容器中注册了一些后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//这一步才是让这些后置处理器开始工作
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
.values().stream().forEach(
//执行bean工厂后置处理器
beanPostProcessor->beanPostProcessor.postProcessBeanFactory(beanFactory);
System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
);
//添加bean后置处理器---针对bean的生命周期各个阶段提供扩展
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//获取当前bean工厂中存在的bean的定义
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (int i = 0; i < beanDefinitionNames.length; i++)
System.out.println(beanDefinitionNames[i]);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(beanFactory.getBean(Bean2.class).getBean1());
@Configuration
static class Config
@Bean
public Bean1 bean1()return new Bean1();
@Bean
public Bean2 bean2()return new Bean2();
使用到bean才会去创建,这显然表明BeanFactory是懒加载的
BeanFactory设置预先实例化所有的Bean
@SpringBootApplication
public class OneMain
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
//手动构建一个BeanDefinition
AbstractBeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
//注册到bean工厂中
beanFactory.registerBeanDefinition("config",beanDefinition);
//给BeanFactory添加一些常用的后置处理器---相当于增加了解析这些注解的能力
//这一步只是往容器中注册了一些后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//这一步才是让这些后置处理器开始工作
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
.values().stream().forEach(
//执行bean工厂后置处理器
beanPostProcessor->beanPostProcessor.postProcessBeanFactory(beanFactory);
System.out.println("Bean工厂后置处理器有: "+beanPostProcessor);
);
//添加bean后置处理器---针对bean的生命周期各个阶段提供扩展
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::以上是关于不一样的视角来学习Spring源码之容器与Bean---上的主要内容,如果未能解决你的问题,请参考以下文章