Spring核心IOC应用与核心源码剖析 Spring设计思想

SSM(Spring SpringMVC Mybatis)的前身是SSH(Spring Struts Hibernate),Spring不仅没有被淘汰,还渐渐有了家族式的发展,spring全家桶(最主要的有脚手架框架springboot,微服务框架springcloud等等)
先自定义SpringIOC和AOP,再去看源码实现,最后源码分析
本次学习使用5.5.12版本,最新的版本往往不太稳定,5.0中jdk必须是8以上,本次学习用jdk11.0.5

ioc和aop不是spring提出的,在spring之前就存在,只不过更偏向于理论化,spring对这两个思想在java中做了非常好的实现

学习流程
1、spring的核心思想:控制反转IOC,面向切面编程AOP
2、手写IOC和AOP
3、IOC和AOP的应用和高级特性:AOP申明式事务
4、IOC源码分析:设计非常优雅
5、AOP源码分析

spring简介:具体看之前的spring博客
分层:
一站式:
轻量级:

以IOC和AOP为内核

常说的spring指的就是Spring Framework

官网地址 spring.io

spring的前身是EJB,2017年发布了spring5.0一直到现在

spring的优势:
1、方便解耦、简化开发
2、AOP处理非业务代码
3、声明式事务的支持
4、方便测试,注入即用
5、方便集成大部分优秀框架
6、降低javaeeapi的使用难度,对jdk进行一定的封装
7、源码是经典的Java学习范例

springFramework的核心结构
在这里插入图片描述

IOC

核心思想
什么是ioc?是一个控制反转的思想,描述的是主动去new出对象变成使用到对象的时候自动注入。
使我们丧失了创建、管理对象的权利,得到了不用考虑对象的创建管理等福利
控制反转:本来由开发者控制,现在变成ioc容器控制,要哪个对象,找ioc拿就是了
ioc解决了什么问题?
解决了对象耦合的问题,当实现类改变的时候,会需要修改很多个文件
在这里插入图片描述

ioc和DI的区别?
DI是依赖注入,是实现IOC控制反转思想的方式

AOP

什么是aop?aop是oop的延续,oop三大特征:封装()、继承(把重复的属性或者方法放到父类中)、多态(),oop是垂直纵向的继承思想,aop是水平横向的切面思想
aop解决了什么问题
解耦合和减少重复代码,把业务代码和非业务代码分开,让开发者只关心业务逻辑代码
什么叫做面向切面编程
使用场景:日志、权限校验、事务控制等

用传统方式创建对象,在两个对象有依赖的情况下,一旦需要修改被依赖对象的实现类,大项目需要修改N多的文件‘

如何动态实例化对象?
使用class.forName,把全限定名配置在xml中
使用工厂通过反射生产对象,工厂模式是解耦合非常好的方式

工厂模式:简单工厂(啥都生产)、工厂方法模式(创建多个工厂,每个工厂只生产一个专门的东西,比如兰州拉面)

1、传统存在的问题
2、通过工厂和xml解决

往对象中传值:1、构造方法 2、set

Connection默认是自动提交的,不同的connection是不同的事务

把connection绑定到线程上

在这里插入图片描述
要保证utils是单例的,这样多个线程使用的才是同一个threadLocal对象

写单例的第一步就是构造函数私有化,不让外部主动去实例化

因为一个service中不只调用一个dao,要保证每个dao中的connection是同一个,就得使用线程绑定connection

dao中抛出异常以便上级service捕获

事务管理器也用单例,一个就够了,不需要一直new,static不也行方法?

设计模式额外学

IOC实现注入的两种方式:注解、xml

注入容器的三种情况三种方式三种容器,如果又有xml又有注解也是使用xml方式启动
在这里插入图片描述
JavaSE应用:
xml:classpath-根据项目resources路径
file…-根据磁盘路径
注解:annotation
JavaWeb应用:
xml: context监听器加载xml
注解: context监听器加载注解配置类
在这里插入图片描述

BeanFactory和ApplicationContext的区别

ApplicationContext是BeanFactory子接口,所有具有他的所有功能,且有更多的功能
在这里插入图片描述

复制工程:工程名、坐标、name、iml文件

springioc配置文件不限制名称
xml定义了文件头,规定了xml中可以配置哪些信息

xmlns是xml的命名空间,命名空间不同,约束不同
对应模块
在这里插入图片描述
xmlns:xsi:xml的基础,必须要引入

命名空间具体的指向
在这里插入图片描述
xmlns:aop,这样对应的标签也要加aop
容器其实就是beanfactory?

使用ContextLoaderLinstener创建ioc容器
全局搜索ContextLoaderLinstener,因为ContextLoaderLinstener是在web包中的,所以要引入web包

手写ioc思路
1、首先创建两个类,A类包含B类,并且有B类的set方法
2、通过xml配置类信息,包括id、class、properties、ref
3、创建beanfactory,通过dom4j解析xml,根据反射机制通过class实例化对象,
并通过ref指向的id的class实例化B类,通过set方法赋值给A类,并且整个代码
使用静态代码块完成

手写aop思路
1、其实就是一个代理工厂,使用jdk或者cglib动态代理,

实例化bean的三种方式
1、无参构造器(推荐) 反射实例化
在这里插入图片描述

2、静态方法,静态工厂获取bean
在这里插入图片描述

3、实例化方法,先实例化工厂,再获取实例bean
在这里插入图片描述

属性赋值常用的两种方式
1、properties标签set方法
普通属性:
在这里插入图片描述
复杂属性:Stringp[]、map、set、properties
在这里插入图片描述

2、构造器注入:索引或者参数名
普通属性
在这里插入图片描述
在这里插入图片描述
复杂属性的赋值:

bean的作用范围及生命周期

scope设置作用范围
生命周期和作用范围有关

单例、原型、请求、会话、application、websocket

单例:随着容器的销毁而销毁,bean是单例的
原型:每次使用创建一个bean,但是不管销毁,由垃圾回收器决定

bean中可以定义初始化和销毁方法
name是别名,少用,用id就好

容器在线程结束前需要关闭

ioc和aop都是为了让开发者进一步解耦,把bean的管理权限交给了ioc管理
,aop是把非业务代理交给aop管理

WebApplicationContext是啥
web应用的时候使用的容器
这个工具类可以在servlect获取web容器
在这里插入图片描述

xml和注解的混合使用
第三方jar的bean使用xml配置,比如阿里的druid德鲁伊连接池
自己开发的bean使用注解配置

依赖注入的两种方式,依赖注入和注入容器是不一样的,注入容器有四种方式,三种分层的和一种通用的
@Autowired 按照类型注入,存在问题,当一个接口下有多个实现类都注入到容器的时候,就不知道要注入哪一个实现类了,
在这里插入图片描述

@Resource 按照名称注入
具体区别看有道云

@service这些相当于bean中的id,使用@Resource+id的方式把指定的类依赖注入进来,@Resource相当于ref

使用注解依赖注入之后set方法就可以不用使用了
问题:autowire按照类型注入是怎么做到的?

@Autowired 是spring提供的注解
@Resource是javax中的注解

纯注解形式开发
在这里插入图片描述
SE应用
在这里插入图片描述
EE应用(web)
在这里插入图片描述

可以用使用import关联其他配置类
在这里插入图片描述
IOC的高级特性
1、延迟加载(只针对单例):使用的时候创建还是启动容器的时候创建对象,默认容器启动的时候创建,非懒加载
单例池中观察
在这里插入图片描述
这里可以配置全局
在这里插入图片描述
常用的设置非延迟加载,不常用的用延迟
2、BeanFactory和FacoryBean
BeanFactory就是容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean。
FacoryBean是工厂Bean,通过FacoryBean可以生成某一类型的Bean实例
Sprng中有两种Bean,一种是普通的Bean,一个就是工厂bean
FacoryBean主要用来创建复杂的Bean,不然普通Bean直接配置就可以了

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
直接就获取到了具体Bean,而不是FactoryBean,Factory只生产同一类型的Bean
这样获取到的是工厂,就不是对象了
在这里插入图片描述

3、后置处理器
spring提供了两种后置处理器,一种工厂一种非工厂BeanPostProcessor 和BeanFactoryPostProcessor,

先有工厂再有Bean
在BeanFactory初始化之后可以使BeanFactoryPostProcessor进5后置处理做事情
在Bean对象实例化(并不是Bean的整个生命周期完成)之后可以使2BeanPostProcessor进后置处
理做些事情
注意:对象不一定是springbean,springbean定是个对象

获取beanid、创建bean的工厂、容器
在这里插入图片描述
在这里插入图片描述
Bean的生命周期
在这里插入图片描述
单例的Bean被放到Map集合中,多例的不归容器管理

在实例化对象之前,工厂还要读取配置文件,使用的是BeanDefinition类,封装Bean相关信息,每个Bean都会被封装成一个BeanDefinition,类似mybatis中是Mappersatement

工厂后置处理器经典应用:导入外部配置文件,使用占位符替换,比如jdbc,PropertyPlaceholderConfigurer类
在这里插入图片描述

在这里插入图片描述
读取配置文件,多个配置文件去重合并

源码分析

分析源码的好处:培养代码架构思维、深入理解框架
分析源码需要的
1、原则:

  • 定焦原则:抓住主线
  • 宏观原则:上帝视角看原则,关注流程接结构和业务流程,不要扣细节
    2、方法
  • 断点:观察调用栈
  • 反调:Find Usages,右键
  • 经验:比如doxxx的就是做具体处理的地方

1、通过class文件查看源码其实是idea反编译过后的,可能会出现一些问题
2、如果通过idea去下载源码是只读的,不能修改的
3、终极大招,官网下载源码,去github上找spring官方源码,

spring源码构建

  • 下载源码:github
  • 安装gradle5.6.3:类似maven、idea 2019.1 jdk 11.0.5
  • 导入源码,(编译顺序:core-oxm-context-beans-aspects-aop)
    �9�9程—>tasks—>other->compileTestJava

在这里插入图片描述
容器不只是map集合,准确来说map是一个单例池,是容器的一个成员singletonObjects,容器包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作

BeanFactor
在这里插入图片描述
MessageSource:国际化
ListableBeanFactory:批量处理、获取
HierarchicalBeanFactory:获取父工厂
ApplicationEventPublisher:事件
ResouceLoader:加载资源

Enablexx注解里面包含了import注解,把相关类注入到IOC中

面试题:

1、BeanNameAware与BeanFactoryAware区别?
2、@Service和@Componet的区别?
几乎都是一样的,@Service里面也是@Componet,用来区分的
3、BeanFactory和FactoryBean的区别?
4、BeanPostProcessor和BeanFactoryPostProcessor的区别?
5、BeanFactory和ApplicationContext的区别?
6、BeanFactory底层具体的实现类的哪个?容器的具体实现类的哪个?
DefaultListableBeanFactory
7、AOP代理对象什么时候被创建?
postProcessAfterInitialization
8、常用的几个容器和区别
xml
注解
file
web?
9、IOC容器初始化的主流程?
10、BeanFactory创建的流程?
11、BeanDifition创建的流程?
12、Bean实例化流程
13、延迟加载机制原理?
14、IOC循环依赖原理?
15、声明式事务和编程式事务的区别
16、事务的四大特性
17、事务的隔离级别
18、事务的传播行为
19、Bean的生命周期?
20、Bean的作用域?
默认只有单例和多利,其他是扩展的//TODO
21、Spring的优势,解决了什么问题
22、cglib效率不是比较高吗?为什么aop还要使用jdk动态代理在这里插入图片描述
23、为什么多例循环依赖的时候不会无限创建内存益溢出呢?
内部判断是多例循环依赖问题,就及时抛出异常了
24、为什么单例循环依赖可以解决多利没办法解决?如果非要呢?那就手动赋值
多利会一直创建对象
25、单例什么时候被创建
容器创建的时候下一步就是解析封装单例bean
26、创建Bean的源码方法doGetBean
SingletonObjects是单例池,保存完整单例对象(就是赋值好的对象),还有婴儿对象(也叫提前对象,就是还没赋值但是已经实例化的对象)
27、三级缓存是如何解决循环依赖问题的
一级缓存SingletonObjects:缓存完整对象
二级缓存early:缓存婴儿对象
三级缓存singletonFactories:也是缓存婴儿对象

doGetBean->getSingleton方法
1、判断单例池中是否有该对象,并且判断该对象有没有被标记为正在创建,没有则返回空
2、判断是否多例循环依赖
3、重载的getSingleton方法开始创建对象,此时会添加BeanName到一个表示正在创建的集合中,然后开始创建对象doCreateBean
4、先创建婴儿对象,判断是否允许循环依赖,且是否存在标记集合里面,后面才初始化
5、一级缓存中不存在的时候把婴儿对象放入三级缓存中,会先移除二级缓存中该对象
6、BService创建的时候发现依赖AService,但是三级缓存中AService已经存在婴儿对象了,直接把A的婴儿对象赋值给BService,这时候把B赋值给A,这样B指向A,A也指向B了。
7、二级缓存干啥用?
8、单例循环依赖为什么不能使用构造器注入呢?因为A要先实例化,但是A构造器中依赖了B,那么就得先实例化B,就矛盾了
基础文档

循环依赖的注入两种方式
1、构造器注入
2、set属性注入

无法解决循环依赖的情况
单例构造器注入
多例属性注入

1、三级缓存就是为了提前暴露对象,方便被依赖对象使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值