offer来了(框架篇)学习笔记-第1章Spring原理及应用

Spring是一个企业级J2EE应用开发一站式解决方案,其提供的功能贯穿了项目开发的表现层、业务层和持久化层,同时,Spring可以和其他应用框架无缝整合。本章将详细介绍常用的Spring核心技术背后的原理。

Spring的特性

Spring基于J2EE技术实现了一套轻量级的Java Web Service系统应用框架。它有很多优秀的特性,很多公司都选择把Spring作为产品或项目的基础开发架构。Spring的特性包括轻量控制反转(Inversion of Control,IoC)、面向容器面向切面(Aspect Oriented Programming,AOP)和框架灵活

轻量

JAR包的大小上来说,Spring是一个轻量级的框架,其核心JAR包spring-web-5.2.0.RELEASE.jar和spring-core-5.2.0.RELEASE.jar的大小均为1.4MB左右;从系统的资源使用上来说,Spring也是一个轻量级的框架,其运行期间只需要少量的操作系统资源(内存和CPU)便能稳定运行。除此之外,Spring还是模块化的,应用程序在使用过程中可以根据需求引入模块(以JAR包依赖方式引入)来实现不同的功能,使其应用更加灵活。

控制反转

Spring的控制反转指一个对象依赖的其他对象将会在容器的初始化完成后主动将其依赖的对象传递给它,而不需要这个对象自己创建或者查找其依赖的对象。Spring基于控制反转技术实现系统对象之间依赖的解耦。

面向容器

Spring实现了对象的配置化生成和对象的生命周期管理,因此,可以理解为其是面向容器的。通过Spring的XML文件或者注解方式,应用程序可以配置每个Bean对象被创建和销毁的时间,以及Bean对象创建的先后顺序和依赖关系。Spring中的实例对象可以是全局唯一的单例模式,也可以在每次需要时都重新生成一个新的实例,具体以哪种方式创建Bean对象由Bean的生命周期决定,通过prototype属性来定义

面向切面

Spring提供了面向切面的编程支持,面向切面技术通过分离系统逻辑和业务逻辑来提高系统的内聚性。在具体的使用过程中,业务层只需要关注并实现和业务相关的代码逻辑,而不需要关注系统功能(例如系统日志、事务支持)和业务功能的复杂关系,Spring通过面向切面技术将系统功能自动织入业务逻辑的关键点。

框架灵活

基于容器化的对象管理技术,Spring中的对象可以被声明式地创建,例如通过XML文件或注解的方式定义对象和对象之间的依赖关系。Spring作为一个轻量级的J2EE Web框架,具有事务管理、持久化框架集成和Java Web服务等功能,应用程序可以根据需求引入相应的模块,以实现不同的功能。

Spring的模块

Spring为企业应用程序提供一站式服务。Spring是模块化的,允许应用按需引入各个模块。Spring提供的常用模块有核心容器层(Core Container)、数据访问层(Data Access)、Web应用层(WebAccess)。除此之外,Spring还包括AOP、Aspects、Instrumentation、Messaging和Test。Spring的模块如图所示。下面对Spring中各个核心的模块做简单的介绍。

核心容器层

核心容器层由Spring-Beans、Spring-Core、Spring-Context和SpEL(Spring ExpressionLanguage,Spring表达式语言)等模块组成。

Spring-Beans

Spring-Beans模块基于工厂模式实现对象的创建。Spring-Beans通过XML配置文件实现了声明式的对象管理,将对象之间复杂的依赖关系从实际编码逻辑中解耦出来。

Spring-Core

Spring-Core模块是Spring的核心功能实现,具体包括控制反转依赖注入。所谓依赖注入,是指在一个Bean实例中引用另外一个Bean实例时,Spring容器会自动创建其所依赖的Bean实例,并将该Bean实例注入(传递)到对应的Bean中。

Spring-Context

Spring-Context模块是在Spring-Beans和Spring-Core模块的基础上构建起来的。Spring-Context模块继承自Spring-Beans模块,并且添加了国际化、事件传播、资源加载和透明地创建上下文等功能。

同时,Spring-Context模块提供了一些J2EE的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Spring-Context模块操作Bean的入口。

Spring-Context-Support提供了将第三方库集成到Spring-Context的支持,比如缓存(EhCache、Guava、JCache)、邮件(JavaMail)、调度(CommonJ、Quartz)和模板引擎(FreeMarker、JasperReports、Velocity)等。

SpEL

SpEL模块提供了丰富的表达式语言支持,用于在运行过程中查询和操作对象实例。SpEL在JSP 2.1表达式语言规范的基础上进行了扩展,支持set方法、get方法、属性赋值、方法调用、访问数组集合、索引内容、逻辑算术运算、命名变量、基于名称在Spring IoC容器检索对象,同时支持列表的投影、选择和聚合等功能。

数据访问层

数据访问层包括JDBC、ORM、OXM、JMS和事务处理模块。

JDBC

JDBC模块提供了JDBC抽象层。Spring持久化层基于JDBC抽象层实现了在不同数据库之间灵活切换,而不用担心不同数据库之间SQL语法的不兼容。

ORM

ORM模块提供了对象关系映射API的集成,包括JPA(Java Persistence API)、JDO(Java DataObject)和Hibernate等。基于该模块,ORM框架能很容易地和Spring的其他功能(例如事务管理)整合。

OXM

OXM模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。

JMS

JMS模块包含消息的生产(Produce)和消费(Consume)功能。从Spring 4.1开始,Spring集成了Spring-Messaging模块,用于实现对消息队列的支持。

事务处理

事务处理(Transactions)模块基于接口方式实现了声明式事务管理。编程式事务的实现需要应用程序调用相应的beginTransaction()、commit()、rollback()等方法来实现事务的管理,Spring声明式事务只需要通过注解或配置即可实现事务的管理,具体的事务管理工作由Spring自动处理,应用程序不需要关心事务的提交(Commit)和回滚(Rollback)。

Web应用层

Web应用层主要包含Web交互和数据传输等相关功能,由Web、Web-MVC、Web-Socket、Web-Portlet组成。

Web

Web模块不但提供了面向Web应用的基本功能,还提供了HTTP(Hyper Text Transfer Protocol,超文本传输协议)客户端及Spring远程调用中与Web相关的部分。Web模块基于Servlet监听器初始化IoC容器

Web-MVC

Web-MVC模块为Web应用提供了模型视图控制(Model View Controller,MVC)和REST API服务的实现。Spring的MVC框架使数据模型和视图分离,数据模型负责数据的业务逻辑,视图负责数据的展示。同时,Web-MVC可与Spring框架的其他模块方便地集成。

Web-Socket

Web-Socket模块提供了对WebSocket-Base的支持,用于实现在Web应用程序中服务端和客户端实时双向通信,尤其在实时消息推送中应用广泛。

Web-Portlet

Web-Portlet模块提供了基于Portlet环境的MVC实现,并提供了与Spring Web-MVC模块相关的功能。

其他重要模块

除了上述介绍的模块,Spring还有其他一些重要的模块,例如,AOP、Aspects、Instrumentation、Messaging和Test等。

AOP

AOP模块提供了面向切面的编程实现,允许应用程序通过定义方法拦截器和切入点来实现系统功能和业务功能之间的解耦。

Aspects

Aspects模块提供了Spring与AspectJ的集成,是一个面向切面编程的模块。

Instrumentation

Instrumentation模块在应用中提供了对Instrumentation的支持和类加载器的实现。

Messaging

Messaging模块为STOMP(Simple Text Orientated Messaging Protocol,简单文本定向消息协议)提供了支持,主要用于应用程序中WebSocket子协议的实现。同时,Messaging模块可通过注解的方式来选择和处理来自WebSocket客户端的STOMP消息。

Test

Test(测试)模块用于对JUnit或TestNG等测试框架提供支持,以实现Spring代码的自动化测试。

Spring的核心JAR包

Spring基于模块化实现,每个模块都对应不同的JAR包。要全面了解Spring,首先应从JAR包开始,但是Spring JAR包众多,每个包中又都含有复杂的技术原理,鉴于篇幅原因,表1-2对常用的JAR包和功能做了简单介绍,以便读者能够整体了解Spring的核心技术架构。

Spring的注解

Spring的注解将应用程序中Bean的定义和Bean之间复杂的依赖关系的配置从XML配置中解放出来,应用程序只需在需要某些服务或者功能时,使用注解依赖注入即可,具体Bean的定义和依赖关系由Spring的自动装配来完成。这使得Spring的使用更加方便。

Spring注解的使用

Spring注解的使用很简单,首先导入命名规范,然后指定IoC的扫描包,最后在Java类中直接使用注解依赖注入需要的资源即可。

导入命名空间及规范

在Spring的applicationContext.xml配置文件中导入命名空间及规范。代码如下。

xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd"

配置扫描包

在applicationContext.xml配置文件中配置需要扫描的包。如下代码开启了自动扫描com.alex.spring包下的所有类,也就是说,只有在com.alex.spring包中的注解才能够生效。

<!-- 指定Spring IoC容器扫描的包 -->
<context:component-scan base-package="com.alex.spring">
</context:component-scan>

完整的Spring注解配置文件如下。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd">
	<!-- 配置扫描包,自动扫描代码中的注解 -->
	<context:component-scan base-package="com.alex.spring" />
</beans>

使用注解

使用注解比较简单,直接在Java类中用@按需使用即可。

//使用@Controller注解声明一个控制器
@Controller
public class RESTController{
	//使用@Autowired注解依赖注入
	@Autowired
	RestTemplate rest;
	//使用注解声明一个服务,服务地址为rest
	@RequestMapping(value="/rest")
	public String rest(){
		return "rest";
	}
}

Spring的常用注解!

Spring IoC的原理

Spring IoC简介

Spring通过一个配置文件描述Bean和Bean之间的依赖关系,利用Java的反射功能实例化Bean并建立Bean之间的依赖关系。Spring的IoC容器在完成这些底层工作的基础上,还提供了Bean实例缓存管理Bean生命周期管理Bean实例代理事件发布资源装载等高级服务。

Spring Bean的装配流程

Spring在启动时会从XML配置文件或注解中读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表;然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层业务提供基础的运行环境。其中Bean缓存池为HashMap实现。Spring Bean的装配流程如图。

Spring Bean的作用域

Spring为Bean定义了5种作用域,分别为Singleton(单例)Prototype(原型)Request(请求级别)Session(会话级别)Global Session(全局会话)

  • Singleton是单例模式,当实例类型为单例模式时,Spring IoC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,都始终指向同一个Bean对象。该模式在多线程下是不安全的。Singleton作用域是Spring中的默认作用域,也可以通过配置将Bean定义为Singleton模式,具体配置如下。
<bean id="userDao" class="com.alex.userDaoImpl" scope="singleton"/>
  • Prototype是原型模式,每次通过Spring容器获取Prototype定义的Bean时,容器都将创建一个新的Bean实例,每个Bean实例都有自己的属性和状态,而Singleton全局只有一个对象。因此,对有状态的Bean经常使用Prototype作用域,而对无状态的Bean则使用Singleton作用域。具体配置如下。
<bean id="userDao" class="com.alex.userDaoImpl" scope="prototype"/>
  • Request指在一次HTTP请求中容器会返回该Bean的同一个实例,而对不同的HTTP请求则会创建新的Bean实例,并且该Bean实例仅在当前HTTP请求内有效,当前HTTP请求结束后,该Bean实例也将会随之被销毁。具体配置如下。
<bean id="userDao" class="com.alex.userDaoImpl" scope="request"/>
  • Session指在一次HTTP Session中容器会返回该Bean的同一个实例,而对不同的Session请求则会创建新的Bean实例,该Bean实例仅在当前Session内有效。和HTTP请求相同,每一次Session都会创建新的Bean实例,而不同的Bean实例之间不共享数据,且Bean实例仅在自己的Session内有效,请求结束,则Bean实例将随之被销毁。具体配置如下。
<bean id="userDao" class="com.alex.userDaoImpl" scope="session"/>
  • Global Session指在一个全局的HTTP Session中容器会返回该Bean的同一个实例,且仅在使用Portlet Context时有效。

Spring Bean的生命周期

  1. 实例化一个Bean。
  2. 按照Spring上下文对实例化的Bean进行配置。
  3. 如果这个Bean实现了BeanNameAware接口,则会调用它实现的setBeanName(String)方法,该方法传递的参数是Spring配置文件中Bean的id值。
  4. 如果这个Bean实现了BeanFactoryAware接口,则会调用它实现的setBeanFactory(BeanFactory)方法,该方法传递的参数是Spring工厂自身。
  5. 如果这个Bean实现了ApplicationContextAware接口,则会调用setApplication Context(ApplicationContext)方法,该方法传入的参数是Spring上下文。
  6. 如果该Bean关联了BeanPostProcessor接口,则会调用postProcessBefore Initialization(Object obj,String s)方法,该方法在Bean初始化前调用,常用于定义初始化Bean的前置工作,比如系统缓存的初始化。
  7. 如果Bean在Spring配置文件中配置了init-method属性,则会自动调用其配置的初始化方法
  8. 如果某个Bean关联了BeanPostProcessor接口,将会调用postProcessAfter Initialization(Object obj,String s)方法。至此,Bean的初始化工作就完成了,应用程序就可以开始使用Bean实例了。
  9. 当Bean不再被需要时,会在清理阶段被清理掉。如果Bean实现了DisposableBean接口,则Spring会在退出前调用实现类的destroy()方法。
  10. 如果某个Bean的Spring配置文件中配置了destroy-method属性,在Bean被销毁前会自动调用其配置的销毁方法。

Spring的4种依赖注入

  1. 构造器注入
    构造器注入指通过在类的构造函数中注入属性或对象来实现依赖注入。如下代码通过<bean></bean>标签配置了一个id为persionDaoImpl的Bean,并通过<constructor-arg></constructor-arg>标签在其构造函数中注入了一个message属性,注入完成后在类中可以直接通过this.message获取注入的属性值。
//在构造函数中注入message属性
public PersionDaoImpl(String message){
	this.message = message;
}
<!--定义Bean实例并在构造函数constructor-arg中注入message属性-->
<bean id="persionDaoImpl" class="com.PersionDaoImpl">
	<constructor-arg value="message"></constructor-arg>
</bean>
  1. set方法注入
    set方法注入是通过在类中实现get、set方法来实现属性或对象的依赖注入的。如下代码通过<bean></bean>标签配置了一个id为persionDaoImpl的Bean,并通过<property></property>标签在Bean中注入了一个id为123的属性值,注入完成后在类中可以直接使用getId()获取注入的属性。
//在构造函数中注入message属性
public class PersionDaoImpl(String message){
	private int id;
	public int getId(){return id;}
	public void setId(int id) {this.id=id}
}
<!--定义Bean实例并通过property注入id为123的属性值-->
<bean id="persionDaoImpl" class="com.PersionDaoImpl">
	<property name="id" value="123"></property>
</bean>
  1. 静态工厂注入
    静态工厂注入是通过调用工厂类中定义的静态方法来获取需要的对象的,为了让Spring管理所有对象,应用程序不能直接通过“工厂类.静态方法()”的方式获取对象,而需要通过Spring注入的方式获取。代码如下。
public class DaoFactory{
	//1.定义静态工厂
	public static final FactoryDao getStaticFactoryDaoImpl(){
		return new StaticFactoryDaoImpl();
	}
	public SpringAction{
		//2.定义工厂对象
		private FactoryDao staticFactoryDao;
		//3.注入工厂对象
		public void setStaticFactoryDaoImpl(FactoryDao staticFactoryDao){
			this.staticFactoryDao = staticFactoryDao;
		}
	}
}

上述代码定义了一个DaoFactory工厂类和getStaticFactoryDaoImpl()静态工厂方法,该方法实例化并返回一个StaticFactoryDaoImpl实例;同时定义了一个SpringAction类,并通过setStaticFactoryDao获取注入的FactoryDao。具体的XML注入语法如下。

<!--定义获取工厂对象的静态方法-->
<bean name="staticFactoryDao" class="DaoFactory"
	factory-method="getStaticFactoryDaoImpl"></bean>
	<!--factory-method用于指定调用哪个工厂方法-->
<bean name="springAction" class="SpringAction">
	<property name="StaticFactoryDao" ref="staticFactoryDao"></property >
</bean>

上述代码定义了一个name为staticFactoryDao的工厂类,并通过factory-method定义了实例化对象的方法,这里实例化对象的方法是一个名为getStaticFactoryDaoImpl的静态方法。该静态方法返回一个工厂类实例,在springAction中通过标签注入静态工厂实例。

  1. 实例工厂注入
    实例工厂注入指的是获取对象实例的方法是非静态的,因此首先需要实例化一个工厂类对象,然后调用对象的实例化方法来实例化对象。具体代码如下。
public class DaoFactory{
	//1.实例工厂
	public FactoryDao getStaticFactoryDaoImpl(){
		return new FactoryDaoImpl();
	}
	public SpringAction{
		//2.注入对象
		private FactoryDao factoryDao;
		public void setFactoryDaoImpl(FactoryDao factoryDao){
			this.factoryDao= factoryDao;
		}
	}
}
<bean name="springAction" class="SpringAction">
	<!--使用实例工厂的方法注入对象-->
	<property name="factoryDao" ref="factoryDao"></property >
</bean>
<!--获取对象的方式是从工厂类中获取实例-->
<bean name="daoFactory" class="com.DaoFactory"></bean>
<bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImple">
</bean>

上述代码定义了一个name为factoryDao的工厂类,并通过factory-method定义了实例化对象的方法,这里实例化对象的方法是一个名为getFactoryDaoImpl的方法。该方法返回一个工厂类,在springAction中通过标签注入工厂实例。

自动装配的5种方式

Spring的装配方式包括手动装配和自动装配。手动装配包括基于XML装配(构造方法、set方法等)和基于注解装配2种方式。自动装配包括5种装配方式,这5种方式均可以用来引导Spring容器自动完成依赖注入,具体如下。

  1. no:关闭自动装配,通过显式设置ref属性来进行对象装配。
  2. byName:通过参数名自动装配,Bean的autowire被设置为byName后,Spring容器试图匹配并装配与该Bean的属性具有相同名字的Bean。
  3. byType:通过参数类型自动装配,Bean的autowire被设置为byType后,Spring容器试图匹配并装配与该Bean的属性具有相同类型的Bean。
  4. constructor:通过设置构造器参数的方式来装配对象,如果没有匹配到带参数的构造器参数类型,则Spring会抛出异常。
  5. autodetect:首先尝试使用constructor来自动装配,如果无法完成自动装配,则使用byType方式进行装配。

Spring AOP的原理

Spring AOP简介

Spring AOP通过面向切面技术将与业务无关却为业务模块所共用的逻辑代码封装起来,以提高代码的复用率,降低模块之间的耦合度。

Spring AOP将应用分为核心关注点横切关注点两个部分。业务处理流程为核心关注点,被业务所依赖的公共部分为横切关注点。横切关注点的特点是其行为经常发生在核心关注点的多处,而多处操作基本相似,比如权限认证、日志、事务AOP的核心思想是将核心关注点和横切关注点分离开来,以降低模块耦合度。Spring AOP的主要应用场景如所示。

AOP的核心概念

  • 横切关注点:定义对哪些方法进行拦截,拦截后执行哪些操作。
  • 切面(Aspect):横切关注点的抽象。
  • 连接点(Joinpoint):在Spring中,连接点指被拦截到的方法,但是从广义上来说,连接点还可以是字段或者构造器。
  • 切入点(Pointcut):对连接点进行拦截的定义。
  • 通知(Advice):拦截到连接点之后要执行的具体操作,通知分为前置通知、后置通知、成功通知、异常通知和环绕通知5类。
  • 目标对象:代理的目标对象。
  • 织入(Weave):将切面应用到目标对象并执行代理对象创建的过程。
  • 引入(Introduction):在运行期为类动态地添加一些方法或字段而不用修改类的代码。

AOP的2种代理方式

Spring提供了JDK和CGLib 2种方式来生成代理对象,具体生成代理对象的方式由AopProxyFactory根据AdvisedSupport对象的配置来决定。Spring默认的代理对象生成策略为:如果是目标类接口,则使用JDK动态代理技术,否则使用CGLib动态代理技术

  • JDK动态代理:JDK动态代理主要通过java.lang.reflect包中Proxy类和InvocationHandler接口来实现。InvocationHandler是一个接口,不同的实现类定义不同的横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编制在一起。Proxy类利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。JDK 1.8中Proxy类的定义如下。
  • CGLib动态代理:CGLib即Code Generation Library,它是一个高性能的代码生成类库,可以在运行期间扩展Java类和实现Java接口。CGLib包的底层通过字节码处理框架ASM来实现,通过转换字节码生成新的类。

  • CGLib动态代理和JDK动态代理的区别:JDK只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则只能通过CGLib创建动态代理来实现。

AOP的5种通知类型

AOP的代码实现

在Spring中,AOP的使用比较简单,如下代码通过@Aspect注解声明一个切面,通过@Pointcut定义需要拦截的方法,然后用@Before、@AfterReturning、@Around分别实现前置通知、后置通知和环绕通知要执行的方法。

Spring MVC的原理

Spring MVC中的MVC即模型-视图-控制器,该框架围绕一个DispatcherServlet设计,DispatcherServlet会把请求分发给各个处理器,并支持可配置的处理器映射和视图渲染等功能。Spring MVC的工作流程如图所示。

  1. 客户端发起HTTP请求:客户端将请求提交到DispatcherServlet。
  2. 寻找处理器:由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理该请求的Controller。
  3. 调用处理器:DispatcherServlet将请求提交到Controller。
  4. 调用业务处理逻辑并返回结果:Controller调用业务处理逻辑后,返回ModelAndView。
  5. 处理视图映射并返回模型:DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图。
  6. HTTP响应:视图负责将结果在客户端浏览器上渲染和展示。

事务

事务是Web应用中不可缺少的组件模型,它保证了用户操作的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durabilily)。事务分本地事务和分布式事务两种。

本地事务

本地事务基于数据库资源实现,事务串行地在JDBC连接上执行,本地事务将事务处理局限在当前事务资源内。其特点是使用灵活但无法支持多数据源事务操作。在数据库连接中使用本地事务的代码示例如下。

在上述代码中,首先通过**conn.setAutoCommit(false)设置数据库连接为非自动提交,然后分别提交了两条更新语句,最后通过conn.commit()提交事务。如果数据库操作成功,则事务完成;如果操作失败,则通过conn.rollback()**回滚事务。

分布式事务

分布式事务(Distributed Transaction)提供了跨数据库的分布式事务操作的数据一致性,跨数据库的一致性包含同一类型数据库的多个数据库实例服务的一致性(例如多个MySQL的事务一致性)和多个不同类型数据库的数据一致性(例如MySQL和Oracle之间的事务一致性)两种情况。

Java事务编程接口(Java Transaction API,JTA)和Java事务服务(Java Transaction Service,JTS)为J2EE平台提供了分布式事务服务。分布式事务包括一个事务管理器(TransactionManager)和一个或多个支持XA协议(XA协议是由X/Open组织提出的分布式事务的规范,XA规范主要定义了事务管理器和资源管理器之间的接口)的资源管理器(Resource Manager)。其中,事务管理器负责所有事务参与单元的协调与控制,资源管理器负责不同的数据库具体的事务执行操作。具体使用代码如下。

在上述代码中,首先定义了一个分布式事务管理器UserTransaction,然后定义了2个连接池connA和connB,接着通过**userTx.begin()启动事务并向两个数据库连接提交2个更新请求,最后通过userTx.commit()统一提交事务。如果执行成功,则事务完成;如果失败,则通过userTx.rollback()**回滚事务。

两阶段提交协议

两阶段提交协议用于保证分布式事务的原子性,即所有数据库节点要么全部都执行要么全部不执行,其执行过程主要分为两个阶段:第一阶段为准备阶段,第二阶段为提交阶段,数据库两阶段提交协议如图所示。

  1. 准备阶段:事务管理器(事务协调者)为每个资源管理器(事务参与者)都发送Prepare消息,每个参与者要么都直接返回失败(例如权限验证失败),要么都在本地执行事务,写本地的redo和undo日志但是不提交,以达到“万事俱备,只欠东风”的状态。
  2. 提交阶段:如果事务协调者收到了某个事务参与者的事务操作失败回复或等待事务参与者反馈超时,则事务协调者为每个事务参与者发送回滚(Rollback)消息,事务参与者再执行回滚本地事务操作;如果事务协调者在规定时间内收到了所有事务参与者的成功回复,则发送提交(Commit)消息,事务参与者在本地执行事务提交操作并执行事务。

两阶段提交的目的是尽可能晚地提交事务,在提交事务前尽可能确保每个数据库都正常运行,并确保该事务对应的SQL语句能够在每个数据库上都正常执行,同时将数据的写日志操作提前完成,防止事务执行过程中发生意外。事务的执行和释放是在JPA中基于锁来实现控制的。

MyBatis的缓存

MyBatis的缓存分为一级缓存和二级缓存,如图所示。在默认情况下,一级缓存是开启的,而且不能被关闭。

  • 一级缓存:指SqlSession级别的缓存,当在同一个SqlSession中执行相同的SQL语句查询时将查询结果集缓存,第二次以后的查询不会从数据库中查询,而是直接从缓存中获取,一级缓存最多能缓存1024条SQL语句
  • 二级缓存:指跨SqlSession的缓存,即Mapper级别的缓存。在Mapper级别的缓存内,不同的SqlSession缓存可以共享。

MyBatis的一级缓存原理

当客户端第一次发出一个SQL查询语句时,MyBatis执行SQL查询并将查询结果写入SqlSession的一级缓存,当第二次有相同的SQL查询语句时,则直接从缓存中获取数据,具体过程如前面的图所示。在缓存中使用的数据结构是Map,其中,Key为MapperId+Offset+Limit+SQL+所有的入参。

当同一个SqlSession多次发出相同的SQL查询语句时,MyBatis直接从缓存中获取数据。如果两次查询中间出现Commit操作(修改、添加、删除),则认为数据发生了变化,MyBatis会把该SqlSession中的一级缓存区域全部清空,当下次再到缓存中查询时将找不到对应的缓存数据,因此要再次从数据库中查询数据并将查询的结果写入缓存。

MyBatis的二级缓存原理

MyBatis二级缓存的范围是Mapper级别的,Mapper以命名空间为单位创建缓存数据结构,数据结构是Map类型,Map中Key为MapperId+Offset+Limit+SQL+所有的入参。MyBatis的二级缓存是通过CacheExecutor实现的。CacheExecutor是Executor的代理对象。当MyBatis接收到用户的查询请求时,首先会根据Map的Key在CacheExecutor缓存中查询数据是否存在,如果不存在则在数据库中查询。

开启二级缓存需要做以下配置:

  1. 在MyBatis全局配置中启用二级缓存配置。
  2. 在对应的Mapper.xml中配置Cache节点。
  3. 在对应的Select查询节点中添加useCache=true。

Spring的生态

Spring是一个开放的平台,在其上可以集成其他组件的功能。下面简单地介绍Spring常用的组件,方便读者根据不同的项目需求选择不同的组件。

Spring Data

Spring Data为数据访问提供一个统一的、一致的、基于Spring的编程模型,同时保留底层数据存储的特殊性。

Spring Data提供了对关系型数据库、非关系型数据库、MapReduce框架和云数据服务的支持,使数据访问变得更简单高效。Spring Data为综合项目,其下包含多个针对特定数据库的子项目。

Spring Data的特性

  • 良好的数据库存储资源和对象映射封装。
  • 针对不同的存储资源提供灵活的查询。
  • 提供数据库字段和实体类的映射。
  • 支持透明数据审查(新增数据、最后一次修改数据)。
  • 可集成自定义数据库存储。
  • 通过JavaConfig和自定义XML名称空间轻松集成Spring。
  • 与Spring MVC控制器的高级集成。
  • 支持跨库存储。

Spring Data的主项目

Spring Data有多个子项目,根据项目的维护组织不同,可分成Spring官方维护的主项目和各个社区维护的社区项目。Spring官方维护的主项目如表所示。

Spring Data的社区项目

Spring Data的社区项目指各个数据库社区为推广数据库而自行提供的Spring Data支持,由各个社区自己维护,具体如表所示。

Spring的其他服务

Spring还提供了其他优秀的框架,由于篇幅原因,这里不展开详细描述,罗列出了其他优秀的Spring服务组件。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值