第一阶段-框架源码分析-Spring
2020-07-09 11:29:52 0 举报
AI智能生成
Spring的学习笔记
作者其他创作
大纲/内容
Spring
Spring的核心结构
数据处理模块
Spring的JDBC和DAO模块封装了大量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败而引起的问题。该模块由JDBC、Transactions、ORM、OXM和JMS等模块组成
Web模块
提供了SpringMVC框架给Web应用,还提供了多种构建和其他应用交互的远程调用方案。SpringMVC框架在Web层提升了应用的松耦合水平。
AOP(Aspect Oritend Programming) / Aspects模块
对面向切面编程提供了丰富的支持。这个模块是Spring应用系统中开发切面的基础,与DI一样,AOP可以帮助应用对象解耦
Core Container模块(Spring核心容器)
核心容器是Spring框架最核心的部分,他管理着Spring应用中bean的创建、配置和管理。在该模块中,包括了Spring bean工厂,它为Spring提供了DI的功能。基于bean工厂,我们还会发现有多重Spring应用上下文的实现。所有的Spring模块都构建于核心容器之上
Test模块
为了使得开发者能够很方便的进行测试,Spring提供了测试模块以致力于Spring应用的测试。通过该模块,Spring为使用Servlet、JNDI等编写单元测试提供了一系列的mock对象实现。
核心思想
IOC和AOP不是spring提出的,在spring之前就已经存在,只不过更偏向与理论化,spring在技术层次把这两个思想做了非常好的实现(java)
IoC
什么是IoC?
概念
IoC Inversion of Control(控制反转/反转控制),注意它是一个技术思想,不是一个技术实现。
描述的事情
java开发领域对象的创建、管理的问题
开发方式
传统开发方式
比如类A依赖于类B,往往会在类A中new一个B的对象
IoC思想下开发方式
我们不用自己去new对象了,而是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要是用哪个对象,就去问IoC容器要即可
为什么叫控制反转?
控制
指的是对象创建(实例化、管理)的权利
反转
控制权交给外部环境(Spring框架、IoC容器)
IoC图示
我们丧失了一个权利(创建、管理对象的权利),得到了一个福利(不用考虑对象的创建、管理等一系列事情)
IoC解决了什么问题?
IoC解决对象之间的耦合问题
如果不使用IoC,当new UserDaoImpl()需要换一个实现类时,需要每个new的地方都修改,维护成本高,耦合性太强。如果使用IoC配置在配置文件或者使用注解,则可以做到解耦合的作用
IoC和DI的区别
什么是DI?
概念:Dependancy Injection(依赖注入)
怎么理解?
IoC和DI描述的是同一件事情,只不过角度不一样
AOP
什么是AOP?(AOP的演变)
概念:AOP Aspect Oriented Programming 面向切面编程/面向方面编程
AOP是OOP的延续,从OOP说起
OOP三大特征:封装、继承、多态
OOP是一种垂直体系
从垂直体系中,可以看出OOP可以解决大多数的代码重复问题,但是有一些情况是处理不了的,比如在顶级父类Animal中的多个方法中相同位置出现了重复代码,OOP就解决不了
每个方法的重复代码无法抽取
横切逻辑代码有什么问题?
横切代码重复问题
横切逻辑代码合业务代码混杂在一起,代码臃肿,维护不方便
AOP出场解决,AOP另辟蹊径提出横向抽取机制,将横切逻辑代码合业务逻辑代码分开
如图
AOP在解决什么问题?
在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复
为什么叫做切面编程
切
指的是横切逻辑,原有业务逻辑代码不懂,只能操作横切逻辑代码,所以面向横切逻辑
面
横切逻辑代码往往要影响的是很多个方法,每一个方法都如同一个点,多个点构成面,此时则为面的概念
自定义IOC和AOP
问题分析
关于转账问题的问题分析
问题解决思路
问题一
实例化对象的方式除了new之外,还有什么技术? 反射(需要把类的全限定类名配置在xml中)
考虑使用设计模式中的工厂模式解耦合,另外项目中往往有很多对象需要实例化,那就在工厂中使用反射技术实例化对象,工厂模式很合适
问题二
1.实现事务控制,本质上还是使用Connection的事务来进行控制,但是在代码中,每个方法在调用的时候都会去重新获取一个Connection连接,而不同的连接,事务是不一致的,所以要保证整个请求下来的使用的是一个连接,保证处在一个事务中。 可以使用ThreadLocal来实现
2.实现事务后,可以发现事务处理和业务处理冗余在了一起,代码的扩展性很不好,可以使用AOP的横切思想,来使业务处理逻辑和事务处理逻辑进行分离,使用动态代理技术进行扩展
3.使用动态代理之后,可以结合配置IOC思想,把新创建的几个工具类来配置到配置文件中
Spring IOC高级应用
IOC基础和高级应用
IOC几种配置和启动方式的分析图
BeanFactory和ApplicationContext的区别
概念: BeanFactory是Spring框架中IoC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而
ApplicationContext是它的一个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能
的。
ApplicationContext是它的一个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能
的。
通常我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的高级接口,比
BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml,java配置类)等等
BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml,java配置类)等等
BeanFactory的类图
启动IoC容器的方式
Java环境下启动Ioc容器
ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
Web环境下启动IoC容器
从xml启动容器
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置Spring ioc容器的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-name>classpath:applicationContext.xml</param-name>
</context-param>
<!--使用监听器启动Spring ioc容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置Spring ioc容器的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-name>classpath:applicationContext.xml</param-name>
</context-param>
<!--使用监听器启动Spring ioc容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
Servlet中代码实现
从配置类启动容器
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置让contextloaderContext知道我们是使用的注解启动ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!-- 配置启动类的全限定类名-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.lvqz.spring.config.SpringConfig</param-name>
</context-param>
<!--使用监听器启动Spring ioc容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置让contextloaderContext知道我们是使用的注解启动ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!-- 配置启动类的全限定类名-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.lvqz.spring.config.SpringConfig</param-name>
</context-param>
<!--使用监听器启动Spring ioc容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
xml配置中基础知识
实例化Bean的三种方式
使用无参构造函数
在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建
失败。
失败。
使用静态方法构造
在实际开发中,我们使⽤的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创
建的过程 中会做很多额外的操作。此时会提供⼀个创建对象的⽅法,恰好这个⽅法是static修饰的
⽅法,即是此种情 况。
建的过程 中会做很多额外的操作。此时会提供⼀个创建对象的⽅法,恰好这个⽅法是static修饰的
⽅法,即是此种情 况。
使用实例方法构造
此种⽅式和上⾯静态⽅法创建其实类似,区别是⽤于获取对象的⽅法不再是static修饰的了,⽽是
类中的⼀ 个普通⽅法。此种⽅式⽐静态⽅法创建的使⽤⼏率要⾼⼀些。
在早期开发的项⽬中,⼯⼚类中的⽅法有可能是静态的,也有可能是⾮静态⽅法,当是⾮静态⽅法
时,即可 采⽤下⾯的配置⽅式:
类中的⼀ 个普通⽅法。此种⽅式⽐静态⽅法创建的使⽤⼏率要⾼⼀些。
在早期开发的项⽬中,⼯⼚类中的⽅法有可能是静态的,也有可能是⾮静态⽅法,当是⾮静态⽅法
时,即可 采⽤下⾯的配置⽅式:
生命周期
singleton:单例模式
对象出生:当创建容器时,对象就被创建了
对象或者:只要容器在,对象一直活着
对象死亡:当销毁容器时,对象就被销毁了。
一句话总结:单例模式的bean对象生命周期与容器相同。
prototype:多例模式
对象出生:当适用对象时,创建新的对象实例
对象活着:只要对象在使用中,就一直活着
对象死亡:当对象长时间不用时,被java的垃圾回收器回收了
一句话总结:多例模式的bean对象,spring框架值负责创建爱你,不负责销毁。
自己不大使用的几个bean属性
factory-bean属性:用于指定创建当前bean对象的工厂bean的唯一标识。当指定了此属性之后,class属性失效。
factory-method属性:用于指定创建当前bean对象的工厂方法,如配合factory-bean属性使用,则class属性失效。如配合class属性使用,则方法必须是static的。
init-method:用于指定bean对象的初始化方法,此方法会在bean对象装配后调用。必须是一个无参方法。
destory-method:用于指定bean对象的销毁方法,此方法会在bean对象销毁前执行。它只能为scope是singleton时起作用。
DI依赖注入配置
按照注入的方式分类
构造函数注入
顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
set方法注入
它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)
按照注入的数据类型分类
基本类型和String
其他Bean类型
复杂类型(集合类型)
Array,List,Set,Map,Properties中的一种类型
List结构的集合
array
list
set
Map结构的集合
map
properties
SpringIOC的高级特性
lazy-init延迟加载
概念:ApplicationContext 容器的默认⾏为是在启动服务器时将所有 singleton bean 提前进行实例化。提前实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。
属性使用和设置:lazy-init="false",立即加载,表示在spring启动时,立刻进行实例化。
如果不想让某个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean设置为延迟实例化。
如果不想让某个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean设置为延迟实例化。
当一个立即加载的bean依赖了一个延迟加载的bean时:
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,而是第一次向容器通过 getBean 索取 bean 时实例化的。
如果一个设置了立即加载的 bean1,引用了一个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,而 bean2 由于被 bean1 引用,所以也被实例化,这种情况也符合延时加载的 bean 在第一次调用时才被实例化的规则。
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,而是第一次向容器通过 getBean 索取 bean 时实例化的。
如果一个设置了立即加载的 bean1,引用了一个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,而 bean2 由于被 bean1 引用,所以也被实例化,这种情况也符合延时加载的 bean 在第一次调用时才被实例化的规则。
如果一个 bean 的 scope 属性为 scope="pototype" 时,即使设置了 lazy-init="false",容器启动时也不会实例化bean,而是调用 getBean()方法实例化的。
FactoryBean和BeanFactory
BeanFactory:BeanFactory接口是容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean的一个工厂,具体使用它下面的子接口类型,例如ApplicationContext;此处我们重点分析FactoryBean
Factory:
Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean),FactoryBean可以生成某一个类型的Bean实例(返回给我们),也就是说我们可以借助于它自定义Bean的创建过程。
Bean创建的三种方式中的静态方法和实例化方法和FactoryBean作用类似,FactoryBean使用较多,尤其在Spring框架一些组件中会使用,还有其他框架和Spring框架整合时使用
Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean),FactoryBean可以生成某一个类型的Bean实例(返回给我们),也就是说我们可以借助于它自定义Bean的创建过程。
Bean创建的三种方式中的静态方法和实例化方法和FactoryBean作用类似,FactoryBean使用较多,尤其在Spring框架一些组件中会使用,还有其他框架和Spring框架整合时使用
在配置中,想要获取Factory下返回的对象,getBean(工厂Id);
想要或者工厂对象 getBean(&工厂Id);
想要或者工厂对象 getBean(&工厂Id);
后置处理器
BeanPostProcessor
使用场景:在Bean对象实例化(并不是Bean的整个生命周期完成)之后可以使用BeanPostProcessor进行后置处理做一些事情
实现BeanPostProcessor后要实现的两个方法
postProcessBeforeInitialization
postProcessAfterInitialization
BeanFactoryPostProcessor
使用场景:
工厂初始化(BeanFactory) ->Bean对象
在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进行后置处理做一些事情
工厂初始化(BeanFactory) ->Bean对象
在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进行后置处理做一些事情
BeanFactory级别的处理,是针对整个Bean的工厂进行处理,典型应
用:PropertyPlaceholderConfigurer
用:PropertyPlaceholderConfigurer
此接口只提供了一个方法,方法参数为ConfigurableListableBeanFactory,该参数类型定义了一些方法
其中有个方法名为getBeanDefinition的⽅法,我们可以根据此方法,找到我们定义bean 的BeanDefinition对象。然后我们可以对定义的属性进行修改,以下是BeanDefinition中的方法
BeanDefinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为⼀个 JavaBean,这个JavaBean 就是 BeanDefinition
注意:调用BeanFactoryPostProcessor方法时,这时候bean还没有实例化,此时 bean 刚被解析成BeanDefinition对象
关于SpringBean的生命周期的一些其他接口
BeanNameAware
BeanFactoryAware
ApplicationContextAware
InitlalizingBean
init-method
在初始化方法和销毁方法中的执行顺序:注解>接口>配置
例如:初始化方法 @PostContruct>InitializingBean>配置属性init-method
例如:初始化方法 @PostContruct>InitializingBean>配置属性init-method
Spring IOC源码剖析
IOC容器初始化主体流程
BeanFactory创建流程
Bean创建流程
lazy-init延迟加载机制原理
Spring IOC循环依赖问题和原理
Spring AOP应用
SpringAOP的一些基础术语
Joinpoint(连接点)
它指的是那些可以用于把增强代码加⼊到业务主线中的点,那么由上图中我们可以看出,这些点指的就是方法。在方法执行的前后通过动态代理技术加入增强的代码。在Spring框架AOP思想的技术实现中,也只支持方法类型的连接点。
Pointcut(切入点)
它指的是那些已经把增强代码加⼊到业务主线进来之后的连接点。由上图中,我们看出表现层 transfer 方法就只是连接点,因为判断访问权限的功能并没有对其增强。
连接点不一定是切入点,但是切入点一定是连接点
Advice(通知/增强)
它指的是切面类中用于提供增强功能的⽅法。并且不同的方法增强的时机是不一样的。例如,开启事务肯定要在业务方法执行之前执行;提交事务要在业务方法正常执行之后执行,而回滚事务要在业务方法执行产生异常之后执行等等。那么这些就是通知的类型。其分类有:前置通知 后置通知 异常通知 最终通知 环绕通知。
Introduction(引介)
Target(目标对象)
代理的目标对象。
Weaving(织入)
是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理)
一个类被 AOP 织入增强后,就产生一个结果代理类。在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
Aspect(切面)
它指定是增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面类。
Spring中Aop的几种实现方式和应用
使用XML
关于切入点表达式
改变代理方式的配置
使用XML和注解混合
使用纯注解
源码阅读和构建
如何读源码?
原则
定焦原则:抓主线
宏观原则:站在上帝视角,关注源码结构和业务流程(淡化具体某行代码的编写细节)
方法和技巧
断点观察调用栈
反调(Find Usages)
经验(spring框架中doXXX,做具体处理的地方)
导入源码步骤
1. 下载源码 (直接用压缩包或者从github上下载)
2. 打开项目的build.gradle换成阿里云的镜像
3. 如果第2步不好使,建议直接替换全局镜像
4.按照顺序编译每个项目,如果遇到乱码就删除对应项目下的build文件夹
顺序:core-oxm-context-beans-aspects-aop
顺序:core-oxm-context-beans-aspects-aop
0 条评论
下一页