第一讲、Spring之容器接口

文章内容根据黑马程序员Spring视频教程所写!!!

视频地址:点击这里


一、BeanFactory与ApplicationContext的关系

public class A01Application {

    private static final Logger log = LoggerFactory.getLogger(A01Application.class);

    public static void main(String[] args) {
    	// 容器对象
        ConfigurableApplicationContext context = SpringApplication.run(A01Application.class, args);
        
        /**
         * 1.到底什么是BeanFactory
         *  - 它是ApplicationContext的父类
         *  - 它才是Spring的核心容器,主要的ApplicationContext的实现都【组合】了它的功能
         */
        context.getBean("aaa");
        System.out.println(context);
        
         /**
         * 2.BeanFactory能干点啥
         *  - 表面上只有getBean()
         *  - 实际上控制反转 基本的依赖注入 直至bean的声明周期的各种功能,都由它的实现类提供
         */
        // 通过反射获取字段
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        // 该字段是private的
        singletonObjects.setAccessible(true);
        // 获取bean工厂
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 得获取bean1工厂的单例bean
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        // 前提:手动创建了两个容器,component1和component2
        map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
                .forEach(e -> {
                    System.out.println(e.getKey() + "=" + e.getValue());
                });
         /**
         * 3. ApplicationContext比BeanFactory多点啥
         */
        // 处理国际化资源
        System.out.println(context.getMessage("hi", null, Locale.CHINA));
        System.out.println(context.getMessage("hi", null, Locale.US));
        // 通配符匹配资源
        Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources) {
            System.out.println(resource);
        }
        // 获取环境变量
        System.out.println(context.getEnvironment().getProperty("java_home"));
        System.out.println(context.getEnvironment().getProperty("server.port"));
        //发布事件
        context.publishEvent(new UserRegisteredEvent(context));
    }
}

通过查看ConfigurableApplicationContext的类图,发现ApplicationContext间接继承了BeanFactory。其中ApplicationContext直接继承的4个接口(图中黄色框框圈着的接口)组合了BeanFactory的功能,这四个接口的功能是我们经常使用的。

context.getBean(“aaa”)`,我们点进getBean()的源码(ctrl+alt+鼠标左键),发现该方法并不是ConfigurableApplicationContext提供,而是通过BeanFactory提供的。

通过调试,我们观察context的内容

context下有beanFactory,beanFactory下有singletonObjects(存储单例bean)

二、BeanFactory功能

DefaultListableBeanFactory

我们主要来看一下BeanFactory实现类DefaultListableBeanFactory,这是最重要的实现类,下图是DefaultListableBeanFactory的类图(ctrl+alt+u)

我们点击DefaultSingletonBeanRegistry,然后按f4,进入到DefaultSingletonBeanRegistry类

发现有一个对象singletonObjects,该对象存储的就是单例bean,bean名称与bean是映射关系。

Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
singletonObjects.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
        .forEach(e -> {
            System.out.println(e.getKey() + "=" + e.getValue());
        });

代码执行后,控制台的打印:

三、ApplicationContext功能

1、处理国际化资源

System.out.println(context.getMessage("hi", null, Locale.CHINA));
System.out.println(context.getMessage("hi", null, Locale.US));

2、通配符匹配资源

这里的资源指的是磁盘路径或类路径下的文件。如果是file:META-INF/spring.factories代表从磁盘路径下找;如果是classpath:META-INF/spring.factories代表从类路径下找;如果是classpath*:META-INF/spring.factories代表从类路径下找,但是包含jar包。

Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources) {
            System.out.println(resource);
        }

3、读取系统环境变量或者spring环境变量

这里获取环境是不区分大小写的

System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));

4、发布事件对象

context.publishEvent()是ApplicationEventPublisher接口的方法

context.publishEvent(new UserRegisteredEvent(context));

通过发布事件可以解耦合,

/**
 * @author Becant
 * 2024-04-24-19:35
 * @version 1.0.0
 * 事件发布类
 */
public class UserRegisteredEvent extends ApplicationEvent {
    
     /**
     * 发布事件
     * @param source 事件源,发布事件的对象
     */
    public UserRegisteredEvent(Object source) {
        super(source);
    }
}

/**
 * @author Becant
 * 2024-04-24-18:49
 * @version 1.0.0
 * 容器
 */
@Component
public class Component1 {

    private static final Logger log = LoggerFactory.getLogger(Component1.class);

    // 要接收某个事件,必须加上@EventListener,参数要与发布事件的参数一样
    @EventListener
    public void receive(UserRegisteredEvent event) {
        log.debug("{}", event);
    }

}

第一讲、Spring之容器接口
http://example.com/2024/04/24/第一讲、spring之容器接口/
发布于
2024年4月24日
更新于
2024年4月25日
许可协议