java spring 理解
阅读原文时间:2023年07月08日阅读:1

1.spring IOC容器 其实就是 new 了一个 ApplicationContext 类对象。-》应用上下文对象。

2.应用上下文对象 实例化、配置,并管理 bean。

    所以上下文对象是 spring 的核心, 创建对象并把他们连接在一起,管理他们的生命周期。

--------------------

spring容器(应用上下文)通过我们提交的 POJO类 和 配置元数据(你懂得) 产生一个充分配置的(Fulled Configured)可使用的(ready for use)系统。

1.spring创建bean的几个手段(bean就是new好的对象)

--通过@Component, @Service注解的方式 (默认都是通过无参的构造函数来实例化对象)

--通过普通的XML方式(跟@Component注解一个道理)   《顺序:通过标签的class 属性得到一个class对象;然后通过class对象获取到对应的方法名称的Method对象;最后反射调用Method.invoke(null, args)》

--通过@Configuration注解的方式

--通过@Bean的方式

--通过静态工厂方法 ????? (因为是静态方法,方法在执行时,不需要一个对象)

--通过实例工厂方法的方式 ????

2.依赖注入的两种方式(实例化后的bean对象,注入才能成为spring bean。)

--构造函数注入

--Setter方法注入

通过@Autowire指定使用哪个构造方法(有参无参)或哪个setter方法注入;

  首先明确一点,直接添加@Autowire注解到字段上,不需要提供 setter方法 也能完成注入。(spring 会通过反射获取当前Class中的private定义的字段,然后通过反射包的方法,File.set(Class, Privatekey) 这种方式完成注入)

  构造函数注入和setter方法注入可以混用,

    对于一些强制依赖,最好使用构造函数注入,对于一些可选依赖,我们可以采用setter方法注入。

    spring 团队推荐使用 构造函数的方式注入,,但是对于一些构造参数过长的构造函数,spring是 不推荐的。

~方法注入vs依赖注入:

  为什么有了依赖注入,还需要有方法注入的方式呢,方法注入解决了什么问题?   (我也看不懂,不知道)

  -依赖注入:什么是依赖? 一个对象的依赖其实就是自身的属性。spring中的依赖注入其实就是属性注入。

    我们知道一个对象由两部分组成:属性 + 行为。 可以说spring通过属性注入 + 方法注入的方式掌控整个bean。

    属性注入和方法注入都是spring提供给我们用来处理bean之间协作关系的手段。

    属性注入有两种方式:构造函数注入和 setter方法注入。

    方法注入需要依赖动态代理完成。(不是很懂)

    方法注入对属性注入进行了一定程度上的补充,因为属性注入的情况下,原型可能会失去原型的意义(为什么要使用方法注入)

3.  精确注入vs自动注入vs方法注入 (依赖注入又叫‘精确注入’,对应的还有‘自动注入’)

  --自动注入是针对整个对象,或者一整批对象。比如我们如果将autoService这个bean的注入模型设置为byName,spring会为我们去寻找所有符合要求的名字(通过setter方法)bean并注入到autoService 中,

  而精确注入这种方式,是我们针对对象中的某个属性,比如我们在autoService中的dmzService这个属性字段上添加了@AutoWired注解,代表我们要精确的注入dmzService这个属性。

  而方法注入主要是基于方法对对象进行注入。

  --我们通常所说***byName***,***byType***跟我们在前文中提到的注入模型中的byName,byType 完全不一样。通常我们说的***byName***,***byType***是spring寻找bean的手段。比如,当我们注入模型为constructor时,Spring 会先通过名称找符合要求的bean,这种通过名称寻找对应的bean的方式,我们可以成为byName. 我们可以将一次注入分为两个阶段,首先是寻找符合要求的bean,其次再将符合要求的bean注入。

3.1   补充spring1.4小结的剩余部分

  -- depends-on:

    我们首先要知道,默认情况下,spring在实例化容器中的对象时是按名称进行自然排序进行实例化的。比如我们现在有A,B,C三个对象,那么spring在实例化时会按照A,B,C这样的顺序进行实例化。

    但是,在某些情况下我们可能需要B在A之前完成实例化。这个时候,我们就需要使用depends-on这个属性了。

  -- lazy:

    默认情况下,spring会在容器启动阶段完成所有bean的实例化,以及一系列的生命周期回调。

    但是,某些情况下我们可能需要让某一个bean延迟实例化。这种情况下,我们需要用到lazy属性。

4. BeanDefinition

  BD包含了我们对 bean 做的配置,,比如  XML 标签的形式进行的配置。

  BD是 spring 对 bean 抽象 后的实体。 它是 spring 创建 bean 的一个标准。

  BD 包含以下与数据:

    -- 一个全限定 类名, 通常来说, 就是对应的 bean 的 全限定类名。

    -- bean 的 行为 配置 元素,  这些  元素 展示了  这个 bean 在容器中 是 如何 工作的, 包括 scope, lifecycle, callbacks 等等。

    -- 这个bean的 依赖信息。

    -- 一些其他配置信息, 比如 我们 配置了一个连接池对象, 那么我们还会配置 其他的池子 大小, 最大连接数等。

  4.1 普通创建bean vs  BD创建bean

    普通: 磁盘上的两个.class文件 , 通过 ClassLoader 加载 成了 JVM 方法区中的两个 class 对象, 然后 通过 new 的方式 创建了 两个对象(ok);

    BD:  磁盘上的两个.class 文件, 通过 ClassLoader 加载 成了 JVM 方法区中的两个 class 对象,而 spring 对其管理得 bean 没有直接采用 new 的方式。 而是 先 通过 解析 配置数据 以及 根据对象 本身的一些定义 而 获取 其 对应的 beanDefinition, 并将这个beanDefinition 作为 之后 创建 这个 bean 的 依据。同时 spring 在这个过程中 提供了 一些 扩展点, 例如 我们在图中所 提到了 BeanFactoryProcessor.  (ok)

  4.2 总结:

    什么是BeanDefinition? 总结起来就是一句话,Spring 创建 bean 是 的 建模对象。

    BeanDefinition 具体使用的子类,以及 Spring 在 哪些地方使用到了 他们。 )(不懂)

  4.3 内容补充:

    单例:

      一个单例的bean 意味着, 这个bean 只会 容器 创建 一次。 在 创建后, 容器中的 每个地方 使用的 都是 同一个 bean 对象。

      

      @Component // 这里配置singleton,默认就是singleton @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public class LuBanService{ }

    原型:

      一个原型的bean 意味着, 每次我们使用时, 都会重新 创建 这个 bean。

      

      @Component // 这里配置prototype @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class LuBanService{ }

5. BeanDefinition

  5.1 什么是 合并?

    一个BD 包含了 很多 配置信息。包括构造函数的参数,  setter方法 的参数, 还有 容器一些特定的配置信息。 比如 初始方法, 静态方法等等。

    一个 子 BD 可以继承 父 BD的 配置信息, 不仅如此,还可以覆盖 其中的 一些值,或者 添加一些自己需要的属性。

    一个父 BD 可以作为 BD 定义的模板。

      简单的总结就是: 子 BD 会从 父 BD 继承没有的属性。子 BD 已经存在的属性不会被覆盖。

      关于 父 BD 中 abstract 属性的 说明: 并不是作为 父 BD 就一定要有 abstract 属性,abstract 只是 代表了 这个 BD 是否要被 Spring 进行 实例化,并 创建 对应的 bean, 如果为 true,代表容器不需要对其进行实例化。

                       如果一个 父 BD 没有 class 属性, 那么 必须要设置 其 abstract 为 true;

                       一般 作为 父 BD 都设置 abstract 为 true,因为我们只是要继承这个模板,而不是 实例化父 BD。 要是设置abstract 为 true, 你又不去继承他,他自己又不能实例化,那没啥用了,这个。

  5.2 Spring 在 哪些阶段做了 合并?

    -- Spring 扫描 获取 BD 时。

    -- Spring 在 实例化一个 BD 时 也会进行 合并。

         在扫描阶段之所以要合并,是因为 String 需要 拿到 指定了、实现了 BeanDefinitionRegistryPostProcessor 接口的 BD 的 名称。

          在实例化阶段, Spring 需要用到 BD 中的 一些列属性 做 判断, 所以进行了一系列合并,。

            总结起来就是一个原因: Spring 需要用到 BD 的属性, 要保证获取到的BD 的属性是正确的。

总结: BeanDefinition 这个类很重要, 是整个Spring 的基石。  之所以每次用到 BD 都要进行一次合并,是为了每次拿到都是 最新的,最有效的。 因为利用容器提供 的 一些扩展点 我们可以修改 BD 中的一些属性。

6. BeanDefinitionRegistryPostProcessor 的 作用。

    -- BeanFactoryPostProcessor 可以对 Bean 配置元数据 进行 操作。 也就是说, Spring 允许 BFPP 读取指定 bean 的 配置 元数据, 并可以在 bean 被实例化之前  修改它。

      这里说的 配置元数据 其实就是 我们之前所说的 BeanDefinition。

    -- 我们可以配置 多个 BFPP, 并且 只要 我们 配置的 BFPP 同时实现了 Ordered 接口的话,我么 还可以控制这些  BFPP 的 执行顺序。

  6.1. 

    -- Ordered 接口  用于 决定 执行顺序。

    --  PriorityOrdered  这个接口 直接 继承了 Ordered 接口,并没有做 任何 扩展。 只是作为 一个 标记 接口, 也 用于 决定 BFPP 的执行顺序。

    --  Aware  先不管。

    --  FunctionalInterface ,这是 Java 8 新增 的一个接口,也是只起标记作用,标记该接口是一个函数式接口。

    --  BeanFactoryPostProcessor 代表这个类是一个 Bean 工厂的后置处理器。

    --  PropertiesLoaderSupport, 这个类主要 包含 定义了 属性的 加载方法。包含属性如下:

          @Nullable

            Protected Properties []  localProperties;     // 本地属性,可以直接在XML 中配置。

          Protected boolean localOverirde = false;              //  是否本地的属性覆盖提供的文件中的属性, 默认不会。

          @NUllable

            Privated Resource []   locations;                   // 根据地址找到对应的文件。

            privated boolean  ignoreResourceNotFound = false;                                             //   没有找到文件是否抛出异常; false  不抛出;

          @Nullable

            private String fileEncoding;                         //  对应文件资源的编码

          private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();           //  文件解析器

    -- PropertyResourceConfigurer ,  这个类可以对读取到的一些 属性 进行 转换;

    --  PlaceholderConfigurerSupport   ,  主要负责对占位符进行 解析。其中的几个属性如下:

          public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";           //  默认解析的前缀

          public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";    //  默认解析的后缀

          public static final String DEFAULT_VALUE_SEPARATOR = ":";      // 属性名 跟 属性值 的分隔符

    -- PropertyPlaceholderConfigurer  继承了上面这些类的所有功能, 同时 可以配置属性的解析顺序。如:

          public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0;    // 不在系统属性中查找

          public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1;   // 如果没有在配置文件中找到,再去系统属性中查找

          public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2;   //  先查找系统中的属性,没有再去查找配置文件中的属性

总结: BFPP 执行的顺序总结如下:

    -- 先执行直接实现了 BeanFactoryRegestryPostProcessor 接口的后置 处理器, 所有实现了BeanDefinitionRegistryPostProcessor 接口的类有 两个 方法: 一个是 PostProcessBeanDefinitionRegistry 方法, 一个是继承父接口的 PostProcessBeanFactory 方法。

    -- PostProcessBeanDefinitoinRegistry 方法 早于 PostProcessBeanFactory 方法执行, 对于 PostProcessBeanDefinitionRegistry 的执行顺序 又 遵循 如下 原子:

      先执行 实现了 PriorityOrdered 接口的类中 的 PostProcessBeanDefinitionRegistry 方法;   再执行实现了 Ordered 接口的 类中 的 PostProcessBeanDefinitionRegistry 的 方法;    最后执行没有实现上面两个接口 的 类 中 的  PostProcessBeanDefinitionRegistry 方法;

    -- 执行 完成 所有 的 PostProcessBeanDefinitionRegistry 方法后, 再执行 实现了 BeanFactoryPostProcessor 接口的后置处理器:

      先执行实现了 PriorityOrdered 接口的类中的 PostProcessBeanFactory 方法;   再执行实现了 Ordered 接口中 的 PostProcessBeanFactory 方法;  最后 执行没有实现上面两个接口类中 PostProcessBeanFactory 的 方法。

7. FactoryBean:      (Spring 中的  一个 特殊的 bean,  Spring 利用它 提供 了 一个 创建 bean 的方式)

  -- FactoryBean  主要用来 定制化 Bean 的 创建 逻辑

  -- 当我们实例化一个Bean 的逻辑很复杂的 时候, 使用 FactoryBean 是很必要的, 这样 可以 规避我们 去使用 过长 的 XML 配置。

  -- FactoryBean 提供了以下 三个方法:

    Object getObject()   //  返回这个 FactoryBean 所创建的 对象。

    boolean  isSingleton()   // 返回 FactoryBean 所创建的对象是否为 单例。默认返回true;

    Class  GetObjectType()   // 返回这个 FactoryBean 所创建的 对象的 类型, 如果我们能确认返回的对象的 类型的 话,我们应该正常 对这个方法 做出实现,而不是 返回 null。

  7.1 Spring 自身大量使用了 FactoryBean 这个概念。至少有 50 个 BeanFactory 实现类 存在 于 Spring 容器中。  、、、;

    ;假设我们定义了一个 FactoryBaen,名为 MyfactoryBean, 当我们调用 getBean('MyFactoryBean‘’)  方法时,返回的并不是这个FactoryBean, 而是这个 FactoryBean 所创建的 Bean, 如果我们想要获取到 这个 FactoryBean, 需要在 名字前面 拼接 ‘&‘’’ 如:‘getBean("myFactoryBean")’;

  7.2 SmartFactoryBean (  继承了 FactoryBean  的 唯一子接口,并且默认这个接口的实例是 懒加载的,就是 Spring 在启动时, 不会立即将这个 bean 创建出来)

    为了让这个 bean 在 spring 启动的时候就创建 出来, 我们在实现 SmartFactoryBean 时, 需要重写 isEagerInit 方法,将返回值 设为 true;

                            我们也可以在 一个不是懒加载的 Bean 中 注入  这个 SmartFactoryBean 所创建的 bean, Spring 为了解决依赖关系,也会将这个 bean 创建 出来。

  7.3 BeanDefinition   vs     FactoryBean

    @Configuration public class Config { @Bean public B b(){ return new B(); } }

     我们通过 @Bean 的方式 来  创建一个Bean,  那么 在 B  的 BeanDefinition 会 记录  factoryBeanName 这个属性, 同时 还会记录 是 这个 Bean 中 哪个 方法 创建 B 的。如: factoryBeanName = config,   factoryMethodName = b;

  7.4  FactoryBean 相关的面试题:

    FactoryBean  和  BeanFactory  的 区别:     FB 是 Spring 提供的 一个 扩展点,适用于 复杂 的 bean 的 创建。 Mybatis 在 和 Spring 整合 时 就用 到了这个扩展点。 并且 FB 创建的 Bean 和 普通的 Bean 不一样。FB 是Spring 创建Bean 的另一种手段。