Spring 框架分析
1.Spring 概述
1.1 Spring的优势
- ⽅便解耦,简化开发
通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进⾏控制,避免硬编码所造成的
过度程序耦合。⽤户也不必再为单例模式类、属性⽂件解析等这些很底层的需求编写代码,可以更
专注于上层的应⽤。 - AOP编程的⽀持
通过Spring的AOP功能,⽅便进⾏⾯向切⾯的编程,许多不容易⽤传统OOP实现的功能可以通过
AOP轻松应付。 - 声明式事务的⽀持
@Transactional
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式⽅式灵活的进⾏事务的管理,提⾼
开发效率和质量。 - ⽅便程序的测试
可以⽤⾮容器依赖的编程⽅式进⾏⼏乎所有的测试⼯作,测试不再是昂贵的操作,⽽是随⼿可做的
事情。 - ⽅便集成各种优秀框架
Spring可以降低各种框架的使⽤难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、
Quartz等)的直接⽀持。 - 降低JavaEE API的使⽤难度
Spring对JavaEE API(如JDBC、JavaMail、远程调⽤等)进⾏了薄薄的封装层,使这些API的使⽤
难度⼤为降低。 - 源码是经典的 Java 学习范例
Spring的源代码设计精妙、结构清晰、匠⼼独⽤,处处体现着⼤师对Java设计模式灵活运⽤以及对
Java技术的⾼深造诣。它的源代码⽆意是Java技术的最佳实践的范例。
1.2 Spring 的核心结构
Spring是⼀个分层⾮常清晰并且依赖关系、职责定位⾮常明确的轻量级框架,主要包括⼏个⼤模块:数据处理模块、Web模块、AOP(Aspect Oriented Programming)/Aspects模块、Core Container模块和 Test 模块,如下图所示,Spring依靠这些基本模块,实现了⼀个令⼈愉悦的融合了现有解决⽅案的零侵⼊的轻量级框架。
- Spring核⼼容器(Core Container) 容器是Spring框架最核⼼的部分,它管理着Spring应⽤中bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼容器之上。
- ⾯向切⾯编程(AOP)/Aspects Spring对⾯向切⾯编程提供了丰富的⽀持。这个模块是Spring应⽤系统中开发切⾯的基础,与DI⼀样,AOP可以帮助应⽤对象解耦。
- 数据访问与集成(Data Access/Integration)
Spring的JDBC和DAO模块封装了⼤量样板代码,这样可以使得数据库代码变得简洁,也可以更专注于我们的业务,还可以避免数据库资源释放失败⽽引起的问题。 另外,Spring AOP为数据访问提供了事务管理服务,同时Spring还对ORM进⾏了集成,如Hibernate、MyBatis等。该模块由JDBC、Transactions、ORM、OXM 和 JMS 等模块组成。 - Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。
- Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。
2.核心思想
2.1什么是IOC?
IOC:Inversion Of Control (控制反转/反转控制),注意IOC描述的是一个技术思想,并非具体实现。
IOC描述的事情是 面向对象语言开发领域 对象的创建,管理的问题。
传统开发方式:比如类A依赖于类B,我们会在类A中new一个类B的对象。
IOC思想下开发方式:我们不用再代码中去new对象了,而是引入一个IOC容器(Spring 框架实现),由这个IOC框架来帮助我们去实例化并且管理对象,当类A需要使用类B对象的时候,由IOC容器将这个B对象注入到A对象中。
为什么称作控制反转?
控制:指的是对象创建(实例化,管理)的权利
反转:控制权不在我们手中,而是交给IOC容器了。
2.2 IOC解决了什么问题?
解决对象之间的耦合度过高的问题
2.3 IOC和DI(Dependancy Injection)的区别
IOC和DI其实描述的都是同一件事情(对象的实例化,管理,依赖关系维护),只是角度不一样罢了。
IOC是站在对象的角度,对象的实例化,管理,依赖关系维护反转给了IOC容器。
DI是站在容器的角度,容器会把对象依赖的其他对象注入。
2.4 什么是AOP?
AOP:Aspect Oriented Programming 面向切面编程
AOP是OOP(Object Oriented Programming)的延续,从OOP先说起.
OOP三大特征:封装,继承,多态。
以上图为例,可以看出OOP是一种垂直纵向的继承体系。OOP编程思想可以解决绝大多数的代码重复的问题,但是有一些情况是处理不了的。比如在下图的顶级父类Animal中的多个方法中的相同位置出现了重复代码,OOP就无法解决。
横切逻辑代码:在多个顺序流程中出现的相同子代码流程,我们称之为横切逻辑代码。
比入上图的性能监控。
横切逻辑代码的使用场景比较有限:一般是事务控制,权限校验,日志。
横切逻辑代码存在的问题:
- 横切逻辑代码重复的问题。
- 横切逻辑代码与业务代码混杂在一起,维护非常不方便。
此时AOP出场,AOP思想提出横向抽取机制,将横切逻辑代码和业务代码进行拆分。如下图
代码拆分是很容易的,但是如何在不改变原有业务逻辑的条件下,实现将横切逻辑代码织入到业务逻辑代码中,达到和未拆分前一致的效果,这个是比较难的。后面我们会在分析源码时继续探讨Spring是如何优雅实现AOP的。
2.5 AOP解决了什么问题?
解决在不改变原有业务逻辑的条件下,实现将横切逻辑代码织入到业务逻辑代码中,在根本上解耦合,避免了横切逻辑代码重复。
2.6 为什么叫做面向切面编程?
将切面二字拆开理解:
切:指的是横切逻辑。
面:被横切逻辑代码影响的往往是多个方法的多个连接点,多点成线,线再组成面。
3.Spring IOC应用
3.1 Spring IOC基础
Spring实现IOC的配置有三种情形:
1.纯XML:
bean信息的定义和依赖全部配置在xml中
2.XML+注解:
部分bean(通常是引入第三方jar包的bean并将其加入到IOC容器)使用xml定义,部分bean(通常是SpringBean)使用注解
3.纯注解模式:
所有的bean都用注解来定义。
通常实际开发中,使用较多的是2和3.
以上3种场景下,对应JAVASE和JAVAWEB应用启动IOC容器时需要使用不同的容器类。
3.2 BeanFactory 和 ApplicationContext的区别
BeanFactory是Spring框架中IOC容器的顶层接口,也称之为基础容器,它只用来定义一些容器的基础功能,定义一些基础规范。
而ApplicationContext 是BeanFactory的子接口,也称之为高级容器,它具备BeanFactory 定义的全部功能,比如说国际化支持和资源访问等等。
3.3 启动IOC容器方式
启动IOC容器的方式:
Java环境下启动IoC容器
ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使⽤)
FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
Web环境下启动IoC容器
从xml启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置Spring ioc容器的配置⽂件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--使⽤监听器启动Spring的IOC容器-->
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
</listener>
</web-app
从配置类启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--告诉ContextloaderListener知道我们使⽤注解的⽅式启动ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<paramvalue>org.springframework.web.context.support.AnnotationConfigWebAppli
cationContext</param-value>
</context-param>
<!--配置启动类的全限定类名-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.lagou.edu.SpringConfig</param-value>
</context-param>
<!--使⽤监听器启动Spring的IOC容器-->
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass>
</listener>
</web-app>
3.4 Spring实例化bean的方式
spring实例化bean的四种方式:
- 使用构造器实例化Bean
无参构造 - 有参构造
- 使用静态工厂方式实例化Bean
- 使用实例工厂方法实例化Bean
- 使用setter 方式
3.5 Bean的作用域及生命周期
下图是Spring 官方对框架支持的bean作用域介绍:
需要注意的是,Spring框架在默认配置下创建的bean对象都是singleton(单例)。
- singleton 单例模式,spring IOC容器中仅存在一个,容器初始化时就会加载(懒加载除外)。
- prototype 原型模式,也叫作多例模式。每次从容器中请求bean时,都会返回一个新的实例。容器初始化时不会加载,只有在请求容器时才会创建。
- 剩下4种都是应用于WebApplicationContext环境。
根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
生命周期:
- 单例模式:singleton
对象出生:当容器创建时,对象就被创建了(除懒加载)
对象生存:只有容器存在时,对象一直存在。
对象死亡:当销毁容器时,对象也被销毁。
一句话概括:单例模式对象的bean生命周期与容器相同。 - 多例模式:prototype
对象出生:当使用对象时,创建新的对象实例。
对象生存:只有对象在使用中,对象一直存在。
对象死亡:当对象长时间不用时,对象也被GC回收。
一句话概括:多例模式对象的bean对象,spring只负责创建,不负责销毁。