蹊源的Java笔记—Spring

前言

在现如今的​​Java​​​开发中有一个不得不提的工具,那就是​​Spring​​​,​​Spring​​​是​​Java EE​​​编程领域的一个轻量级开源框架, 是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。本期博客带领大家去了解​​Spring​​相关的知识点。

线程并发与线程安全可参考我的博客:​​蹊源的Java笔记—线程并发与线程安全​​

Mysql数据库可参考我的博客:​​蹊源的Java笔记—Mysql数据库​​

正文

Spirng

Spring是​​Java EE​​​(​​Java​​企业应用)编程领域的一个轻量级开源框架, 是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。

所谓敏捷开发是指以用户的需求进化为核心,采用迭代、循序渐进的方法进行软件开发方式。

Spring常用的七大模块
Spring Core
Spring Core封装包是框架的最基础部分,提供​​IOC​​​控制反转和​​DI​​依赖注入特性:

  • IOC控制反转:一种编程思想,借助容器来管理对象之间的依赖关系。
  • DI依赖注入:​​DI​​​是实现​​IOC​​​的一种手段,其内部是通过​​Java​​的反射机制来实现的。

Spring Context
Spring Context构建于​​Core​​​封装包基础的上下文封装包,提供了一种框架式的对象访问方法,​​Spring​​​ 上下文包括企业服务,例如​​JNDI​​​、电子邮件、国际化、校验等。
​​​JNDI​​​提供统一的客户端​​API​​​,通过不同的访问提供者接口​​JNDI​​​服务供应接口(​​SPI​​)的实现.

Spring Dao
Spring Dao对数据访问进行封装:

  • ​Dao​​中来管理异常处理和不同数据库提供商抛出的异常。
  • 简化了错误处理,并极大的降低了需要编写的异常代码数量,比如打开和关闭连接。

Spring ORM
Spring ORM提供了对象关系映射,包括​​Mybatis​​​、​​Hibernate​​​等。这些对象关系映射框架都遵从​​Spring​​​的通用事务和​​Dao​​的层次结构。

Spring AOP
Spring AOP声明式事务,是一种​​OOP​​的延伸,用于给不存在继承关系的对象之间引用一个公共行为。

Spring Web
Spring Web,​​Spring​​​中的​​Web​​​提供了基础的针对​​WEB​​开发的集成特性。

Spring Web MVC
Spring Web MVC是一个​​Model-View-Controller Web​​​框架,它是基于前端控制器​​Servel​​​并发处理​​http​​请求并进行展示。

Spring IOC容器

蹊源的Java笔记—Spring_Spring原理

Spring注册Bean到IOC容器的流程:

  1. 读取​​Bean​​的配置信息
  2. 根据​​Bean​​​注册表实例化​​Bean​
  3. 将​​Bean​​​实例放在​​Spring​​容器中
  4. 应用程序通过​​Bean​​​缓存池(内部是通过​​HashMap​​​来实现的)来使用​​Bean​

Spring Bean的作用域:

  • 单例模式(默认):​​Spring​​创建​​Bean​​的原则是不等​​bean​​创建完成就将​​beanFactory​​提早放到缓存中,如果其他​​bean​​依赖这个​​bean​​可以直接使用,这种三级缓存的机制很好地解决了循环依赖的问题。
  • 多例模式(原型模式):每次使用时都会创建新的​​Bean​​实例,​​Bean​​调用​​setAllowCircularReferences(false)​​来禁止循环依赖,否则出现循环依赖会直接抛异常。
  • Request模式:一次 ​​request​​ 一个实例, 当前 ​​Http​​ 请求结束,该 ​​bean​​ 实例也将会被销毁.
  • Session模式:在一次 ​​Http Session​​ 中,容器会返回该 ​​Bean​​ 的同一实例。而对不同的 ​​Session​​ 请求则会创建新的实例,该 ​​bean​​ 实例仅在当前 ​​Session​​ 内有效。
  • global Session模式(不常使用):在一个全局的 ​​Http Session​​ 中,容器会返回该 ​​Bean​​ 的同一个实例,仅在 使用​​portlet context​​ 时有效。

在​​xml​​​配置文件中添加配置属性​​scope= prototype​​​使用,或者使用注解​​@Scope("prototype")​

Bean的生命周期

  1. Bean的实例化:由​​BeanFactory​​​读取​​Bean​​定义文件,并生成各个实例。
  2. IOC依赖注入:执行​​Bean​​的属性依赖注入。
  3. BeanName:执行​​BeanNameAware​​​的​​setBeanName()​​​, 此处传递的就是 ​​Spring​​​ 配置文件中 ​​Bean​​​ 的 ​​id​​ 值
  4. BeanFactory:执行​​BeanFactoryAware​​​的​​setBeanFactory()​
  5. 传入Spring上下文:执行​​ApplicationContextAware​​​接口的​​setApplicationContext()​
  6. 初始化预处理:​​BeanPostProcessors​​​的​​postProcessBeforeInitialization()​
  7. 构造:如果​​Bean​​​在​​Spring​​​配置文件中配置了​​init-method​​属性会自动调用其配置的初始化方法。
  8. 加工构造后:​​BeanPostProcessors​​​的​​processAfterInitialization()​​​,如果是单例模式会将​​Bean​​​的实例放在​​Bean​​的缓存池中。
  9. 自动清理阶段:执行​​DisposableBean​​​的​​destroy()​
  10. 自配置清理:如果这个​​Bean​​​的​​Spring​​​配置中配置了​​destroy-method​​属性,会自动调用其配置的销毁方法。

Spring常用注解

1.自动注入(也叫Spring自动装配)

  • @Resources: 默认是​​byname​
  • @Autowired: 默认是​​bytype​​​ ,如果存在多个相同类型的,通过变量名可以与​​Bean​​​的​​id​​​或者​​name​​进行匹配
  • @Autowired+@Qualifier(“name”): 可以实现等同 ​​@Resources​​的效果

2.声明Bean定义

蹊源的Java笔记—Spring_java_02

3.@Configuration:用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法

  • ​@EnableAutoConfiguration​​​ ,如果使用​​@Configuration​​的话需要在启动类添加这个注解,用于扫描配置类。
  • 等同于在​​spring​​​配置文件中 ​​<context :annotation-config>​

4.@Constroller与@RestConstroller的区别

  • ​@Constroller​​使用了视图解析器
  • ​@RestConstroller​​​没有使用视图解析器,不能打开​​jsp​

5.@ComponentScan 对Bean进行扫描

  • 等同于在​​Spring​​​配置文件中 ​​<context:component-scan base-package="包名">​

6.@Bean和@Component的区别
相同点:两者的结果都是为​​Spring​​​容器注册​​Bean​​​.
不同点:​​@Component​​​ 通常是通过类路径扫描来自动侦测以及自动装配到​​Spring​​​容器中。
    ​​​@Bean​​​ 注解通常是我们在标有该注解的方法中定义产生这个​​bean​​的逻辑。

AOP面向切面编程

应用场景:需要多个不具有继承关系的对象引入一个公共行为
使用方法

  • 在​​XML​​​配置文件中 加入​​<aop:aspect-autoproxy>​​​开启​​AOP​​​支持,并在​​XML​​中定义切面
  • 使用​​@Aspect​​​注解定义切面,启动类加上​​@EnableAspectJAutoProxy(proxyTargetClass =true)​​​来开启​​AOP​​支持

Spring AOP的实现方式

JDK动态代理

  • 类对象必须实现接口
  • ​JDK​​​动态代理,背后是借助​​Java​​​多态的特性,因为​​JDK​​​动态代理生成的​​class​​​文件已经继承了​​Proxy​​​,而​​Java​​​是单继承的,不能继承目标对象,只能实现目标对象,所以是基于​​JDK​​动态代理是基于接口的。

JDK动态代理主要涉及两个类:

  • InvocationHandler:是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
  • Proxy: 利用 ​​InvocationHandler​​ 动态创建 一个符合某一接口的实例,生成目标类的代理对象。

Cglib动态代理

  • ​Cglib​​​是一个强大的高性能,高质量的代码生成类库, 可以在运行期扩展 ​​Java​​​ 类与实现 ​​Java​​​ 接口,​​CgLib​​​ 封装了​​asm​​​,可以再运行期动态生成新 的 ​​class​​。

特别要注意的是:

  • 目标类实现接口的情况下使用​​JDK动态代理​​​,没有实现接口的情况下使用​​Cglib动态代理​​。
  • 可以使用​​ProxyTargetClass = true​​​,强制所有都使用​​Cglib​​动态代理。
  • ​Cglib​​​所创建的动态代理对象在实际运行时候的性能要比​​JDK​​​动态代理高不少,有研究表明,大概要高10倍;但是​​Cglib​​​在创建对象的时候所花费的时间却比​​JDK​​动态代理要多很多,有研究表明,大概有8倍的差距;
  • 对于​​singleton​​​的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用​​Cglib​​​动态代理,反正,则比较适用​​JDK​​动态代理。

使用AOP主要的应用场景:

  1. ​Authentication​​ 权限检查
  2. ​Caching​​ 缓存
  3. ​Context passing​​ 内容传递
  4. ​Error handling​​ 错误处理
  5. ​Lazy loading​​ 延迟加载
  6. ​Debugging​​ 调试
  7. ​logging​​​, ​​tracing​​​, ​​profiling and monitoring​​日志记录,跟踪,优化,校准
  8. ​Performance optimization​​性能优化,效率检查
  9. ​Persistence​​ 持久化
  10. ​Resource pooling​​资源池
  11. ​Synchronization​​同步
  12. ​Transactions​​ 事务管理

循环依赖
循环依赖指的是​​Spring Bean​​之间相互依赖的情况。

解决循环依赖的前置条件:

  • 出现循环依赖的​​Bean​​必须要是单例
  • 依赖注入的方式不能全是构造器注入的方式

Spring在创建Bean的时候默认是按照自然排序来进行创建的,IOC容器先创建A,再创建B

  • A、B均采用​​setter​​方式相互注入
  • A采用​​setter​​方式获取B,B采用构造器方式获取A

以上两种情况的循环依赖时可以解决循环依赖,其他情况都会异常抛出。

解决循环依赖的流程:

  1. 当A完成了实例化并添加进了​​Bean​​的缓存池(一级缓存)中。
  2. 为A进行属性注入了,在注入时发现A依赖了B,会去实例化B。
  3. 在创建B的时候,因为A已经放在​​Bean​​​的缓存池(一级缓存)当中了,所以无论B采用的​​setter​​方式还是构造器方式都可以获取A。

这里需要注意的是:

  • 如果A采用的是构造器方式,创建A时发现依赖于B,于是会先去创建B,但是B又依赖于A,并且缓存没有A,所以会直接因为循环依赖,导致启动异常。
  • ​@Autowired​​​、​​@Resources​​​实际上都是​​setter​​方式注入依赖。

AOP在解决循环依赖的问题上,会使用三级缓存的方式去完成:

  • singletonObject:一级缓存,这里的​​bean​​​是已经创建完成的,一旦进行​​getBean​​操作时,我们第一时间就会寻找一级缓存
  • earlySingletonObjects:二级缓存,该缓存所获取到的​​bean​​是提前曝光出来的,是还没创建完成的。
  • singletonFactories:三级缓存为早期曝光对象工厂,这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象。

知识点:

  1. 添加单例缓存,当​​bean​​​被创建完以后进行的操作。这时候说明​​bean​​​已经创建完,删除二三级缓存,直接留下一级缓存,并且注册该​​bean​​。
  2. 二级缓存可以提前曝光被其他​​Bean​​​所引用,它可以解決循环依赖。但是二级缓存无法解决​​AOP​​​循环依赖的问题,因为不可能每次执行​​singleFactory.getObject()​​方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。
  3. 三级缓存是用来来保存产生的代理对象,但它并没有所谓性能上的“提升”。

Spring MVC

  • 模型(​​Model​​)代表数据控制器。数据的读取,插入,更新都是由模型来负责。
  • 视图(​​View​​)是展示给用户的最终页面。视图负责将数据以用户友好的形式展现出来。
  • 控制器(​​Controller​​​)是模型,视图以及其他任何处理 ​​HTTP​​ 请求所必须的资源之前的中介

SpringMVC处理请求的的控制器

  • DispatcherServlet前端控制器:接收用户请求
  • HandlerAdapter处理器适配器:选择合适的处理器,并且调用相应功能处理方法
  • ViewResolver视图解析器:用于视图解析

用户发送请求和返回响应的流程:

  1. 发送请求 至​​DispatcherServlet​
  2. 映射处理器 获取处理器映射至 ​​DispatcherServet​
  3. ​HandlerAdapter​​进行处理器适配
  4. 调用处理器相应功能处理方法 (获得 ​​View​​​ 和​​model​​​ 至​​DispatcherServlet​​​ ) 这里涉及​​Controller​
  5. ​ViewResolver​​​ 接收​​View​​ 进行视图解析
  6. ​Model​​​加入到​​View​​ 中进行视图渲染
  7. ​DispatcherServlet​​返回响应

Spring 事务管理

事务

事务是访问并可能更新数据库中各种数据项的一个程序执行单元。
在关系性数据库中,事务可以是一条​​​sql​​​,一组​​sql​​语句。

声明式事务与编程式事务

  • 声明式事务管理使业务代码不受污染,一个普通的​​POJO​​对象,只要加上注解就可以获得完全的事务支持。
  • 声明式事务管理它的最细粒度只能作用到方法级别,而编程式事务那样可以作用到代码块级别。

Spring对事物的支持

​Spring​​​并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给​​Hibernate​​​或者​​JTA​​等持久化机制所提供的相关平台框架的事务来实现。

常见的有:

  • Jdbc事务:​​mybayis​​通常使用这个方式进行事务管理
  • Hibernate事务:​​Hibernate​​的事务管理方式
  • Java持久化API事务:​​JPA​​就是基于这种方式
  • Java原生API事务 :使用以上多种事务

Transational注解

@Transational是一种声明式事务的一种方式,利用的是​​java​​​的反射机制,实现动态代理,再借助泛型实现,能够忽略​​class​​​的类型,基于​​AOP​​做事务上的操作。

  • ​Service​​​类前加上​​@Transactional​​​,声明这个​​service​​​所有​​public​​方法需要事务管理。
  • ​@Transactional​​​放在方法前面需要方法是​​public​​级别的。
  • 实际开发中我们使不使用​​@Transactional​​取决于我们是否需要保留中间态错误数据。
@Transactional(propagation = Propagation.SUPPORTS,isolation = Isolation.READ_COMMITTED,rollbackFor={BizException.class})

@Transactional参数说明:

  • propagation :用于指定传播行为
  • isolation:用于指定隔离级别
  • rollbackFor:当发生​​BizException​​进行事务回滚

事务的特点

  • 原子性:多条指令作为一个集体,要么都执行,要么都不执行
  • 一致性:比如 a=100 b=100, a、b之间交易 总和一定是200
  • 隔离性:一个事务不受其他事务的影响
  • 持久性:事务一旦提交,它对数据库的改变时永久性的

传播行为

场景:当一个事务去调用另一个事务时,用于定义事务的边界:

  1. Propagation.REQUIRED:表示当前方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,否则,会启动一个新的事务。(默认)
  2. Propagation.SUPPORTS:表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在事务中运行。
  3. Propagation.MANDATORY:表示当前方法必须运行在事务中,如果事务不存在,则会抛出异常。
  4. PROPAGATION_REQUIRES_NEW:表示当前方法必须运行在自己的事务。一个新的事务将会被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用​​JtaTransactionManager​​​作为事务管理器,则需要访问​​TransactionManager​​对象。
  5. PROPAGATION_NOT_SUPPORTED:表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将会被挂起。如果使用​​JtaTransactionManager​​​作为事务管理器,则需要访问​​TransactionManager​​对象。
  6. PROPAGATION_NEVER:表示该方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。
  7. PROPAGATION_NESTED:表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么行为和​​Propagation.REQUIRED​​一致。但要注意当前事务管理器是否支持嵌套事务。

隔离级别

场景: 隔离级别定义一个事务可能受其他并发事务活动活动影响的程度。

​mysql​​事务隔离级别 默认是 :可重复读

蹊源的Java笔记—Spring_Spring原理_03

此外还有:​​DEFAULT​​使用数据库默认的事务隔离级别.

脏读:读取出来的数据是无效数据。( 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的)

幻读:读取数据时读取出来多条历史数据。( 当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。)

不可重复读:数据由于锁机制,只能被读取一次。 (发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据)

Spring boot

蹊源的Java笔记—Spring_蹊源_04

SpringBoot是基于​​Spring4​​的条件注册的一套快速开发整合包, 实现了自动配置,降低了项目搭建的复杂度。

SpringBoot的优势

  • 可快速构建独立的​​Spring​​应用程序
  • 嵌入的 ​​Tomcat​​​,无需部署 ​​WAR​​ 文件
  • 简化了​​Maven​​配置
  • 自动配置​​Spring​
  • 提供了​​actuator​​模块用于健康检查
  • 无需任何​​XML​​配置

SpringBoot的启动过程主要做的三件事:

  • 进行​​SpringApplication​​的初始化模块,配置一些基本的环境变量、资源、构造器、监听器;
  • 调​​run​​​方法,启动流程的监听模块、加载配置环境​​Environment​​​模块、及核心的创建上下文环境​​applicationContext​​模块;
  • 自动化配置模块,创建​​spring​​​容器, ​​refreshContext()​​​ ,实现​​starter​​​自动化配置,​​spring.factories​​​文件加载, ​​bean​​实例化

​spring.factories​​就像是工厂一样配置了大量的接口对应的实现类,我们通过这些配置 + 反射处理就可以拿到相应的实现类。

SpringBoot启动的16个阶段:

  1. 新建​​module​​​,在主程序类加入断点,启动​​springboot​
  2. 首先进入​​SpringAplication​​​类​​run​​方法
  3. ​run​​​方法新建​​SpringApplication​​对象
  4. ​run​​方法首先创建并启动计时监控类
  5. 接着通过​​configureHeadlessProperty​​​设置​​java.awt.headless​​的值
  6. 调用​​getRunListeners​​​创建所有​​spring​​监听器
  7. ​DefaultApplicationArguments​​初始化应用应用参数
  8. ​prepareEnvironment​​​根据运行监听器和参数准备​​spring​​环境
  9. 调用​​createApplicationContext​​方法创建应用上下文
  10. 通过​​prepareContext​​准备应用上下文
  11. ​refreshContext​​方法刷新上下文
  12. 调用​​stop​​方法停止计时监控器类
  13. 调用​​started​​发布应用上下文启动完成事件
  14. ​callRunners​​​方法执行所有​​runner​​运行器
  15. 调用​​running​​发布应用上下文就绪事件
  16. 最后返回应用上下文

蹊源的Java笔记—Spring_java_05