SSM框架中项目核心层:Spring
目录SpringSpring简介Ioc控制反转Ioc的概念Spring的配置文件Spring容器创建对象的特点DI(依赖注入)基于注解的DISpringSpring框架由Rod Johnson开发,2004年发布了Spring框架的第一版。Spring是一个从实际开发中抽取出来的框架,因此它完成了大量开发中的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大提高了企业应用的开发效率。Spr
目录
Spring
Spring框架由Rod Johnson开发,2004年发布了Spring框架的第一版。Spring是一个从实际开发中抽取出来的框架,因此它完成了大量开发中的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大提高了企业应用的开发效率。
SSM-MyBatis篇:SSM框架中项目持久层:MyBatisSSM-SpringMVC篇:SSM框架中项目逻辑层:SpringMVCMaven:Java项目管理神器:Maven
Spring简介
作用:为代码“解耦合”,降低代码间的耦合度(符合软件工程中的高内聚、低耦合的特点),让对象和对象(模块和模块)之间关系不是通过代码关联,而是通过配置来说明。
Spring根据代码的功能特点,使用Ioc降低业务对象之间耦合度,Ioc使得主业务在相互调用的过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了,而是由Spring容器统一管理,自动注入,即赋值。而AOP使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由Spring容器统一完成织入。
官网:
Spring安装包:- spring-framework-5.2.5.RELEASE - docs # 相关文档 - javadoc-api # 相关api - kdoc-api - spring-framework-reference # 开发手册 - libs # 源码 - spring-model-5.2.5.RELEASE.jar # 相关模块的字节码.class文件 - spring-model-5.2.5.RELEASE-javadoc.jar # 相关模块的开发文档 - spring-model-5.2.5.RELEASE-sources.jar # 相关模块的源文件.java - schema # 相关模块的约束文件
Spring优点
- 轻量
- 针对接口编程、解耦合
AOP编程的支持- 方便集成各种优秀框架
Ioc控制反转
Ioc的概念
概念:Ioc,Inversion of Control,控制反转,是一个理论,把对象的创建、属性赋值,对象的声明周期都交给代码以外的容器管理。
Ioc分为控制和反转
- 控制:对象创建、属性赋值,对象的声明周期
- 反转:把开发人员管理对象的权限转移给了代码之外的容器实现,由容器完成对象的管理。
- 正转:开发人员在代码中,使用
new构造方法创建对象。
Ioc的实现
- 依赖注入:
DI,Dependency Injection,程序只需要提供要使用的对象的名称即可,对象如何创建,如何从容器中查找,获取都由容器内部自己实现
Spring的配置文件
<!-- xxx.xml(spring) -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 声明对象
id:自定义对象名称,唯一值(可没有。spring提供默认的名称)
class:类的全限定名称,spring通过反射机制创建对象,不能是接口
spring根据id和class创建对象,把对象放入到spring的一个map之中
map.put(id, object)
-->
<bean id="someService" class="org.bjpowernode.service.impl.SomeServiceImpl"/>
</beans>
Spring的标准配置文件
- 根标签是
beansbeans后面是约束文件说明,beans里面是bean声明bean是java对象,Spring容器创建的java对象
创建对象并调用接口方法
package org.bjpowernode;
import org.bjpowernode.service.SomeService;
import org.bjpowernode.service.impl.SomeServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppMain {
public static void main(String[] args) {
/* 一般创建对象格式 */
// SomeService service = new SomeServiceImpl(); Impl一般指接口实现类
// service.doSome();
/* 指定spring的配置文件
* 从类路径(classpath)之下开始的路径,一般是resources
* */
String config = "beans.xml";
// 创建容器对象, ApplicationContext表示spring容器对象,通过ctx获取某个Java对象
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
// 从容器中获取指定名称的对象,使用getBean("id")
SomeService service = (SomeService)ctx.getBean("someService");
// 调用对象的方法,接口中的方法
service.doSome();
}
}
Spring容器创建对象的特点
Spring创建对象特点:
Spring创建对象调用的构造函数默认是无参构造,如果定义了有参构造,则再定义无参构造.创建
Spring容器对象的时候,会读取配置文件,创建文件中声明所有的的Java对象,即new ClassPathXmlApplicationContext(config);
优点:获取对象的速度快,因为对象已经创建好了
缺点:占用内存
String对象存在Spring内部的map中,并从map进行获取对象(可以调用ApplicationContext接口里面的方法试一试)
创建非自定义类对象
<beans>
<bean id="date" class="java.util.Date"/>
</beans>
DI注入
基于XML的DI
Spring调用类的五参数构造方法,创建对象,对象创建后给属性赋值
- 使用
xml配置文件中的标签和属性 - 使用注解
DI分类:
set注入,设值注入- 构造注入
set注入,简单类型注入(简单的数据类型和String)
<beans>
<!--
set注入,Spring调用类的set方法(必须要有set方法),完成赋值
<property name="名称" value="值" />
-->
<bean id="myStudent" class="com.bjpowernode.ba01.Student">
<property name="name" value="lh"/>
<property name="age" value="12"/>
</bean>
</beans>
注入过程:按照前调用无参构造的原则,其后调用
setxx方法进行赋值(和属性名无关,无关是不是类的属性),而非直接通过有参构造进行赋值给非自定义类同理
set注入,引用类型注入
<beans>
<!--
set注入
<property name="名称" ref="beanid" />
-->
<bean id="myst" class="com.bjpowernode.ba01.Student">
<property name="name" value="lh"/>
<property name="age" value="12"/>
<property name="sl" ref="mysl"/>
</bean>
<bean id="mysl" class="com.bjpowernode.ba01.School">
<property name="name" value="yc"/>
<property name="age" value="32"/>
</bean>
</beans>
构造注入:Spring调用类中的有参构造方法,在创建对象的同时,给属性赋值
<beans>
<!--
<constructor-arg>:表示一个构造方法的形参
标签属性:name:构造方法形参名
index:构造方法的参数位置T
value:简单类型的形参值
ref:引用类型的形参值
-->
<!-- name -->
<bean id="myst" class="com.bjpowernode.ba01.Student">
<constructor-arg name="name" value="pl"/>
<constructor-arg name="age" value="234"/>
<constructor-arg name="sl" ref="mysl"/>
</bean>
<!-- index -->
<bean id="myst" class="com.bjpowernode.ba01.Student">
<constructor-arg index="0" value="pl"/>
<constructor-arg index="1" value="234"/>
<constructor-arg index="2" ref="mysl"/>
</bean>
<!-- 省略index -->
<bean id="mysl" class="com.bjpowernode.ba01.School">
<property name="name" value="yc"/>
<property name="age" value="32"/>
</bean>
</beans>
引用类型自动注入:Spring可以根据某些规则给引用类型完成赋值,只对引用类型有效,规则byName和byType
byName:按名称注入,Java类中引用类型属性名称和Spring容器中bean的id一样,且数据类型也一样,这样的bean能够赋值给引用类型。byType:按类型注入,Java类中引用类型的数据类型和Spring容器中bean的class同源关系,则可以赋值给引用类型。
byName规则
<beans>
<bean id="st" class="com.bjpowernode.ba01.Student" autowire="byName">
<property name="name" value="lh"/>
<property name="age" value="123"/>
</bean>
<!-- 此处的id要和引用该类型的类中的引用数据名称相同 -->
<bean id="sl" class="com.bjpowernode.ba01.School">
<property name="name" value="yc"/>
<property name="age" value="231"/>
</bean>
</beans>
byType规则(只能有一个,具有唯一性)
同源关系
java中引用类型的数据类型和bean的class值是一样的。java中引用类型的数据类型(父)和bean的class(子)值是父子类关系的。java中引用类型的数据类型和bean的class值是接口和实现类关系的。
<beans>
<bean id="st" class="com.bjpowernode.ba01.Student" autowire="byType">
<property name="name" value="lh"/>
<property name="age" value="123"/>
</bean>
<bean id="mysl" class="com.bjpowernode.ba01.School">
<property name="name" value="yc"/>
<property name="age" value="231"/>
</bean>
</beans>
多个配置文件:在实际应用里,随着应用规模的增加,系统中Bean数量也大量增加口,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将Spring配置文件分解成多个配置文件。
- 按功能模块分,-个模块一个配置文件。
- 按类的功能分,数据库操作相关的类在一个文件,配置
redis, 事务等等的一个配置文件。
Spring管理多个配置文件:常用的是包含关系的配置文件,项目中有一个总的文件,里面是有import标签包含其他的配置文件
<!--
classpath:表示类路径。类文件所在的目录,spring通过 类路径加载配置文件
-->
<import reource="classpath:/其他文件路径1"/>
<import reource="classpath:/其他文件路径2"/> <!-- 或者不加/-->
使用通配符*
<!--
不同包含总配置文件本身
-->
<import reource="classpath:/其他文件路径_*"/>
基于注解的DI
概念:使用Spring提供的注解,完成Java对象创建,属性赋值
Component:需要在类上使用注解@Component,该注解的value属性用于指定该bean的id值
@Component(value="myTest") /* 如果不加默认是类首字母小写的类名 */
class Test {
// do something
}
<beans>
<!--
component-scan 组件扫描器,组件是Java对象
base-package 注解在项目中的包名,框架扫描这个包和子包中的所有类,找类中的所有注解
遇到注解后,按照注解表示的功能,去创建对象,给属性赋值
-->
<context:component-scan base-package="com.bjpowernode.ba01"/>
</beans>
利@Component功能相同的创建对象的注解。
@Repository:放在dao接口的实现类上面,表示创建dao对象,持久层对象,能访问数据库@Service:放在业务层接口的实现类上面,表示创建业务层对象,业务层对象有事务的功能@Controller:放在控制器类的上面,表示创建控制器对象。属于表示层对象。控制器对象能接受请求,把请求的处理结果显示给用户。
以上四个注解都能创建对象,但是@Repository @Service @Controller有角色说明,表示对象是分层的。对象是属于不能层的,具有额外的功能。
扫描多个包的三种方式
<beans>
<!-- 多次使用组件扫描器 -->
<context:component-scan base-package="com.bjpowernode.ba01"/>
<context:component-scan base-package="com.bjpowernode.ba02"/>
<!-- 使用分隔符 -->
<context:component-scan base-package="com.bjpowernode.ba01;com.bjpowernode.ba02"/>
<!-- 指定父包 -->
<context:component-scan base-package="com.bjpowernode"/>
</beans>
注解指定属性值的方式
- 简单类型
value - 引用类型
简单类型value
放置位置:
- 在属性定义的上面,无需
set方法,推荐使用 - 在
set方法的上面
@Component(value = "MyStudent")
public class Student {
@Value(value = "lh")
private String name;
@Value(value = "12")
private int age;
// do something
}
value通过外部配置属性文件获得
// .xml
<!-- 读取外部属性配置文件.properties -->
<context:property-placeholder location="classpath:/myconf.properties"/>
// Student.java
@Component(value = "MyStudent")
public class Student {
// @Value("${key}")
@Value("${myname}")
private String name;
@Value("${myage}")
private int age;
// do something
}
# myconf.properties
myname=lh
myage=23
// app.java
package com.bjpowernode;
import com.bjpowernode.ba01.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void Test() {
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
Student st = (Student) ctx.getBean("MyStudent");
System.out.println(st);
}
}
引用类型
放置位置:
- 在属性定义的上面,无需
set方法,推荐使用 - 在
set方法的上面
@Component(value = "MyStudent")
public class Student {
@Value("${myname}")
private String name;
@Value("${myage}")
private int age;
/**
* 引用类型
* @Autowired: spring框架提供的,给引用类型赋值的,使用自动注入的原理
* 支持byType和byName,默认是byType
*/
@Autowired()
private School sl; // 提示:school必须在student赋值前先赋值
}
使用byName方法(唯一性)
// School.java
@Component("MySchool")
public class School {
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Value("${lname}")
private String name;
@Value("${lage}")
private int age;
}
// Student.java
@Component(value = "MyStudent")
public class Student {
// 此处与Qualifier一起用
/** @Autowired: 给引用类型赋值
* @Qualifier(value="bean id")
*/
@Autowired
@Qualifier(value = "MySchool")
private School sl;
}
Autowired(required):required默认是true
true:spring在启动的时候,创建容器对象时候,会检查引用类型是否赋值成功,如果赋值失败,终止程序执行, 并报错。
false:引用类型赋值失败,程序正常执行,不报错。引用类型的值是null。
另一种引用注解的方式:@Resource,为JDK提供,此处不作讲解。
AOP面向切面编程
AOP的概念
概念:AOP,Aspect Orient Programming,面向切面编程。
Aspect:表示切面,给业务方法增加的功能,叫做切面,一般是非业务功能,可复用,例如日志功能,事务功能,权限检查,参数检查,统计检查。Orient:面向,对着Programming:编程
AOP的作用
-
让切面编程复用
-
让开发人员专注业务逻辑,提高开发效率
-
实现业务功能和其他业务功能解耦合
-
给存在的业务方法,增加功能,不用修改原来的代码
AOP的术语
Aspect:表示切面,给业务方法增加的功能JoinPoint:连接点,连接切面的业务方法,在这个业务方法执行时,会同时执行切面的功能PointCut:切入点,是一个或多个连接点集合,表示这些方法执行时,都能增加切面的功能target:目标对象,给那个对象增加切面的功能,这个对象就是目标对象Advice:通知(增强),表示切面执行时间,在目标方法之前执行切面,还是目标方法之后执行切面
AOP三个最主要的要素:Aspect,PointCut,Advice,这个概念是在Advice时间,在PointCut的位置,执行Aspect。
AOP是一个动态的思想,在程序运行期间,创建代理(ServiceProxy),使用代理执行方法时,增加切面的功能,这个代理对象是存在内存中的。
AOP技术思想的实现
使用框架实现AOP,框架有很多,著名的有两个
Spring:Spring框架实现AOP思想中的部分功能。Spring框架实现AOP的操作比较繁琐、比重。Apsectj:独立的框架,专门是AOP,属于Eclipse。
Apsectj官网:
Apsectj
Apsectj可以通过xml和注解两种方式实现AOP
通知:Advice
@Before:前置通知@AfterReturnning:后置通知,在目标方法结束后触发@Around:环绕通知@AfterThrowing:异常通知@After:最终通知
位置:PointCut
表达式:execution(方法的定义)
execution(访问权限类型(可省略) 返回值类型 包名类名.方法名(参数类型和参数个数) 抛出异常类型(可省略))
可使用通配符
符号 意义 *0至多个任意字符 ··用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包路径 +用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类
举例
// 切面类
package com.bjpowernode.handle;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 切面类的注解,表示当前类是一个切面类
*/
@Aspect
public class MyAspect {
@Before(value = "execution(" +
"public " +
"void " +
"com.bjpowernode.service.impl.SomeServiceImpl.doSome(String, Integer))")
public void myBefore() {
System.out.println("time" + new Date());
}
}
// 接口
package com.bjpowernode.service;
public interface SomeService {
void doSome(String name, Integer age);
}
// 实现类
package com.bjpowernode.service.impl;
import com.bjpowernode.service.SomeService;
import org.springframework.stereotype.Component;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name, Integer age) {
System.out.println("do somehtin");
}
}
<!-- 配置文件 -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="myStudent" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
<bean id="MyAspect" class="com.bjpowernode.handle.MyAspect"/>
<!-- 声明自动代理生成器:目的是创建目标对象的代理
调用aspectj框架中的功能,寻找spring容器中的所有目标对象,
把每个目标对象加入切面类中的功能,生成代理。
这个代理对象是修改的内存中的目标对象,这 个目标对象就是代理对象(ServiceProxy)
-->
<aop:aspectj-autoproxy />
</beans>
// 主函数
package com.bjpowernode;
import com.bjpowernode.service.SomeService;
import com.bjpowernode.service.impl.SomeServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void Test() {
String config = "applicationContext.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
SomeService service = (SomeService) ctx.getBean("myStudent");
service.doSome("lh", 20);
}
}
# 结果输出
timeSun Feb 06 19:25:54 CST 2022 # Before前置切面,完成
do somehtin
此时
xml会创建三个对象,一个是实现类对象,一个是切面类对象,一个是代理切面类对象# 实现类对象 myStudent # 切面类对象 MyAspect # 代理切面类对象 org.springframework.aop.config.internalAutoProxyCreator
获取执行切面方法的方法的信息
@Aspect
public class MyAspect {
@AfterReturning(value = "execution(" +
"public " +
"void " +
"com.bjpowernode.service.impl.SomeServiceImpl*.doSome(String, Integer))")
public void myBefore(JoinPoint js) {
System.out.println(js.getSignature());
System.out.println("time" + new Date());
}
}
JoinPoint不管之后加什么参数,该参数都放在第一个位置其他此类和接口方法可自行看源码
@AfterReturnning:此方法比较特殊,因为是在方法执行之后执行,所以其可以获取方法的返回结果retunning=自定义的变量,表示目标方法的返回值的,自定义变量名称必须和通知方法的形参名一-样@AfterReturnning(value = "", returning="res") public void mytest(Object res) { // }
环绕通知:@Around。其他通知是相当于一个配件,该通知相当于一个容器,内部包含方法执行
//环绕置通知方法的定义
//1)方法是public
//2)方法是必须有返回值,推荐使用object类型
//3)方法名称自定义
//4)方法必须有ProceedingJoinPoint参数,是用来执行方法的
@Aspect
public class MyAspect {
@Around(value = "" +
"execution(public void com.bjpowernode.service.impl.SomeServiceImpl2.doSome(..))")
public Object fd(ProceedingJoinPoint pp) throws Throwable {
// 执行目标方法
Object rr = null;
rr = pp.proceed();
return rr;
}
}
辅助注解:@Pointcut
/**
@Pointcut:定义和管理切入点,不是通知注解。
属性: value 切入点表达式
位置: 在一个自定义方法的上面, 这个方法看做是切入点表达式的别名。
其他的通知注解中,可以使用方法名称,就表示使用这个切入点表达式了
*/
@Pointcut("execution(public void com.bjpowernode.service.impl.SomeServiceImpl2.doSome(..))")
public void my() { // 此时代表execution中的内容
// 无需代码
}
@After(value = "my()") // 即可
publid void my() {
// do something
}
Spring集成MyBatis
MyBatis博客可参考:
集成思路
spring能集成很多的框架,是spring一个优势功能。通过集成功能,让开发人员使用其他框架更方便。
集成使用的是spring Ioc核心技术。
将Spring和MyBatis进行整合,主要解决的问题是将SqlSessionFactory对象交给Spring管理,所以,该整合只需要将SqlSessionFactory的对象生成器SqlSessionFactoryBean注册在Spring容器中,再将其注册给Dao的实现类即可完成整合。
实现Spring和MyBatis的整合常用的方式:扫描的Mapper动态代理
<!-- applicationContext.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入外部配置文件 -->
<context:property-placeholder location="jdbc.properties"/>
<bean id="mydataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 此处mybatis配和spring创建对象 -->
<!-- 指定数据源 -->
<property name="dataSource" ref="mydataSource"/>
<!-- 指定mybatis配置文件 -->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!-- MapperScannerConfigurer循环basePackage所表示的包,把包中的每个接口都找到
调用sqlsession.getmapper,把每个dao都创建出代理对象然后放在容器中
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定SqlSessionFactory对象名称 -->
<property name="sqlSessionFactoryBeanName" value="factory"/>
<!-- 指定基本包名,生成studentDao -->
<property name="basePackage" value="com.bjpowernode.dao"/>
</bean>
<bean id="studentService" class="com.bjpowernode.service.impl.ServiceStudentImpl">
<property name="studentDao" ref="studentDao"/>
</bean>
</beans>
实现流程
下面具体阐述该过程
- 利用阿里云数据连接池模块
DruidDataSource并引入外部配置文件jdbc.propertie来连接数据库 - 利用
mybatis-spring.jar包中的模块沟通连接MyBatis和Spring模块,此处使用了两个模块SqlSessionFactoryBean:引入mybatis.xml并通过SqlSessionFactoryBuilder来获取SqlSessionFactory接口,此时抛弃了传统的通过mybatis.xml配置数据库,而是通过DruidDataSource来配置数据连接池,该继承自DefaultSqlSessionFactory,与SqlSessionFactory殊途同归。MapperScannerConfigurer:首先获取SqlSessionFactoryBean,然后使用basePackage属性来获取所有的dao接口并配合主配置文件mybatis.xml中的mapper来实现动态代理,同getMapper(xxx.class)方法,生成的dao接口的工具类(实现类)默认是接口名首字母的小写,然后服务即可利用该实现类实现业务
- 定义服务的
<bean/>实现类,参数使用dao生成的实现类(引用类型)即可。
Spring事务
Spring的事务概念
什么是事务?事务是一些sql序列的集合, 是多条sq|,作为一个整体执行。
什么情况下需要事务?
一个操作需要多条(2条或2条以上的
sql),sql语句-起完成,操作才能成功。
事务:加在业务类的方法上面(public方法 上面),表示业务方法执行时,需要事务的支持。
不同的数据库访问技术,处理事务是不同的
-
JDBC访问数据库,事务处理public void updateAccount() { conn.setAutoCommit(false); /* * do something */ conn.setAutoCommit(true); } -
MyBatis执行数据库,事务处理public void updateAccount() { SqlSession session = SqlSession.openSession(false); try { /* * do something */ session.commit(); } catch { session.rollback(); } }spring统一管理事务,把不同的数据库访问技术的事务处理统一起来。
Spring事务管理器
Spring框架使用事务管理器对象,管理所有的事务。
事务管理器接口:PlatformTransactionManager,作用:定义了事务的操作,主要是commit()和rollback()
事务管理器实现类:一种数据库的访问技术有一个实现类,由具体的实现类具体完成事务的提交和回滚。
JDBC和MyBatis访问数据库有自己的事务管理器实现类:DataSourceTransactionManager。hibernate框架,他的事务管理器实现类:HibernateTransactionManager。
事务使用的是
AOP的环绕通知@Around
Spring的事务定义
事务定义接口TransactionDefinition中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对他们的操作。
隔离级别:控制事务之间影响的程度
-
ISOLATION_DEFAULT:采用DB默认的事务隔离级别,MySQL默认是ISOLATION_REPEATABLE_READ,Oracle默认是ISOLATION_READ_COMMITTED。 -
ISOLATION_READ_COMMITTED:读已提交,解决脏读,存在不可重复读和幻读 -
ISOLATION_READ_UNCOMMITTED:读未提交,未解决任何并发问题 -
ISOLATION_REPEATABLE_READ:可重复读解决脏读和不可重复读,存在幻读 -
ISOLATION_SERIALIZABLE:串行化,不存在并发问题
事务超时:表示一个业务方法最长的执行时间,没有到达时间没有执行完毕,spring回滚事务 。
以秒为单位,整数值,默认是-1。
传播行为:业务方法在调用时事务在方法之间的传递和使用,使用传播行为可以标识是否有事务。
-
PROPAGATION_REQUIRED:Spring默认传播行为,方法在调用时,如果存在事务,则使用当前事务;如果没有事务,则新建事务,方法在新事务中执行 -
PROPAGATION_REQUIRES_NEW:方法需要一个新事务,如果调用方法时,存在一个事务,则原来的事务暂停,直到新事务执行完毕;如果方法调用时没有事务,则新建一个事务,在新事务中执行代码 -
PROPAGATION_SUPPORTS:支持,有事务则可以正常执行,没有事务也可以正常执行,一般是查询操作。 -
PROPAGATION_MANDATORY -
PROPAGATION_NESTED -
PROPAGATION_NEVER -
PROPAGATION_NOT_SUPPORTED
Spring注解管理事务
通过@Transactional注解方式,可将事务织入到相应public方法中,实现事务管理。
属性:
propagation:事务的传播行为,他使用的是Propagation的枚举值,默认值为Propagation.REQUIRED。isolation:用于设置事务的隔离级别。该属性类型为Isolation枚举,默认值为Isolation.DEFAULT。readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为boolean,默认值为false。timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为int,默认值为-1,即没有时限。rollbackFor:表示回滚的异常类列表,他的值是一个数组,每个值是异常类型的class。rollbackForClassName:表示回滚的异常类列表他的值是异常类名称,是String类型的值。noRollbackFor:不需要回滚的异常类列表。是class类型的。noRollbackForClassName:不需要回滚的异常类列表,是String类型的值。
注解使用步骤:
-
在
Spring的配置文件,声明事务的内容- 声明事务管理器,说明使用哪个事务管理器
- 声明使用注解管理事务,开启是注解驱动
-
在类的源代码中,加入
@Transactional@Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false, timeout = 20, rollbackFor ={NullPointerException.class, NotEnoughException.class})applicationContext.xml文件<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--指定数据源--> <property name="dataSource" value="mydataSource"/> </bean> <!-- 开启事务注解驱动 transaction-manager:指定事务管理器id --> <tx:annotation-driven transaction-manager="transactionManager"/>
事务默认是运行时产生异常,此时不用谢
rollback。
使用特点
spring框架自己提供的事务控制。- 适合中小型项目。(直接在源代码中进行更改)
- 使用方便,效率高
AspectJ配置事务
使用步骤
pom.xml中加入spring-aspectj的依赖- 在
spring的配置文件声明事务的内容- 声明事务管理器
- 声明业务方法需要的事务属性
- 声明切入点表达式
applicationContext.xml
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定数据源-->
<property name="dataSource" value="mydataSource"/>
</bean>
<!-- 声明业务方法的事务属性 -->
<tx:advice id="serviceAdvice" transaction-manager="transactionManager">
<!-- 给具体的业务方法增加事务的说明 -->
<tx:attributes>
<!-- name: 业务方法的名称/带有部分通配符的名称/使用*表示以上方法以外的(不是所有) -->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="10"/>
</tx:attributes>
</tx:advice>
切入点表达式
<aop:config>
<!--
声明切入点表达式
expression:切入点表达式,表 示那些类和类中的方法要参与事务
id: 切入点表达式的名称,唯-值
-->
<aop:pointcut id="servicePointcut" expression="execution(* *..service..*.*(..))"/>
<!--关联切入点表达式和事务通知-->
<aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
意义:如果aop:advisor中pointcut-ref所表示的切入点表达式和advice-ref相同,则执行AOP
Spring和Web
maven选择webapp<groupId>com.bjpowernode</groupId> <artifactId>testmy</artifactId> <version>1.0.0</version> <packaging>war</packaging>
此处内容是根据servlet来配置的,为了继续后续SSM框架的学习,后期使用SpringMVC进行整合。
更多推荐



所有评论(0)