目录
一、BeanFactory与ApplicationContext关系
2,为什么说Beanfactory是spring的核心容器?
4,BeanFactory的实现类——DefaultListableBeanFactory
1,ApplicationContext之国际化——MessageSource
2,ApplicationContext之资源加载——ResourcePatternResolver
3,ApplicationContext之事件传播、事件发布——ApplicationEventPublisher
4,ApplicationContext之环境配置——EnvironmentCapable
前言:
Hello~大家好,只做技术交流分享~若大佬路过,请多指教~。该文章为spring源码系列文章,讲解的是beanFactory和ApplicationContext的区别,以及它们分别做了什么,娓娓道来~
一、BeanFactory与ApplicationContext关系
今天基于SpringBoot启动类讲解两者的关系,相信各位小伙伴对于该类也不陌生,最简单的启动类配置如下,可以看到类中有一个main()方法的入口,它是有返回值的——ConfigurableApplicationContext 类,简单来说返回的就是一个容器
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ManageApplication.class, args);
}
}
我们可通过Ctrl+Alt+U的快捷键查看该类的类图,如下:
可以看到图中的一个类与类之间的层级关系,最上级的接口,标注出来了,就是BeanFactory接口,图中我们也是可以看到ApplicationContext的,很明显是间接去继承了BeanFactory,其实这个图还表明了一个关系,就是ApplicationContext在BeanFactory基础上所作的功能拓展,上图的长线红框已标注,分别是EnvironmentCapable, MessageSource, ApplicationEventPublisher, ResourcePatternResolver,它们分别代表了什么功能,做了什么事,下面会进行详细讲解。
二、BeanFactory
1,什么是Beanfactory接口?
它是ApplicationContext的父接口,是spring的核心容器,它主要是通过ApplicationContext去实现以及组合它的功能,比如通过ConfigurableApplicationContext去获取一个Bean,比如获取“abc”
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(ManageApplication.class, args);
context.getBean("abc");
}
}
实际上这个getBean()的功能并不是由 ConfigurableApplicationContext去提供的,我们可以点进源码看一看:
这里可以看出,源码中先是调用getBeanFactory()得到beanFactory对象后再去进行getBean()的,所以这个方法实际是beanFactory提供的
2,为什么说Beanfactory是spring的核心容器?
接下来我们通过DEBUG模式进行调试,看一看启动类中run()方法返回的对象内容
将ConfigurableApplicationContext对象context进行DEBUG可以看出里面是包含了beanFactory的,在里面我们可以看到一个singletonObjects对象,那么它是干什么的呢?singleton表明的是单例的意思,Object当然就是对象了,那说明singletonObjects里面装的就是spring的单例bean,如下图所示:
3,Beanfactory的内部方法
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
从上述代码可以看出,BeanFactory是没有几个方法的,可能用的最多的就是getBean(),其他的containsBean()是否包含该bean啊,还是说isSingleton()是否单例,都是不怎么用得上的,看着没什么用,实际上spring的一个IOC(控制反转),DI(依赖注入),包括bean生命周期的一个管理,都是由它的一系列实现类去完成的
4,BeanFactory的实现类——DefaultListableBeanFactory
接下来说一说BeanFactory的一个实现类——DefaultListableBeanFactory,从图中可以看出BeanFactory只是其实现的一部分,还有其他非常多的一个类与接口的继承
当然我们这里主要是探究一下容器对象的装载,看一看它的继承父类DefaultSingletonBeanRegistry,进入到里面看源码
可以看到里面正正是前面提到的一个单例bean,它是一个map,由于是私有化的,所以我们看不见它里面是什么,我们可以通过反射的方式将它获取出来,或者说像我上面使用DEBUG模式,也是可以看到里面装载的是什么内容,我这里简单的写一下,有兴趣的小伙伴也可以自己去试试,建议是使用反射去进行获取它这个类中的;
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
ConfigurableApplicationContext context = SpringApplication.run(ManageApplication.class, args);
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Object obj = singletonObjects.get(beanFactory);
System.err.println(obj);
}
}
这里我也通过注解去扫描了两个类,我们看看能不能在singletonObjects里面将这两个类查找出来
如图选中的,就是上图两个类的对象,很清楚地说明了启动后初始化对象的存储位置,BeanFactory先说到这里,多余的不多说,上班为了下班,先下班!
三、ApplicationContext
ApplicationContext比BeanFactory多些啥?再次放出此图,长红线框出的四个接口,就是在BeanFactory基础上所作的一些扩展,接下来讲一讲分别是实现了什么,做了哪些事,能做什么
1,ApplicationContext之国际化——MessageSource
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args) throws Exception{
ConfigurableApplicationContext context = SpringApplication.run(ManageApplication.class, args);
/**
*@Param 1——"hi" : 需要翻译的参数
*@Param 2
*@Param 3 选择语言
*
*/
context.getMessage("参数1", null, Locale.CHINA);
}
}
国际化简而言之就是说国际通用的意思,那就是需要不同的语言进行翻译,如上getMessage()方法中,通过后面的语言对前面的资源进行一个翻译,当然这个是可以自定义的,在资源文件夹下创建,在此不多做演示,有兴趣的小伙伴可以自己操作一下
2,ApplicationContext之资源加载——ResourcePatternResolver
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args) throws Exception{
ConfigurableApplicationContext context = SpringApplication.run(ManageApplication.class, args);
Resource[] resources = context.getResources("classpath:application.yml");
for (Resource resource : resources) {
System.err.println("========"+resource);
}
}
}
很明显的直接上图说话,ResourcePatternResolver主做的是一个资源加载,获取之后,直接打印出该对象,可以看出是已经获取到了我们的一个配置文件application.yml,那我们仅仅能获取配置资源文件吗,当然不是的,在我们SpringBoot启动的时候,大家都知道会进行一个自动装配的过程,那我们装配之后的对象能否获取出来呢,可以试一试
注:不建议小伙伴在日常开发中在循环中码一条输出语句,数据量稍大一点是会出问题的,博主仅为测试
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args) throws Exception{
ConfigurableApplicationContext context = SpringApplication.run(ManageApplication.class, args);
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
System.err.println("========"+resource);
}
}
}
在上面一版的基础上对代码做了改造,路径换成了META-INF/spring.factories,自动配置的文件就在这里面,关系到SpringBoot的自动配置原理,目前博主未解说这一块,麻烦小伙伴们自己了解一下,我们的classpath作为类路径,是不能找到jar包下面的文件的,所以加上了一个*变成了classpath*:,接下来展示一下结果
3,ApplicationContext之事件传播、事件发布——ApplicationEventPublisher
ApplicationEventPublisher主要功能是做一个事件传播、事件发布的功能,在我们平时的开发业务中可能会用到,虽然不具备延迟的特性,但是用于某些场景解耦来说是足够的了,比如用户的消息通知,可能会使用不同的模板,比如邮箱,短信等等,我们可以去使用事件的方式去解决这样的业务,保证方法的一个解耦,避免高耦合,可能导致后期的一个代码维护难度加大,接下来简单的呈现一下事件发布的一个过程
首先需要创建一个类去继承ApplicationEvent的类,里面都是有注释的:
/**
* @Description 用户消息发送事件
* @Author xiaochen
* @Date 2022/7/27 14:27
*/
public class UserMessagePublishEvent extends ApplicationEvent {
/**
* 事件发布
* @param source 事件发布源
*/
public UserMessagePublishEvent(Object source) {
super(source);
}
}
再者事件发布后,我们需要一个类去监听此次的事件,以便于做出一些业务操作,这里就简单的写一下,直接使用前面的DemoTest02类去做监听了
@Configuration
public class DemoTest02 {
// 在这里使用demoTest02去进行一个事件监听
@EventListener
public void userMessageListener(UserMessagePublishEvent event){
System.out.println("用户消息事件监听接收===="+event);
}
}
然后我们来发布一个事件
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args){
ConfigurableApplicationContext context = SpringApplication.run(ManageApplication.class, args);
context.publishEvent(new UserMessagePublishEvent("用户消息发送事件发布"));
System.err.println("用户消息发送事件完成");
}
}
看一下结果是如何的,是怎么连通这个流程的,小伙伴们还是需要细细看一下,才能熟练运用于平时的一个业务开发当中。
4,ApplicationContext之环境配置——EnvironmentCapable
EnvironmentCapable的作用简单地说就是获取环境变量,由代码演示过程及结果:
@SpringBootApplication
public class ManageApplication {
public static void main(String[] args) throws Exception{
ConfigurableApplicationContext context = SpringApplication.run(ManageApplication.class, args);
ConfigurableEnvironment environment = context.getEnvironment();
// 获取系统环境变量
String java_home = environment.getProperty("JAVA_HOME");
// 获取项目配置环境变量
String name = environment.getProperty("spring.application.name");
System.out.println(java_home);
System.out.println(name);
}
}
结果如下:
四、总结
最后,对于本文做一个简单的总结,分享的东西不多,主要是BeanFactory和ApplicationContext两个的区别与联系:
联系:ApplicationContext是间接继承于BeanFactory的,BeanFactory是spring的一个核心容器,它主要是通过ApplicationContext去实现以及组合它的功能;
区别:ApplicationContext在BeanFactory的基础上做了哪些扩展——EnvironmentCapable, MessageSource, ApplicationEventPublisher, ResourcePatternResolver
文章中做了比较详细的解说,此处不多废话,欢迎大佬们的交流与指正~