蹊源的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容器

Spring注册Bean到IOC容器的流程:
- 读取
Bean的配置信息 - 根据
Bean注册表实例化Bean - 将
Bean实例放在Spring容器中 - 应用程序通过
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的生命周期
- Bean的实例化:由
BeanFactory读取Bean定义文件,并生成各个实例。 - IOC依赖注入:执行
Bean的属性依赖注入。 - BeanName:执行
BeanNameAware的setBeanName(), 此处传递的就是 Spring 配置文件中 Bean 的 id 值 - BeanFactory:执行
BeanFactoryAware的setBeanFactory() - 传入Spring上下文:执行
ApplicationContextAware接口的setApplicationContext() - 初始化预处理:
BeanPostProcessors的postProcessBeforeInitialization() - 构造:如果
Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。 - 加工构造后:
BeanPostProcessors的processAfterInitialization(),如果是单例模式会将Bean的实例放在Bean的缓存池中。 - 自动清理阶段:执行
DisposableBean的destroy() - 自配置清理:如果这个
Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
Spring常用注解
1.自动注入(也叫Spring自动装配)
- @Resources: 默认是
byname - @Autowired: 默认是
bytype ,如果存在多个相同类型的,通过变量名可以与Bean的id或者name进行匹配 - @Autowired+@Qualifier(“name”): 可以实现等同
@Resources的效果
2.声明Bean定义

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主要的应用场景:
-
Authentication 权限检查 -
Caching 缓存 -
Context passing 内容传递 -
Error handling 错误处理 -
Lazy loading 延迟加载 -
Debugging 调试 -
logging, tracing, profiling and monitoring日志记录,跟踪,优化,校准 -
Performance optimization性能优化,效率检查 -
Persistence 持久化 -
Resource pooling资源池 -
Synchronization同步 -
Transactions 事务管理
循环依赖
循环依赖指的是Spring Bean之间相互依赖的情况。
解决循环依赖的前置条件:
- 出现循环依赖的
Bean必须要是单例 - 依赖注入的方式不能全是构造器注入的方式
Spring在创建Bean的时候默认是按照自然排序来进行创建的,IOC容器先创建A,再创建B:
- A、B均采用
setter方式相互注入 - A采用
setter方式获取B,B采用构造器方式获取A
以上两种情况的循环依赖时可以解决循环依赖,其他情况都会异常抛出。
解决循环依赖的流程:
- 当A完成了实例化并添加进了
Bean的缓存池(一级缓存)中。 - 为A进行属性注入了,在注入时发现A依赖了B,会去实例化B。
- 在创建B的时候,因为A已经放在
Bean的缓存池(一级缓存)当中了,所以无论B采用的setter方式还是构造器方式都可以获取A。
这里需要注意的是:
- 如果A采用的是构造器方式,创建A时发现依赖于B,于是会先去创建B,但是B又依赖于A,并且缓存没有A,所以会直接因为循环依赖,导致启动异常。
-
@Autowired、@Resources实际上都是setter方式注入依赖。
AOP在解决循环依赖的问题上,会使用三级缓存的方式去完成:
- singletonObject:一级缓存,这里的
bean是已经创建完成的,一旦进行getBean操作时,我们第一时间就会寻找一级缓存 - earlySingletonObjects:二级缓存,该缓存所获取到的
bean是提前曝光出来的,是还没创建完成的。 - singletonFactories:三级缓存为早期曝光对象工厂,这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象。
知识点:
- 添加单例缓存,当
bean被创建完以后进行的操作。这时候说明bean已经创建完,删除二三级缓存,直接留下一级缓存,并且注册该bean。 - 二级缓存可以提前曝光被其他
Bean所引用,它可以解決循环依赖。但是二级缓存无法解决AOP循环依赖的问题,因为不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。 - 三级缓存是用来来保存产生的代理对象,但它并没有所谓性能上的“提升”。
Spring MVC
- 模型(
Model)代表数据控制器。数据的读取,插入,更新都是由模型来负责。 - 视图(
View)是展示给用户的最终页面。视图负责将数据以用户友好的形式展现出来。 - 控制器(
Controller)是模型,视图以及其他任何处理 HTTP 请求所必须的资源之前的中介
SpringMVC处理请求的的控制器
- DispatcherServlet前端控制器:接收用户请求
- HandlerAdapter处理器适配器:选择合适的处理器,并且调用相应功能处理方法
- ViewResolver视图解析器:用于视图解析
用户发送请求和返回响应的流程:
- 发送请求 至
DispatcherServlet - 映射处理器 获取处理器映射至
DispatcherServet -
HandlerAdapter进行处理器适配 - 调用处理器相应功能处理方法 (获得
View 和model 至DispatcherServlet ) 这里涉及Controller -
ViewResolver 接收View 进行视图解析 -
Model加入到View 中进行视图渲染 -
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
- 隔离性:一个事务不受其他事务的影响
- 持久性:事务一旦提交,它对数据库的改变时永久性的
传播行为
场景:当一个事务去调用另一个事务时,用于定义事务的边界:
- Propagation.REQUIRED:表示当前方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,否则,会启动一个新的事务。(默认)
- Propagation.SUPPORTS:表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在事务中运行。
- Propagation.MANDATORY:表示当前方法必须运行在事务中,如果事务不存在,则会抛出异常。
- PROPAGATION_REQUIRES_NEW:表示当前方法必须运行在自己的事务。一个新的事务将会被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用
JtaTransactionManager作为事务管理器,则需要访问TransactionManager对象。 - PROPAGATION_NOT_SUPPORTED:表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将会被挂起。如果使用
JtaTransactionManager作为事务管理器,则需要访问TransactionManager对象。 - PROPAGATION_NEVER:表示该方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。
- PROPAGATION_NESTED:表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么行为和
Propagation.REQUIRED一致。但要注意当前事务管理器是否支持嵌套事务。
隔离级别
场景: 隔离级别定义一个事务可能受其他并发事务活动活动影响的程度。
mysql事务隔离级别 默认是 :可重复读

此外还有:DEFAULT使用数据库默认的事务隔离级别.
脏读:读取出来的数据是无效数据。( 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的)
幻读:读取数据时读取出来多条历史数据。( 当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。)
不可重复读:数据由于锁机制,只能被读取一次。 (发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据)
Spring boot

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个阶段:
- 新建
module,在主程序类加入断点,启动springboot - 首先进入
SpringAplication类run方法 -
run方法新建SpringApplication对象 -
run方法首先创建并启动计时监控类 - 接着通过
configureHeadlessProperty设置java.awt.headless的值 - 调用
getRunListeners创建所有spring监听器 -
DefaultApplicationArguments初始化应用应用参数 -
prepareEnvironment根据运行监听器和参数准备spring环境 - 调用
createApplicationContext方法创建应用上下文 - 通过
prepareContext准备应用上下文 -
refreshContext方法刷新上下文 - 调用
stop方法停止计时监控器类 - 调用
started发布应用上下文启动完成事件 -
callRunners方法执行所有runner运行器 - 调用
running发布应用上下文就绪事件 - 最后返回应用上下文









