四、Spring & Mybatis
Spring & SpringBoot
4.0 Spring 介绍
Spring源码解读,github:seaswalker/spring-analysis
Spring
是一个轻量级的IOC和AOP容器框架。是为Java应用程序提供基础服务的一套框架,目的是简化应用程序的开发,使开发人员只需关心业务需求。核心模块如下:
Spring Core | 核心类库,提供IOC服务 |
Spring Context | 提供框架式的Bean访问方式,以及企业级功能(JIDI、定时任务等) |
Spring AOP | 提供AOP服务 |
Spring DAO | 对JDBC进行了抽象,简化了数据访问异常等处理 |
Spring ORM | 对现有的ORM持久层框架进行了支持 |
Spring Web | 提供基本的面向Web综合特性 |
Spring MVC | 提供面向web应用的Model-View-Controller实现 |
Spring优点
- Spring的
依赖注入
将对象之间的依赖关系交给了框架来处理,减小了各个组件之间的耦合性 AOP面向切面编程
,可以将通用任务抽取出来,复用性更高- Spring对于
其余主流框架都提供了很好的支持
,代码侵入性很低
4.1 Spring 控制反转(IOC)
IOC也叫控制反转,将对象间的依赖关系交给Spring容器,使用配置文件来创建所依赖的对象,由主动创建对象改为了被动方式,实现解耦合。可以通过注解@Autowired
和@Resource
来注入对象,被注入对象必须被以下四个注解之一标注:@Controller
@Service
@Repository
@Component
在Spring配置文件中配置 context:annoation-config/ 元素开启注解,还有一个概念是DI(依赖注入),即在应用程序在运行时依赖IOC容器来动态注入对象所需要的外部资源等。
4.2 IOC容器的初始化过程
IOC容器的初始化主要包括Resource定位
、载入
和注册
三个步骤。
-
Resource 定位是指BeanDefinition的资源定位
也就是IOC容器找数据的过程。Spring中使用外部资源描述一个Bean对象,IOC容器第一步就是需要定位Resource外部资源。由ResourceLoader通过统一的Resource接口来完成定位。 -
BeanDefinition载入
载入过程就是把定义的Bean表示成IOC容器内部的数据结构,即BeanDefinition。在配置文件中每一个Bean都对应一个BeanDefinition对象。通过BeanDefinitionReader读取,解析Resource定位资源,将用户定义好的Bean表示成IOC容器的内部数据结构BeanDefinition。
在IOC内部维护着一个BeanDefinitionMap的数据结构,通过BeanDefinitionMap,IOC容器可以对Bean进行更好的管理。 -
BeanDefinition注册
注册指将前面的BeanDefinition保存到Map中的过程,通过BeanDefinitionRegistry接口来实现注册
4.3 Spring中的AOP面向切面编程
AOP面向切面编程是指当需要在某一个方法之前或之后做一些额外的操作,如日志记录,权限判断,异常统计等,可利用AOP将功能代码从业务逻辑代码中分离
。使架构变得高内聚低耦合
(1)操作术语
Joinpoint(连接点) | 可以作为切入点的机会,所有方法都可以作为切入点 |
Pointcut(切入点) | 切入点指我们要对哪些Joinpoint进行拦截定义 |
Advice(通知/增强) | 通知指拦截到Joinpoint之后所要做的事情 |
Aspect(切面) | 切入点和通知大的结合 |
Introduction(引介) | 引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或属性 |
Target(目标对象) | 代理的目标对象(要增强的类) |
Weaving(织入) | AOP的实现过程。把advice应用到target的过程 |
Proxy(代理) | 一个类被AOP织入增强后,就产生一个结果代理类 |
- 切入点就是在类里边可以有很多方法被增强。如在实际操作中,只是在增强了个别方法,则定义实际被增强的某个方法 为切入点
- 通知/增强 指 增强逻辑。如扩展日志功能,这个日志功能称为增强;
- 切面指把增强应用到具体方法上面的过程
(2)AOP的实现方式,主要有两种
- 使用JDK动态代理实现,使用java.lang.reflection.Proxy类来处理
JDK 动态代理只能对实现了接口的类生成代理,而不是针对类,该目标实现的接口都将被代理。原理是通过在运行期创建一个接口实现类来完成对目标对象的代理。实现步骤大概如下:
- 定义一个实现接口InvocationHandler类
- 通过构造函数,注入被代理函数
- 实现invoke( Object proxy, Method method, Object[] args )方法
- 在主函数中获得被代理类的类加载器
- 使用Proxy.newProxyInstance()产生一个代理对象
- 通过代理对象调用各种方法
- 使用cglib实现
其主要对类实现代理,对是否实现接口无要求。原则对指定类生成一个子类,覆盖其中方法,因为是继承,所以被代理类或方法不可以被声明为final,步骤:
- 定义一个实现MethodInterceptor接口的类
- 实现其中intercept()方法,其中调用proxy.invokeSuper()
(3)AOP如何选择代理方式
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,也可强制使用cglib实现AOP
- 如果目标对象没有实现接口,必须采用cglib,Spring会自动在JDK东动态和cglib之间转换
4.4 BeanFactory和FactoryBean的区别
- BeanFactory:是一个Bean工厂,是Spring IOC容器的顶层接口,用于管理Bean,即实例、定位、配置应用程序中得到对象及建立这些对象间的依赖。
- FactoryBean:工厂Bean,是一个Bean,作用是产生其他Bean实例,需要提供一个工厂方法,该方法用来返回其他Bean实例
4.5 BeanFactory和ApplicationContext区别
BeanFactory,Spring最顶层接口,包含各种Bean的定义,读取Bena配置文档,管理Bean的加载,实例化,控制Bean的生命周期,维护Bean之间的依赖关系。
ApplicationContext接口时BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能
- 继承了MessageSource,支持国际化
- 提供了统一的资源文件访问方式
- 提供了在Listener中注册Bean‘的事件
- 提供同时加载多个配置文件的功能
- 载入多个上下文,是的每一个上下文都专注于一个特定层次,如web层
(1) ApplicationContext三种常见的实现方式
- FileSystemXmlApplicationContext:此容器从一个XML文件中加载Bean的定义, XML Bean配置文件全路径名必须提供给它的构造函数。
- ClassPathXmlApplicationContext: 此容器也从一个XML文件中加载Bean的定义, 需要正确设置classpath因为这个容器将在classpath里找Bean配置
- WebXmlApplicationContext: 从容器加载一个XML文件,定义了一个Web应用所有的Bean。
(2) 在创建Bean和内存占用方面的区别
相对于基本的BeanFactory,ApplicationContext占用内存空间较多。当应用程序配置较多的Bean时。程序启动要慢,解释如下:
- BeanFactory采用的时延迟加载形式来注如Bena的,即只有在使用到某个Bean时(调用getBean()方法),才对该Bean进行加载实例化。这样,就不能发现一些存在于Spring配置中的问题。如果Bean的某一个属性没有注入,BeanFactory加载后,直至第一次使用调用getBean方法才会抛出异常。
- ApplicationContext,它在容器启动时,一次性创建了所有Bean。在容器启动时我们就能发现Spring配置错误,这样有利于检查所有依赖属性是否注入。ApplicationContext启动后预载入所有单实例Bean,确保当使用时可以直接获取。
(3)BeanFactoryApplicationContext的优缺点分析:
- | BeanFactory | ApplicationContext |
---|---|---|
优点 | 应用启动的时候占用资源少 | 所有Bean在启动时都进行了预加载,系统运行的速度加快;在启动时可发现应用的配置问题 |
缺点 | 运行速度会相对来说慢,而且有时会出现空指针异常 | 所有Bean都进行了预加载,占用内存较大 |
4.6 Spring中Bean的作用域
- singleton:Bean在每个SpringIOC容器只有一个实例,默认作用域。
- prototype:一个Bean的定义可以有多个实例。
- request:每次http请求都会创建一个Bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- session:在一个HTTP session中。一个Bean定义对应一个实例,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- global-session:在一个全局的HTTP session中。一个Bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
扩展1. 循环依赖
如果A对象创建的过程中需要用到B对象,但是B对象创建时也需要用到A对象,就形成了循环依赖,Spring怎么解决? 答:通过构造器注入构成的循环依赖没法解决,只能抛出BeanCurrentCreationException异常。
4.7 Spring单例Bean的线程安全问题
单例Bean和线程安全没有必然关系。只是多个线程操作同一个Bean的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。解决方法有二:
- 在Bean对象中尽量避免定义可变成员变量(不太现实)
- 在类中定义一个ThreadLocal成员变量,将需要改变的成员变量保存在ThreadLocal中(推荐)
4.8 Spring事务
(1)Spring中支持编程式事务管理
和声明式事务管理
- 编程式事务管理:使用Transcation Template实现
- 声明式事务管理:建立在AOP之上,其本质通过AOP功能,对方法前后进行拦截,将事务处理的功能织入到拦截方法中,也就在目标方法之前加入一个事务,在执行完目标方法之后根据执行情况提交或回滚事务
声明式事务的优点:不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。
(2)事务选择
声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的开发方式
,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足之处是声明式事务的最细粒度只能作用到方法级别
,无法做到像编程式事务那样可以作用到代码块级别
。
4.9 SpringMVC的消息处理流程
SpringMVC是一种轻量级Web框架,基于请求驱动的框架,使用了前端控制器(DispatcherServlet)m模型设计,再根据请求映射规则,分发给相应的页面控制器进行处理
具体流程为:通过前端控制器DispatcherServlet来接收并分发请求,然后通过HandlerMapping和HandlerAdapter找到具体可以处理该请求的Handler,经过逻辑处理,返回一个ModelAndView,经过ViewResolver处理,最后生成一个View视图返回给客户端,
4.10 Spring如何管理bean
主要有三种方式:BeanWrapper、BeanFactory和使用ApplicationContext
- BeanWrapper
- BeanFactory
- ApplicationContext
4.11 Spring中Bean的生命周期
大致分为四个阶段
- 实例化(Instantiation):
1
,实例化一个 bean 对象; - 属性赋值(Populate):
2
,为 bean 设置相关属性和依赖; - 初始化(Initialization):
3-7
,其中第 5、6 步为初始化操作,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,该阶段结束,才能被用户使用; - 销毁(Destruction):
8-10
,第8步不是真正意义上的销毁(还没使用呢),而是先在使用前注册了销毁的相关调用接口,为了后面第9、10步真正销毁 bean 时再执行相应的方法。
5 SpringBoot
SpringBoot是一种全新的框架,目的是为了简化Spring应用的初始搭建以及开发过程。该框架使用特定的方式(集成starter,约定优于配置)来进行配置,从而使开发人员不需要再定义样板化的配置。SpringBoot提供了一种新的编程范式,可以更加快速便捷地开发Spring项目,在开发过程当中可以专注于应用程序本身的功能开发,而无需在Spring配置上花太大的工夫。
5.1 SpringBoot特点
-
为基于Spring的项目开发提供更快捷的入门体验:特别是对于刚开始使用Spring框架的开发人员,无需关心使用Spring框架都需要引入哪些jar包,无需关心Spring框架与其他框架整合时都需要哪些配置文件。
SpringBoot会自动配置Spring
。 -
无需手动管理依赖jar包的版本
:SringBoot通过 spring boot starter管理其提供的所有依赖的版本,当升级SpringBoot时,这些依赖的版本也会随之升级,个人无需指定版本号,但是也可以自定义版本号覆盖SpringBoot的默认值。 -
自动配置,无需XML
:SpringBoot尝试根据你添加的jar依赖自动配置你的应用。 -
嵌入式的Tomcat,无需部署war文件
:传统的项目我们在启动或者部署的时候,需要将项目的war包放到服务器(如Tomcat、JBoss)的指定目录下,然后再启动服务器。而SpringBoot项目在启动时无需将war文件部署到服务器中。SpringBoot内置了Tomcat和Jetty容器。在服务器启动SpringBoot项目时可以通过jar指令直接启动, 在开发IDE中启动时只需运行Application类的main方法即可。 -
简化Maven配置
:SpringBoot通过 spring boot starter管理jar包,无需手动配置jar包的版本。之前我们在进行jar包配置时,需要找到jar包的版本号,而在SpringBoot项目中,则无需去管理版本号。