Spring系列二:基础篇(2)

本文涉及的产品
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: ​目录IOCIOC5.说一说什么是IOC?什么是DI?Java 是面向对象的编程语言,一个个实例对象相互合作组成了业务逻辑,原来,我们都是在代码里创建对象和对象的依赖。所谓的IOC(控制反转):就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。引入IOC之前和引入IOC之后也就是说,控制对象生命周期的不再是引用它的对象,而是容器。对具体对象,以前是它控制其它对象,现在所有对象都被容器控制,所以这就叫控制反转。控制反转示意图DI(依赖注入):指的是容器在实例化对象的时候把

今天我们来继续学习Spring系列基础篇的内容,快跟着叶秋学长一起学习吧~~

目录

IOC

5.说一说什么是IOC?什么是DI?

6.能简单说一下Spring IOC的实现机制吗?

7.说说BeanFactory和ApplicantContext?

8.你知道Spring容器启动阶段会干什么吗?


IOC

5.说一说什么是IOC?什么是DI?

Java 是面向对象的编程语言,一个个实例对象相互合作组成了业务逻辑,原来,我们都是在代码里创建对象和对象的依赖。

所谓的IOC(控制反转):就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。

image.gif编辑

引入IOC之前和引入IOC之后

也就是说,控制对象生命周期的不再是引用它的对象,而是容器。对具体对象,以前是它控制其它对象,现在所有对象都被容器控制,所以这就叫控制反转

image.gif编辑

控制反转示意图

DI(依赖注入):指的是容器在实例化对象的时候把它依赖的类注入给它。有的说法IOC和DI是一回事,有的说法是IOC是思想,DI是IOC的实现。

为什么要使用IOC呢?

最主要的是两个字解耦,硬编码会造成对象间的过度耦合,使用IOC之后,我们可以不用关心对象间的依赖,专心开发应用就行。

6.能简单说一下Spring IOC的实现机制吗?

PS:这道题老三在面试中被问到过,问法是“你有自己实现过简单的Spring吗?

Spring的IOC本质就是一个大工厂,我们想想一个工厂是怎么运行的呢?

image.gif编辑

工厂运行

    • 生产产品:一个工厂最核心的功能就是生产产品。在Spring里,不用Bean自己来实例化,而是交给Spring,应该怎么实现呢?——答案毫无疑问,反射
      那么这个厂子的生产管理是怎么做的?你应该也知道——工厂模式
    • 库存产品:工厂一般都是有库房的,用来库存产品,毕竟生产的产品不能立马就拉走。Spring我们都知道是一个容器,这个容器里存的就是对象,不能每次来取对象,都得现场来反射创建对象,得把创建出的对象存起来。
    • 订单处理:还有最重要的一点,工厂根据什么来提供产品呢?订单。这些订单可能五花八门,有线上签签的、有到工厂签的、还有工厂销售上门签的……最后经过处理,指导工厂的出货。
      在Spring里,也有这样的订单,它就是我们bean的定义和依赖关系,可以是xml形式,也可以是我们最熟悉的注解形式。

    我们简单地实现一个mini版的Spring IOC:

    image.gif编辑

    mini版本Spring IOC

    Bean定义:

    Bean通过一个配置文件定义,把它解析成一个类型。

      • beans.properties
        偷懒,这里直接用了最方便解析的properties,这里直接用一个<key,value>类型的配置来代表Bean的定义,其中key是beanName,value是class
      userDao:cn.fighter3.bean.UserDao
      • image.gif
      • BeanDefinition.java
        bean定义类,配置文件中bean定义对应的实体
      public class BeanDefinition {
          private String beanName;
          private Class beanClass;
           //省略getter、setter  
       }
      • image.gif
      • ResourceLoader.java
        资源加载器,用来完成配置文件中配置的加载
      public class ResourceLoader {
          public static Map<String, BeanDefinition> getResource() {
              Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);
              Properties properties = new Properties();
              try {
                  InputStream inputStream = ResourceLoader.class.getResourceAsStream("/beans.properties");
                  properties.load(inputStream);
                  Iterator<String> it = properties.stringPropertyNames().iterator();
                  while (it.hasNext()) {
                      String key = it.next();
                      String className = properties.getProperty(key);
                      BeanDefinition beanDefinition = new BeanDefinition();
                      beanDefinition.setBeanName(key);
                      Class clazz = Class.forName(className);
                      beanDefinition.setBeanClass(clazz);
                      beanDefinitionMap.put(key, beanDefinition);
                  }
                  inputStream.close();
              } catch (IOException | ClassNotFoundException e) {
                  e.printStackTrace();
              }
              return beanDefinitionMap;
          }
      }
      • image.gif
      • BeanRegister.java
        对象注册器,这里用于单例bean的缓存,我们大幅简化,默认所有bean都是单例的。可以看到所谓单例注册,也很简单,不过是往HashMap里存对象。
      public class BeanRegister {
          //单例Bean缓存
          private Map<String, Object> singletonMap = new HashMap<>(32);
          /**
           * 获取单例Bean
           *
           * @param beanName bean名称
           * @return
           */
          public Object getSingletonBean(String beanName) {
              return singletonMap.get(beanName);
          }
          /**
           * 注册单例bean
           *
           * @param beanName
           * @param bean
           */
          public void registerSingletonBean(String beanName, Object bean) {
              if (singletonMap.containsKey(beanName)) {
                  return;
              }
              singletonMap.put(beanName, bean);
          }
      }
      • image.gif
      • BeanFactory.javaimage.gif编辑BeanFactory
        • 对象工厂,我们最核心的一个类,在它初始化的时候,创建了bean注册器,完成了资源的加载。
        • 获取bean的时候,先从单例缓存中取,如果没有取到,就创建并注册一个bean
        public class BeanFactory {
            private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
            private BeanRegister beanRegister;
            public BeanFactory() {
                //创建bean注册器
                beanRegister = new BeanRegister();
                //加载资源
                this.beanDefinitionMap = new ResourceLoader().getResource();
            }
            /**
             * 获取bean
             *
             * @param beanName bean名称
             * @return
             */
            public Object getBean(String beanName) {
                //从bean缓存中取
                Object bean = beanRegister.getSingletonBean(beanName);
                if (bean != null) {
                    return bean;
                }
                //根据bean定义,创建bean
                return createBean(beanDefinitionMap.get(beanName));
            }
            /**
             * 创建Bean
             *
             * @param beanDefinition bean定义
             * @return
             */
            private Object createBean(BeanDefinition beanDefinition) {
                try {
                    Object bean = beanDefinition.getBeanClass().newInstance();
                    //缓存bean
                    beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
                    return bean;
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }
        • image.gif
          • 测试
            • UserDao.java
              我们的Bean类,很简单
            public class UserDao {
                public void queryUserInfo(){
                    System.out.println("A good man.");
                }
            }
            • image.gif
            • 单元测试
            public class ApiTest {
                @Test
                public void test_BeanFactory() {
                    //1.创建bean工厂(同时完成了加载资源、创建注册单例bean注册器的操作)
                    BeanFactory beanFactory = new BeanFactory();
                    //2.第一次获取bean(通过反射创建bean,缓存bean)
                    UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
                    userDao1.queryUserInfo();
                    //3.第二次获取bean(从缓存中获取bean)
                    UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");
                    userDao2.queryUserInfo();
                }
            }
            • image.gif
            • 运行结果
            A good man.
            A good man.
            • image.gif

              至此,我们一个乞丐+破船版的Spring就完成了,代码也比较完整,有条件的可以跑一下。

              PS:因为时间+篇幅的限制,这个demo比较简陋,没有面向接口、没有解耦、边界检查、异常处理……健壮性、扩展性都有很大的不足,感兴趣可以学习参考[15]。

              7.说说BeanFactory和ApplicantContext?

              可以这么形容,BeanFactory是Spring的“心脏”,ApplicantContext是完整的“身躯”。

              image.gif编辑

              BeanFactory和ApplicantContext的比喻

                • BeanFactory(Bean工厂)是Spring框架的基础设施,面向Spring本身。
                • ApplicantContext(应用上下文)建立在BeanFactoty基础上,面向使用Spring框架的开发者。

                BeanFactory 接口

                BeanFactory是类的通用工厂,可以创建并管理各种类的对象。

                Spring为BeanFactory提供了很多种实现,最常用的是XmlBeanFactory,但在Spring 3.2中已被废弃,建议使用XmlBeanDefinitionReader、DefaultListableBeanFactory。

                image.gif编辑

                Spring5 BeanFactory继承体系

                BeanFactory接口位于类结构树的顶端,它最主要的方法就是getBean(String var1),这个方法从容器中返回特定名称的Bean。

                BeanFactory的功能通过其它的接口得到了不断的扩展,比如AbstractAutowireCapableBeanFactory定义了将容器中的Bean按照某种规则(比如按名字匹配、按类型匹配等)进行自动装配的方法。

                这里看一个 XMLBeanFactory(已过期)  获取bean 的例子:

                public class HelloWorldApp{ 
                   public static void main(String[] args) { 
                      BeanFactory factory = new XmlBeanFactory (new ClassPathResource("beans.xml")); 
                      HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");    
                      obj.getMessage();    
                   }
                }

                image.gif

                ApplicationContext 接口

                ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。可以这么说,使用BeanFactory就是手动档,使用ApplicationContext就是自动档。

                image.gif编辑

                Spring5 ApplicationContext部分体系类图

                ApplicationContext 继承了HierachicalBeanFactory和ListableBeanFactory接口,在此基础上,还通过其他的接口扩展了BeanFactory的功能,包括:

                  • Bean instantiation/wiring
                  • Bean 的实例化/串联
                  • 自动的 BeanPostProcessor 注册
                  • 自动的 BeanFactoryPostProcessor 注册
                  • 方便的 MessageSource 访问(i18n)
                  • ApplicationEvent 的发布与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化

                  这是 ApplicationContext 的使用例子:

                  public class HelloWorldApp{ 
                     public static void main(String[] args) { 
                        ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); 
                        HelloWorld obj = (HelloWorld) context.getBean("helloWorld");    
                        obj.getMessage();    
                     }
                  }

                  image.gif

                  ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。

                  8.你知道Spring容器启动阶段会干什么吗?

                  PS:这道题老三面试被问到过

                  Spring的IOC容器工作的过程,其实可以划分为两个阶段:容器启动阶段Bean实例化阶段

                  其中容器启动阶段主要做的工作是加载和解析配置文件,保存到对应的Bean定义中。

                  image.gif编辑

                  容器启动和Bean实例化阶段

                  容器启动开始,首先会通过某种途径加载Congiguration MetaData,在大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的Congiguration MetaData进行解析和分析,并将分析后的信息组为相应的BeanDefinition。

                  image.gif编辑

                  xml配置信息映射注册过程

                  最后把这些保存了Bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动就完成了。

                  相关文章
                  |
                  XML Oracle Java
                  Spring boot——logback 基础使用篇(一)
                  Spring boot——logback 基础使用
                  Spring boot——logback 基础使用篇(一)
                  |
                  运维 负载均衡 安全
                  Spring Cloud Zuul 基础搭建
                  通过前几篇文章的介绍,我们了解了Spring Cloud Eureka 如何搭建注册中心,Spring Cloud Ribbon 如何做负载均衡,Spring Cloud Hystrix 断路器如何保护我们的服务,以防止雪崩效应的出现,Spring Cloud Feign进行声明式服务调用都有哪些应用,相比Ribbon和Hystrix都有哪些改善。可以说,以上几个组件都是搭建一套微服务架构所必须的。通过以上思路,能够梳理出下面这种基础架构:
                  147 0
                  Spring Cloud Zuul 基础搭建
                  |
                  XML 消息中间件 开发框架
                  后端开发必须知道的Spring框架基础模块大全
                  后端开发必须知道的Spring框架基础模块大全
                  190 0
                  后端开发必须知道的Spring框架基础模块大全
                  |
                  设计模式 XML 存储
                  Spring 基础容器 BeanFactory
                  什么是BeanFactory? Spring官网对BeanFactory的解释 BeanFactory API 为Spring的IoC功能提供了底层基础。它的特定契约主要用于Spring的其他部分以及相关第三方框架其他部分的集成,它的DefaultListableBeanFactory实现是更高级别GenericApplicationContext容器的一个委托。
                  147 0
                  Spring 基础容器 BeanFactory
                  |
                  前端开发 Java Spring
                  Spring Mvc基础篇 (请求路径和参数绑定)详情
                  该篇章主要介绍SpringMvc基础学习中的 请求路径 参数绑定 详情
                  Spring Mvc基础篇 (请求路径和参数绑定)详情
                  |
                  存储 Java 容器
                  |
                  XML 运维 负载均衡
                  |
                  设计模式 前端开发 Java
                  Spring系列一:Spring基础篇
                  有人说,“Java程序员都是Spring程序员”,小叶秋不太赞成这个观点,但是这也可以看出Spring在Java世界里举足轻重的作用。今天叶秋学长带领小伙伴们一起进入Spring学习体系,还等什么快跟着叶秋学长一起内卷起来~~ 基础 1.Spring是什么?特性?有哪些模块? 2.Spring有哪些模块呢? 3.Spring有哪些常用注解呢? 4.Spring 中应用了哪些设计模式呢?
                  161 0
                  Spring系列一:Spring基础篇
                  |
                  XML 前端开发 Java
                  spring学习23-基础组件
                  spring学习23-基础组件
                  121 0
                  |
                  存储 XML 分布式计算
                  [Java基础篇]Spring Boot整合MongoDB
                  [Java基础篇]Spring Boot整合MongoDB
                  308 0
                  [Java基础篇]Spring Boot整合MongoDB