• spring底层实现-1-BeanFactory与applicationContext的功能与实现


    spring容器的常见接口与功能

    BeanFactory与ApplicationContext的关系

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

     另一方面,BeanFactory是ApplicationContext一个成员变量

    BeanFactory的功能

     

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

    ApplicationContext 比 BeanFactory 多点啥

    applicationContext对beanfactory的功能拓展主要体现在四个扩展接口:MessageSource(国际化)、ResourcePatternResolver(通配符方式获取一组 Resource 资源 )、ApplicationEventPublisher(事件发布与监听,实现组件之间的解耦 )、EnvironmentCapable上(整合 Environment 环境(能通过它获取各种来源的配置信息))

    MessageSource(国际化)

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

    可以通过浏览器的请求头获取这个信息 ,根据用户的使用需求返回规定的语言

    ResourcePatternResolver

    通过通配符获取一系列资源。 

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

    EnvironmentCapable

    获取来自环境变量或者properties配置文件的配置信息。

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

    ApplicationEventPublisher

     用于发布或者监听事件,spring中任何一个组件都可以作为监听器

    1. //发布事件
    2. context.publishEvent(new UserRegisteredEvent(context));
    3. //监听事件
    4. @EventListener
    5. public void aaa(UserRegisteredEvent event) {
    6. log.debug("{}", event);
    7. log.debug("发送短信");
    8. }

    事件可以用于解耦功能,比如,对于一个注册功能,在注册成功之后可能需要发送短信、可能需要返回信息,可能两者都需要,不同的业务或者使用场景会有不一样的使用要求,这时候就不能在一个方法里面吧注册功能写死了,可以发事件:先获取事件发布器,然后发布事件

    1. @Autowired
    2. private ApplicationEventPublisher context;
    3. public void register() {
    4. log.debug("用户注册");
    5. context.publishEvent(new UserRegisteredEvent(this));
    6. }

    接着另一个业务监听到事件之后就去执行相应的业务,做到注册功能和后续功能的解耦合。

    1. @EventListener
    2. public void aaa(UserRegisteredEvent event) {
    3. log.debug("{}", event);
    4. log.debug("发送短信");
    5. }

    spring容器的实现

    BeanFactory的实现

    BeanFactory的实现之一:DefaultListableBeanFactory,这个实现获得的对象是一个bean容器,先创建一个实例对象得到spring核心容器

    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

     一开始创建好里面是没有任何的bean的,所以需要给这个BeanFactory添加一些bean的定义,不是直接添加对象,因为对象是BeanFactory帮你创建的,所以你需要告诉的是bean的信息,bean 的定义(class, scope, 初始化, 销毁),再有BeanFactory根据定义创建对象。

    例如,我们有如下的类和类的依赖引用关系

    1. @Configuration
    2. static class Config {
    3. @Bean
    4. public Bean1 bean1() {
    5. return new Bean1();
    6. }
    7. @Bean
    8. public Bean2 bean2() {
    9. return new Bean2();
    10. }
    11. }
    12. static class Bean1 {
    13. private static final Logger log = LoggerFactory.getLogger(Bean1.class);
    14. public Bean1() {
    15. log.debug("构造 Bean1()");
    16. }
    17. @Autowired
    18. private Bean2 bean2;
    19. public Bean2 getBean2() {
    20. return bean2;
    21. }
    22. }
    23. static class Bean2 {
    24. private static final Logger log = LoggerFactory.getLogger(Bean2.class);
    25. public Bean2() {
    26. log.debug("构造 Bean2()");
    27. }
    28. }
    29. }
    1. AbstractBeanDefinition beanDefinition =
    2. BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
    3. beanFactory.registerBeanDefinition("config", beanDefinition);

    这一段代码是创建了一个config类的bean,并且把这个bean放到了bean容器beanFactory中,通过打印bean容器发现此时容器里只有一个config对象的信息,并没有把bean1和bean2的信息加入到容器中,即没有解析config类中的注解,是因为解析注解并不是有BeanDefinition完成,需要另外一个工具类——BeanFactory后处理器,后处理器可以扩展beanfactory功能,例如解析注解

    先把常用的后处理器放入bean工厂,让bean工厂有这么些个bean,但是如果要用到这些后处理器还需要建立这些处理器与beanFactory的联系并执行。

    1. // 给 BeanFactory 添加一些常用的后处理器
    2. AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

    添加后处理器之后需要运行该后处理器:因为处理器是beanfactory对象,所以要先把这些对象获取了,然后再去执行

    1. // BeanFactory 后处理器主要功能,补充了一些 bean 定义
    2. beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
    3. beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
    4. });

    此时去打印beanFactory中的bean就可以发现里面是把bean1和bean2的信息也创建并加入进容器了。

    这时候根据getBean(Bean1.class).getBean2()会发现返回值是一个null,即Bean1类中的@Autowired注解失效 ,没有创建出bean2对象这个依赖注解还需要Bean后处理器来解决。

     

    1. // Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
    2. beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
    3. .sorted(beanFactory.getDependencyComparator())
    4. .forEach(beanPostProcessor -> {
    5. System.out.println(">>>>" + beanPostProcessor);
    6. beanFactory.addBeanPostProcessor(beanPostProcessor);
    7. });

    因为beanFactory一开始只是在容器中存放各个bean的信息,只有当getBean的时候才会去创建bean对象,而有时候我们为了性能考虑会像提前把bean对象创建好,这时候就可以用到下面这个方法

    beanFactory.preInstantiateSingletons(); // 准备好所有单例

     总结:beanFactory 不会做的事

    1. 不会主动调用 BeanFactory 后处理器

    2. 不会主动添加 Bean 后处理器

    3. 不会主动初始化单例

    4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }

    ApplicationContext的实现

     四个比较经典的实现类:

    ClassPathXmlApplicationContext

    到类路径下读取配置文件 

    1. // ⬇️较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
    2. private static void testClassPathXmlApplicationContext() {
    3. ClassPathXmlApplicationContext context =
    4. new ClassPathXmlApplicationContext("a02.xml");
    5. for (String name : context.getBeanDefinitionNames()) {
    6. System.out.println(name);
    7. }
    8. System.out.println(context.getBean(Bean2.class).getBean1());
    9. }

     

    FileSystemXmlApplicationContext

    1. // ⬇️基于磁盘路径下 xml 格式的配置文件来创建
    2. private static void testFileSystemXmlApplicationContext() {
    3. FileSystemXmlApplicationContext context =
    4. new FileSystemXmlApplicationContext(
    5. "src\\main\\resources\\a02.xml");
    6. for (String name : context.getBeanDefinitionNames()) {
    7. System.out.println(name);
    8. }
    9. System.out.println(context.getBean(Bean2.class).getBean1());
    10. }

     上面这两个类的实现原理是借助了DefaultListableBeanFactory,读取xml文件,把读取到的bean信息形成bean放入bean工厂里面

    1. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    2. System.out.println("读取之前...");
    3. for (String name : beanFactory.getBeanDefinitionNames()) {
    4. System.out.println(name);
    5. }
    6. System.out.println("读取之后...");
    7. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    8. reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\a02.xml"));
    9. for (String name : beanFactory.getBeanDefinitionNames()) {
    10. System.out.println(name);
    11. }

    AnnotationConfigApplicationContext

    基于注解配置的实现

    1. private static void testAnnotationConfigServletWebServerApplicationContext() {
    2. AnnotationConfigServletWebServerApplicationContext context =
    3. new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    4. for (String name : context.getBeanDefinitionNames()) {
    5. System.out.println(name);
    6. }
    7. }
    8. @Configuration
    9. static class Config {
    10. @Bean
    11. public Bean1 bean1() {
    12. return new Bean1();
    13. }
    14. @Bean
    15. public Bean2 bean2(Bean1 bean1) {
    16. Bean2 bean2 = new Bean2();
    17. bean2.setBean1(bean1);
    18. return bean2;
    19. }
    20. }

    AnnotationConfigServletWebServerApplicationContext

    较为经典的容器, 基于 java 配置类来创建, 用于 web 环境

    1. private static void testAnnotationConfigServletWebServerApplicationContext() {
    2. AnnotationConfigServletWebServerApplicationContext context =
    3. new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    4. for (String name : context.getBeanDefinitionNames()) {
    5. System.out.println(name);
    6. }
    7. }
    8. @Configuration
    9. static class WebConfig {
    10. @Bean
    11. public ServletWebServerFactory servletWebServerFactory(){
    12. return new TomcatServletWebServerFactory();
    13. }
    14. @Bean
    15. public DispatcherServlet dispatcherServlet() {
    16. return new DispatcherServlet();
    17. }
    18. @Bean
    19. public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
    20. return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    21. }
    22. @Bean("/hello")
    23. public Controller controller1() {
    24. return (request, response) -> {
    25. response.getWriter().print("hello");
    26. return null;
    27. };
    28. }
    29. }

  • 相关阅读:
    Tomcat相关概述和部署
    记一次失败的pip使用经历
    SQL sever中的触发器
    线性代数:向量、张量、矩阵和标量
    【论文阅读】SimGNN:A Neural Network Approach to Fast Graph Similarity Computation
    Euro-NCAP-HWA测试流程中文版V1.1(2023发布)
    一个手机ip从这个城市去到另一个城市多久会变
    RavenDB完全事务性的 NoSQL 文档数据库
    spring 中的路径匹配
    nginx(六十二)proxy模块(三)接收用户请求的包体
  • 原文地址:https://blog.csdn.net/PnJgHT/article/details/125608538