自学内容网 自学内容网

SpringBoot源码(1)ApplicationContext和BeanFactory

1、调用getBean方法

@SpringBootApplication
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
        applicationContext.getBean("1");
    }
}

先创建ConfigurableApplicationContext对象,然后调用getBean方法

ctrl+alt+u:显示类图

在这里插入图片描述

BeanFactory是ApplicationContext的父接口,BeanFactory是容器的核心接口,ApplicationContext是提供组合

在这里插入图片描述

getBean方法会先获取BeanFactory对象,然后调用BeanFactory对象的getBean方法,不是ApplicationContext直接调用的getBean方法

在这里插入图片描述

在这里插入图片描述

创建的BeanFactory对象是DefaultListableBeanFactory类的对象,BeanFactory对象是保存在GenericApplicationContext类中的

在这里插入图片描述

实际创建DefaultListableBeanFactory容器对象,DefaultListableBeanFactory类实现了ConfigurableListableBeanFactory接口,ConfigurableListableBeanFactory接口继承了ListableBeanFactory接口,ListableBeanFactory接口继承了BeanFactory接口

在这里插入图片描述

调用DefaultListableBeanFactory对象的getBean方法,实际调用的是AbstractBeanFactory抽象类中的getBean方法

结论:DefaultListableBeanFactory类非常重要!!!它提供了IOC控制反转、DI依赖注入、Bean的生命周期

2、获取BeanFactory对象中所有的单例对象

需求:输出singletonObjects容器中所有的单例对象

在这里插入图片描述

DefaultSingletonBeanRegistry类是DefaultListableBeanFactory类的父类,DefaultSingletonBeanRegistry类中包含singletonObjects是单例对象容器,它是一个ConcurrentHashMap,它是一级缓存

@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
        // 获取单例对象singletonObjects
        Class<DefaultSingletonBeanRegistry> cls = DefaultSingletonBeanRegistry.class;
        Field field = cls.getDeclaredField("singletonObjects");
        field.setAccessible(true);
        // field.get(对象实例),对象是BeanFactory对象,
        // application.getBeanFactory()方法返回的是ConfigurableListableBeanFactory接口,实际返回的是DefaultListableBeanFactory类
        // DefaultListableBeanFactory类是DefaultSingletonBeanRegistry的子类,因此DefaultListableBeanFactory对象可以获取到DefaultSingletonBeanRegistry的属性
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
        Map<String, Object> map = (Map<String, Object>) field.get(beanFactory);
        map.forEach((k, v) -> {
            log.info("k = {}, v = {}", k, v);
        });
    }
}

思路:利用反射获取容器中所有的单例对象,即属性.get(对象实例),singletonObjects容器在DefaultSingletonBeanRegistry类中,DefaultSingletonBeanRegistry对象如何获取呢?DefaultListableBeanFactory类是DefaultSingletonBeanRegistry类的子类,可以通过applicationContext.getBeanFactory()方法获取DefaultListableBeanFactory对象

3、ApplicationContext相比BeanFactory的扩展功能

ApplicationContext接口继承了BeanFactory接口,ApplicationContext对象封装了BeanFactory对象,并实现4种扩展功能

在这里插入图片描述

先点击类,然后点击F4可以跳转到指定类中

(1)MessageSource接口

在这里插入图片描述

MessageSource接口是用于国际化的,它包含getMessage方法可以选择指定code指定locale的语言值

在这里插入图片描述

其中,messages.properties是国际化的通用配置文件,它必须存在(可以不写内容)

message_zh.properties配置文件中填写menu.menuName.code=按钮

message_en.properties配置文件中填写menu.menuName.code=button

@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
        String message = applicationContext.getMessage("menu.menuName.code", null, Locale.CHINA);
        log.info("中文:{}", message);
        message = applicationContext.getMessage("menu.menuName.code", null, Locale.ENGLISH);
        log.info("英文:{}", message);
    }
}

由于ApplicationContext接口继承了MessageSource接口,因此可以调用MessageSource接口的getMessage方法

在这里插入图片描述

输出结果是国际化语言值中文和英文

解析浏览器请求头的语言信息是LocaleResolver

(2)ResourcePatternResolver接口

在这里插入图片描述

ResourcePatternResolver类用来查找资源文件

@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
        Resource[] resources = applicationContext.getResources("classpath:application.yml");
        Arrays.stream(resources).forEach(resource -> {
            log.info("resource: {}", resource);
            String filename = resource.getFilename();
            log.info("filename: {}", filename);
        });
        log.info("===========================================");
        resources = applicationContext.getResources("classpath*:META-INF/spring.factories");
        Arrays.stream(resources).forEach(resource -> {
            log.info("resource: {}", resource);
            String filename = resource.getFilename();
            log.info("filename: {}", filename);
        });
    }
}

寻找classpath类路径下的指定资源文件,比如找application.yml、META-INF/spring.factories

其中,classpath*:a.txt的*是查找jar包下的a.txt文件

在这里插入图片描述

共有3个地方有META_INF/spring.factories文件

(3)EnvironmentCapable接口

public interface EnvironmentCapable {

/**
 * Return the {@link Environment} associated with this component.
 */
Environment getEnvironment();

}

这个接口是获取环境变量的

@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String javaHome = environment.getProperty("java_home");
        log.info("Java home: " + javaHome);
        String serverPort = environment.getProperty("server.port");
        log.info("Server port: " + serverPort);
    }
}

调用getEnvironment方法和getProperty方法获取环境变量

在这里插入图片描述

结果是获取得到环境变量包括application.yml

(4)ApplicationEventPublisher接口

在这里插入图片描述

事件用来解耦的,ApplicationEventPublisher接口定义了publishEvent方法可以发布事件

public class UserLoginApplicationEvent extends ApplicationEvent {
    public UserLoginApplicationEvent(Object source) {
        super(source);
    }
}

1、定义事件:先定义事件UserLoginApplicationEvent,它要继承ApplicationEvent抽象类

@SpringBootApplication
@Log4j2
public class SpringBootDemoApplication {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
        applicationContext.publishEvent(new UserLoginApplicationEvent(applicationContext));
    }
}

2、发布事件:调用publishEvent方法,参数是创建一个UserLoginApplicationEvent事件,source是applicationContext,当然可以在一个对象中注入ApplicationEventPublisher或者ApplicationContext接口然后调用publishEvent方法发布事件

@Component
@Log4j2
public class UserLoginApplicationListener implements ApplicationListener<UserLoginApplicationEvent> {
    @Override
    public void onApplicationEvent(UserLoginApplicationEvent event) {
        log.info("UserLoginApplicationListener received event: " + event);
    }
}

3、监听事件:第1种方式是定义一个类接收事件,必须注入到容器中,监听器必须实现ApplicationListener接口,指定泛型是UserLoginApplicationEvent事件,重写onApplicationEvent方法

@Component
@Log4j2
public class UserLoginListener {

    @EventListener
    public void receivedEvent(UserLoginApplicationEvent event) {
        log.info("UserLoginListener received event: {}", event);
    }

}

3、监听事件:第2种方式是创建一个public void方法,入参是事件,方法用注解@EventListener来监听事件

在这里插入图片描述

测试结果是启动服务会发布事件,两个监听器都会收到事件并输出,并且实现ApplicationListener接口的方式比方法用注解@EventListener来监听事件的方式先执行


原文地址:https://blog.csdn.net/weixin_43823462/article/details/140590097

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!