Spring源码系列之——BeanFactory和ApplicationContext

目录

前言:

一、BeanFactory与ApplicationContext关系

二、BeanFactory

        1,什么是Beanfactory接口?

        2,为什么说Beanfactory是spring的核心容器?

         3,Beanfactory的内部方法

        4,BeanFactory的实现类——DefaultListableBeanFactory

三、ApplicationContext

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

文章中做了比较详细的解说,此处不多废话,欢迎大佬们的交流与指正~

        

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值