java spring 理解
1.spring IOC容器 其实就是 new 了一个 ApplicationContext 类对象。-》应用上下文对象。
2.应用上下文对象 实例化、配置,并管理 bean。
所以上下文对象是 spring 的核心, 创建对象并把他们连接在一起,管理他们的生命周期。
--------------------
spring容器(应用上下文)通过我们提交的 POJO类 和 配置元数据(你懂得) 产生一个充分配置的(Fulled Configured)可使用的(ready for use)系统。
1.spring创建bean的几个手段(bean就是new好的对象)
--通过@Component, @Service注解的方式 (默认都是通过无参的构造函数来实例化对象)
--通过普通的XML方式(跟@Component注解一个道理) 《顺序:通过<bean>标签的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 </bean> 标签的形式进行的配置。
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 对象。
<bean scope='singleton' />
@Component // 这里配置singleton,默认就是singleton @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public class LuBanService{ }
原型:
一个原型的bean 意味着, 每次我们使用时, 都会重新 创建 这个 bean。
<bean scope='ptototype' />
@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 的另一种手段。
java spring 理解的更多相关文章
- 从零开始学 Java - Spring 集成 ActiveMQ 配置(一)
你家小区下面有没有快递柜 近两年来,我们收取快递的方式好像变了,变得我们其实并不需要见到快递小哥也能拿到自己的快递了.对,我说的就是类似快递柜.菜鸟驿站这类的代收点的出现,把我们原来快递小哥必须拿着快 ...
- 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)
硬盘和内存的作用是什么 硬盘的作用毫无疑问我们大家都清楚,不就是用来存储数据文件的么?如照片.视频.各种文档或等等,肯定也有你喜欢的某位岛国老师的动作片,这个时候无论我们电脑是否关机重启它们永远在那里 ...
- 从零开始学 Java - Spring AOP 实现主从读写分离
深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...
- Java Spring IOC用法
Java Spring IOC用法 Spring IoC 在前两篇文章中,我们讲了java web环境搭建 和 java web项目搭建,现在看下spring ioc在java中的运用,开发工具为In ...
- Effective Java通俗理解(下)
Effective Java通俗理解(上) 第31条:用实例域代替序数 枚举类型有一个ordinal方法,它范围该常量的序数从0开始,不建议使用这个方法,因为这不能很好地对枚举进行维护,正确应该是利用 ...
- Java Spring Boot VS .NetCore (一)来一个简单的 Hello World
系列文章 Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filte ...
- Java Spring Boot VS .NetCore (二)实现一个过滤器Filter
Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...
- 从零开始学 Java - Spring 集成 Memcached 缓存配置(二)
Memcached 客户端选择 上一篇文章 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)中我们讲到这篇要谈客户端的选择,在 Java 中一般常用的有三个: Memc ...
- [java] 深入理解内部类: inner-classes
[java] 深入理解内部类: inner-classes // */ // ]]> [java] 深入理解内部类: inner-classes Table of Contents 1 简介 ...
- 从零开始学 Java - Spring 集成 ActiveMQ 配置(二)
从上一篇开始说起 上一篇从零开始学 Java - Spring 集成 ActiveMQ 配置(一)文章中讲了我关于消息队列的思考过程,现在这一篇会讲到 ActivMQ 与 Spring 框架的整合配置 ...
随机推荐
- day04-视图和视图解析器
视图和视图解析器 1.基本介绍 在SpringMVC中的目标方法,最终返回的都是一个视图(有各种视图) 注意,这里的视图是一个类对象,不是一个页面!! 返回的视图都会由一个视图解析器来处理(视图解析器 ...
- Android第六次作业
图片一 用内部存储实现文件写入和读取功能 <?xml version="1.0" encoding="utf-8"?> <LinearLayo ...
- HEU_KMS_Activator_v27.0.2全能系统数字许可激活工具
HEU_KMS_Activator_v27.0.2全能系统数字许可激活工具 HEU KMS Activator是KMS激活工具,支持激活 Windows XP.Windows 7.Windows 8. ...
- 解决 Vue3 中路由切换到其他页面再切换回来时 Echarts 图表不显示的问题
问题复现: 正常状态下: 切换到其他页面再切换回来: 问题解决: 其实这个问题的解决方式官网写得清清楚楚,我们看看官网怎么解决的: 接下来我用代码解释下这句话(正确的做法是,在图表容器被销毁之后,调用 ...
- STM32F0_HAL初始化系列:串口DMA输出
static void MX_USART1_UART_Init(void) { /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_ ...
- 华为eNSP 基础企业级WLAN配置
目标:STA可以扫描并连接到两个ssid,STA可以ping通ip:200.200.200.200 步骤:1 实现AP与AC之间的连通性. 2 在AC上配置与WLAN相关的各项参数 3 把AP ...
- C#:开发一个winform窗体程序,实现强势股票池的数据获取。(需对接第三方接口,目前可免费使用)
写在前面:短暂的接触过一段时间的股票市场,只能说A股真的太能杀了! 开发这个工具的初衷呢,是和几个好友在每日做"慈善"后,突发奇想:如果能实时获取当前股市里强势的股票就好了,因为这 ...
- sqllabs靶场less1-4
less1-4 语法:Select 列名称 from 表名称 (where column_name='xxx' and -) 在数据库中: information_schema:存放和数据库有关的东西 ...
- PostgreSQL 打印详细错误调用栈 - pg_backtrace
一.用法 create extension pg_backtrace; select pg_backtrace_init(); 二.示例 postgres=# select count(*)/0.0 ...
- js提示框触发和定时关闭
<!DOCTYPE html><meta charset="utf-8"> <script src="https://cdn.staticf ...