• 【探索Spring底层】容器接口


    【探索Spring底层】容器接口

    1.容器接口有哪些?

    在Spring中,容器接口有BeanFactory 接口和ApplicationContext 接口

    其继承关系如下(Ctrl + alt + u)

    在这里插入图片描述

    由这个继承关系图可以看出,ApplicationContext 接口是 BeanFactory 的子接口。它扩展了 BeanFactory 接口的功能

    而BeanFactory 才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能

    那么在哪里可以看见ApplicationContext 类呢?

    就在SpringBoot的启动类中可以看见

    @SpringBootApplication
    @MapperScan("com.lingnan.mapper")
    public class TestSpringbootApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(TestSpringbootApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    SpringApplication.run()方法的返回值,则是ApplicationContext 的子类ConfigurableApplicationContext,也就是Spring容器

    使用debug模式,可以清晰看见ApplicationContext 里面有BeanFactory ,并且BeanFactory 有很多单例

    在这里插入图片描述


    2. BeanFactory能干嘛?

    使用快捷键Ctrl + n查找BeanFactory,然后使用Ctrl + F12即可查看BeanFactory的方法

    在这里插入图片描述

    可以看到,BeanFactory接口的方法看起来并不多,只有getBean

    实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供


    3.ApplicationContext有哪些扩展功能?

    通过前面的ApplicationContext 的继承关系图,不难看出,ApplicationContext 比BeanFactory多继承了MessageSource、ResourcePatternResolver、EnvironmentCapable、ApplicationEventPublisher这几个接口

    这几个接口也就是ApplicationEventPublisher的扩展功能

    MessageSource接口是用来提供处理国际化资源的能力,可以识别多国语言,相当于翻译

    ResourcePatternResolver接口提供的是通配符匹配资源(磁盘或者类路径下的文件)的能力

    ApplicationEventPublisher接口则是可以用来发布事件对象

    EnvironmentCapable接口提供了读取Spring的一些环境信息的能力


    3.1 MessageSource

    MessageSource提供了getMessage方法,可以将语言翻译成不同国家的语言

    在这里插入图片描述

    System.out.println(context.getMessage("hi", null, Locale.CHINA));
    System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
    System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
    
    • 1
    • 2
    • 3

    这里就能将hi翻译成中文、英语、日语

    因为这个项目下提供了三个不同的文件,就会根据里面的键值对转化为对应的语言

    在这里插入图片描述


    3.2 ResourcePatternResolver

    ResourcePatternResolver提供了getResources方法,可以根据路径或者通配符获取多个资源

    public interface ResourcePatternResolver extends ResourceLoader {
    
    
    	String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
    
    	Resource[] getResources(String locationPattern) throws IOException;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当然根据ResourcePatternResolver的父接口ResourceLoader,可以发现也可以获取单个资源

    public interface ResourceLoader {
    
       String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
     
       Resource getResource(String location);
    e org.springframework.util.ClassUtils#forName(String, ClassLoader)
        */
       @Nullable
       ClassLoader getClassLoader();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    下面举个栗子

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

    classpath是指当前类路径下(在jar包的话就需要像上面这样加*),而Resource是Spring中对资源的抽象


    3.3 EnvironmentCapable

    EnvironmentCapable提供了getEnvironment方法,获取当前Spring的配置信息(环境变量、application.properties等)

    public interface EnvironmentCapable {
    
       /**
        * Return the {@link Environment} associated with this component.
        */
       Environment getEnvironment();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以借助Environment的父接口PropertyResolver提供的getProperty方法获取某个配置信息

    public interface Environment extends PropertyResolver {
    
       String[] getActiveProfiles();
    
       String[] getDefaultProfiles();
    
       @Deprecated
       boolean acceptsProfiles(String... profiles);
    
       boolean acceptsProfiles(Profiles profiles);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    举个栗子

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

    这样就能获取到环境变量和server.port端口号了


    3.4 ApplicationEventPublisher

    ApplicationEventPublisher的作用是用来发布事件的

    @FunctionalInterface
    public interface ApplicationEventPublisher {
    
       default void publishEvent(ApplicationEvent event) {
          publishEvent((Object) event);
       }
    
       void publishEvent(Object event);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ApplicationEventPublisher提供了一个publishEvent方法用来发布事件

    而事件则需要继承ApplicationEvent,source代表事件源,也就是谁发的事件

    public class UserRegisteredEvent extends ApplicationEvent {
        public UserRegisteredEvent(Object source) {
            super(source);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后就可以调用publishEvent来发送事件,参数为context,因为context是事件源,也就是context发的事件

    context.publishEvent(new UserRegisteredEvent(context));
    
    • 1

    除了发事件,还应该有收事件的东西,也就是监听器

    Spring中任何一个类都可以作为监听器

    需要加上 @EventListener注解

    然后参数为事件,也就是发的事件是什么类型,接收的事件也应该是什么类型

    @Component
    public class Component2 {
    
        private static final Logger log = LoggerFactory.getLogger(Component2.class);
    
        @EventListener
        public void aaa(UserRegisteredEvent event) {
            log.debug("{}", event);
            log.debug("发送短信");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样就可以了,其实这种操作很像mq,可以用来异步处理某些业务

    比如我这里有一个注册方法,然后异步需要发送短信,那么发布事件之后,aaa方法就会异步执行

    @Component
    public class Component1 {
    
        private static final Logger log = LoggerFactory.getLogger(Component1.class);
    
        @Autowired
        private ApplicationEventPublisher context;
    
        public void register() {
            log.debug("用户注册");
            context.publishEvent(new UserRegisteredEvent(this));
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

  • 相关阅读:
    心理咨询预约微信小程序开发制作步骤
    内核动态调试输出dev_info, dev_dbg
    C++——酒店管理系统
    TestStand-从LabVIEW创建TestStand数据类型的簇
    java毕业设计财务管理系统的设计与实现Mybatis+系统+数据库+调试部署
    MySQL的增删改查2
    2023年汉字小达人区级比赛倒计时2天,最新问题解答和真题练一练
    FastAdmin后台开发框架 lang 任意文件读取漏洞复现
    QT简介、安装与运行
    敏捷.敏捷项目管理第二版.Jim Highsmith
  • 原文地址:https://blog.csdn.net/weixin_51146329/article/details/126455555