一、Spring学习 : 容器---->BeanFactory+ApplicationContext

发布时间 2023-12-05 00:05:46作者: VayneBeSelf

BeanFactory 接口

是什么?

先来看我们的boot项目启动类的run方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

返回一个 ConfigurableApplicationContext 的spring 容器,那么通过名称我们想到这个run返回的Spring容器和ApplicationContext具有一定的关系

我们查看ConfigurableApplicationContext 的类图

可以看到ConfigurableApplicationContext 的父接口是ApplicationContext,而再向上看找到了BeanFactory 的顶级接口,说明ApplicationContext扩展了BeanFactory , 去实现了:

去实现了图中的四个接口。

那么看到这里,什么是BeanFactory
他是ApplicationContext的父接口,同时是Spring的核心容器,主要的ApplicationContext 实现都组合了BeanFactory的功能

能做什么?

我们查看 BeanFactory 的接口看到

接口中的表面功能其实并不多,但是我们的IOC、基本的DI、Bean的生命周期的功能,都在他的实现类DefaultListableBeanFactory 里面:

那么DefaultListableBeanFactory的父类中,有个DefaultSingletonBeanRegistry 实现了我们常说的单例对象的注册接口,那么我们看看他里面有哪些东西

//run方法返回applicationContext的可配置容器
        ConfigurableApplicationContext applicationContext =
                SpringApplication.run(SpringBootCloudStudyApplication.class, args);
        System.out.println(applicationContext);
        /**
         *  然后我们根据父子类关系看到了BeanFactory和ApplicationContext的关系,
         *  而其中BeanFactory的实现类DefaultListableBeanFactory中,有个父类DefaultSingletonBeanRegistry实现类单例注册接口
         *
         */
        //我们反射调用一下试下拿到DefaultSingletonBeanRegistry的所有单例对象
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
        //获取当前容器的所有单例对象
        Map<String, Object> beans  = (Map<String, Object>) singletonObjects.get(beanFactory);
        beans.forEach((k, v) -> {
            System.out.println(k + " : " + v);
        });

ApplicationContext 接口

我们刚刚其实看到了,ApplicationContext 相比于BeanFactory 多了四个接口实现,也就是BeanFactory不具备的:

那么这些接口用来干嘛呢?

MessageSource:具备处理国际语言的能力

ResourcePatternResolver : 具备根据通配符匹配资源的能力

ApplicationEventPublisher:具备发布事件对象的能力

EnvironmentCapable:可以读取项目的环境信息,比如我们的yml的项目配置文件之类的能力

我们来看下具体的功能实现:

//MessageSource:具备处理国际语言的能力
        System.out.println(applicationContext.getMessage("hi", null, Locale.CHINA));
        System.out.println(applicationContext.getMessage("hi", null, Locale.ENGLISH));
//ResourcePatternResolver : 具备根据通配符匹配资源的能力
//类路径
        Resource[] resources = applicationContext.getResources("classpath:application.properties");
        for (Resource resource : resources) {
            System.out.println(resource);
        }
    }

//ResourcePatternResolver : 具备根据通配符匹配资源的能力
//jar包中的资源文件
        Resource[] JarResources = applicationContext.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : JarResources) {
            System.out.println(resource);
        }
//EnvironmentCapable:可以读取项目的环境信息,比如我们的yml的项目配置文件之类的能力
        String javaHome = applicationContext.getEnvironment().getProperty("JAVA_HOME");
        String port = applicationContext.getEnvironment().getProperty("server.port");
        System.out.println(javaHome);
        System.out.println(port);
@Component
public class UserRegisteredListener {
    @EventListener
    public void onUserRegistered(UserRegisteredEvent event){
        System.out.println("onUserRegistered监听器监听到了" );
    }
}

public class UserRegisteredEvent extends ApplicationEvent {
    public UserRegisteredEvent(Object source) {
        super(source);
        System.out.println("触发了用户注册事件");
    }
}

//ApplicationEventPublisher:具备发布事件对象的能力
        //需要先定义一个事件继承于ApplicationEvent,然后就可以发布事件
        applicationContext.publishEvent(new UserRegisteredEvent(applicationContext));

其实这样也就实现了我们说的事件解耦

小结

BeanFactory和ApplicationContext之间并不是简单的接口继承关系,ApplicationContext组合并扩展了BeanFactory的功能

又学了一种代码之间的解耦关系