目录
简介
spring 是目前主流的 Java Web 开发框架。Spring 是分层的 Java SE/EE 一站式轻量级开源框架,以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核。
IoC 指的是将对象的创建权交给 Spring 去创建,IOC(控制对象的生命周期和对象间的关系)。使用 Spring 之前,对象的创建都是由我们使用 new 创建,而使用 Spring 之后,对象的创建都交给了 Spring 框架。在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。DI(通过反射实现属性的动态注入)。
AOP 用来封装多个类的公共行为,将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP 还解决一些系统层面上的问题,比如日志、事务、权限等。
特点:
- 方便解耦,简化开发
- 方便集成各种优秀框架,降低 Java EE API 的使用难度
- 声明式事务的支持
- AOP 编程的支持
体验spring
- 创建maven项目,导入spring依赖
- 编写beans.xml,和与之相关的Java bean类
编写ApplicationContext接口和ClassPathXmlApplicationContext实现类(编写spring底层,实际使用时不用,直接使用spring自带的) 读取beans.xml,调用getBean(接口类类型)。- 完成dao层和service层,测试运行
读取beans.xml:FileSystemXmlApplicationContext和ClassPathXmlApplicationContext,FileSystemXmlApplicationContext有两种写法1.盘符路径(绝对路径) 2.classpath:(相对路径),ClassPathXmlApplicationContext默认相对路径,不需要写classpath:
IoC控制反转的xml配置管理,也是spring框架的核心知识点之一,spring通过配置文件beans.xml动态实例对象(Dao dao=new DaoService a/b)
<?xml version="1.0" encoding="UTF-8"?>
<!--spring IOC 就是管理bean的生命周期和bean之间的关系-->
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="personDao" class="com.xx.dao.impl.PersonDaoImpl">
<property name="personInfoId" value="1"/>
<property name="personInfo" value="abc"/>
</bean>
<bean id="personService" class="com.xx.service.impl.PersonServiceImpl">
<!-- personService 需要使用一个id为personDao bean进行初始化-->
<!--
根据名称
根据类型
-->
<property name="personDao" ref="personDao"/>
</bean>
<bean id="orderService" class="com.xx.service.impl.OrderServiceImpl">
<property name="personDao" ref="personDao"/>
</bean>
<!-- <bean id="personDaoWithCount" class="com.xx.dao.impl.PersonDaoImpWithCount">
<property name="personInfoId" value="1"/>
<property name="personInfo" value="abc"/>
</bean>-->
</beans>
属性注入
构造器注入
在Spring的配置文件中,使用bean标签,配置id和class属性之后,且没有其他属性和标签时。采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建成功。
构造器注入属性值有三种方式:
//通过下标传递参数
<constructor-arg index="0">
//通过类型传递参数
<constructor-arg type="int">
//通过属性名传递参数
<constructor-arg name="cardId">
set注入
通过setter方法进行属性值注入
//set注入
<property name="persionId" value="1"/>
复杂对象
一些如property,map,list,set等复杂对象
//property类型
<props>
//给property赋值
<prop key="administrator">[email protected]</prop>
<prop key="support">[email protected]</prop>
<prop key="development">[email protected]</prop>
</props>
//map类型
<map>
//给map属性赋值
<entry key="an entry" value="just some string"/>
//map的value是对象时使用
<entry key ="a ref" value-ref="myDataSource"/>
</map>
//list类型
<list>
//给list赋值
<value>a list element followed by a reference</value>
//list里是对象时使用
<ref bean="myDataSource" />
</list>
//set类型
<set>
//给set赋值
<value>just some string</value>
//set里是对象时使用
<ref bean="myDataSource" />
</set>
类型转换器
解决复杂类型,如日期
//Java代码
public class MyConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parse = simpleDateFormat.parse(source);
return parse;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
//xml配置
<bean id="convert" class="com.xx.basic.convert.MyConverter"></bean>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="convert"></ref>
</set>
</property>
</bean>
p命名空间和c命名空间
p命名空间(简化set注入)
- 实体类中必须有set方法;
- 实体类中必须有无参构造器(默认存在);
- 必须导入p命名空间注入方式依赖。
导入约束后在bean标签里添加 p:name="" 使用:
xmlns:p="http://www.springframework.org/schema/p"
c命名空间(简化构造器注入)
- 实体类中必须存在有参构造器;
- 必须导入c命名空间注入方式依赖。
导入约束后在bean标签里添加 c:name="" 使用:
xmlns:c="http://www.springframework.org/schema/c"
自动装配
使用autowire来配置自动装载模式
- AUTOWIRE_NO(no):默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配。bean。
- AUTOWIRE_BY_NAME(byName):通过bean的名称进行自动装配。如果一个bean的property与另一个bean的name相同,就进行自动装配。
- AUTOWIRE_BY_TYPE(byType):通过参数的数据类型进行自动装配.。
- AUTOWIRE_CONSTRUCTOR(constructor):利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
<bean id="person" class="com.xx.Person" autowire="constructor/byName/byType"></bean>
注解注入
@Value
//添加约束
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
//引入注解
<context:annotation-config/>
//引入属性文件
<context:property-placeholder location="classpath:test.properties"/>
使用注解的方式需要用到注解@Value,在属性文件的读取中使用的是“$”,从Spring 容器中获取属性使用“#”。#{}里可以使用一些Java方法,案例:
ublic class PersonCard {
@Value("${test.boolean}")
private Boolean testBoolean;
@Value("${test.string}")
private String testString;
@Value("${test.integer}")
private Integer testInteger;
@Value("${test.long}")
private Long testLong;
@Value("${test.float}")
private Float testFloat;
@Value("${test.double}")
private Double testDouble;
@Value("#{'${test.array}'.split(',')}")
private String[] testArray;
@Value("#{'${test.list}'.split(',')}")
private List<String> testList;
@Value("#{'${test.set}'.split(',')}")
private Set<String> testSet;
@Value("#{${test.map}}")
private Map<String, Object> testMap;
}
工厂模式
一般用于构建复杂对象的实例,三种工厂模式(factorybean,实例工厂,静态工厂)。
单例对象:工厂启动工厂中所有的单例的对象随之创建,工厂销毁工厂中所有单例对象随之销毁 多例对象:每次在工厂中使用时进行创建,工厂不负责多例对象的销毁
beanfactory
BeanFactory 是 Spring 框架的核心接口之一,用于管理和获取应用程序中的 Bean 实例。它是一个工厂模式的实现,负责创建、配置和管理 Bean 对象。BeanFactory 是 Spring IoC 容器的基础,它可以从配置元数据(如 XML 文件)中读取 Bean 的定义,并在需要时实例化和提供这些 Bean。
factorybean
FactoryBean是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。创建实现类实现FactoryBean重写getObject(),在beans.xml配置实现类的bean和接口类的bean。
一般情况下,Spring是通过反射机制利用bean的class属性指定实现类来实例化bean的。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,那么则需要在标签中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可以得到一个更加简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。
区别
生命周期流程:
使用beanFactory创建对象时,需要遵循严格的生命周期流程。它会按照预定的顺序执行各个生命周期阶段,包括实例化、属性填充、初始化和销毁等。这种严格的生命周期管理确保了bean的正确创建和销毁,但也使得自定义对象的创建变得复杂。
而在FactoryBean中,我们可以更加自由地定义对象的创建过程。通过实现FactoryBean接口,我们可以在getObject()方法中编写自定义的创建逻辑,从而实现更加灵活的对象创建方式。这使得我们能够根据需求定制对象的创建过程,而不需要遵循严格的生命周期流程。
对象管理:
使用beanFactory创建的对象由Spring容器负责管理,我们无需过多关心对象的创建和销毁。Spring容器会在需要时自动创建bean,并在容器关闭时销毁它们。
而FactoryBean创建的对象同样由Spring容器管理,但我们需要明确地将FactoryBean注册到容器中。这意味着我们可以通过配置文件或编码的方式明确指定要创建的bean是一个FactoryBean。Spring容器会自动检测到FactoryBean接口,并调用其getObject()方法获取实际的bean对象。
实例工厂
创建一个实例方法来创建Bean的实例,在beans.xml配置 bean调用实例方法(填写factory-bean和factory-method)
<bean id="id名" factory-bean="实例方法所在类的beanid"
factory-method="实例方法名"></bean>
举例:
//使用实例工厂
<bean id="student1" class="com.xk.entity.Student">
<property name="studentId" value="1"/>
<property name="studentName" value="1"/>
<property name="birthday">
//这是属性并不是bean,不需要id
<bean factory-bean="dateFormat" factory-method="parse" >
<constructor-arg value="2023-01-01" />
</bean>
</property>
</bean>
<bean id="dateFormat" class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd" />
</bean>
静态工厂
创建一个静态方法来创建Bean的实例(填写class和factory-method)
<bean id="id名" class="类的全限定名" factory-method="静态实例方法名"></bean>
由于spring默认单例模式,在测试启动时,默认会立即将单实例bean进行实例化,可以使用lazy-init=true 让bean使用时再实例
区别
- 静态工厂方法:初始化之前,工厂中的类已经被实例化放在工厂容器中
- 实例工厂方法:在构造方法初始化时,会将类进行实例化放在工厂中
- 静态工厂创建对象是一步到位,写一个bean标签即可;实例工厂创建对象是分两步,
需要两个bean标签,一个标签是工厂类,另一个标签是对象类
bean生命周期
- scope=“singleton” 单例模式:在启动(容器初始化之前),就已经创建了bean,且整个应用只有一个。先创建bean对象,然后把bean放到容器的map中,完成容器的初始化。
- scope=“prototype” 原型模式:在用到对象的时候,才创建对象。
- 调用无参创建对象
- 调用set方法初始化属性
- 调用初始化方法
- 关闭容器,调用销毁的方法
对应的方法
- createBeanInstance() -> 实例化
- populateBean() -> 属性赋值
- initializeBean() -> 初始化
- destroyBeans() ->销毁
Spring 容器在初始化一个Bean实例时,会指定该实例的作用域,scope=""属性支持6 种作用域(scope属性)。1.singleton(单例模式) 2.prototype(原型模式) 3.request 4.session 5.application 6.websocket;后四种只能在web应用中使用。
singleton(单例模式)
Spring 容器中只有一个 Bean 实例,Bean 以单例的方式存在,每次都返回一个相同的 Bean 实例。底层由map存储,所以bean是同一个。容器启动的时候创建对象,容器正常关闭时销毁对象。Spring默认状态是单例的,单例默认状态是非懒加载的。
prototype(原型模式)
每次通过 Spring 容器获取 Bean 时,容器都会创建一个 Bean 实例。获取对象的时候创建对象,spring容器不负责对象的销毁。(垃圾回收机制)。多例状态是默认懒加载的,只要是多例模式都是懒加载,只有在单例模式下才能控制懒加载有效。
懒加载
(lazy-init="true/false"),默认是false,Spring在启动时,默认会立即将单实例bean进行实例化,并加载到Spring容器中。进行延迟加载(延迟到在第一次调用的时候实例化)可以节省资源
spring注解开发
类的注入
@Service(service层)、@Controller(controller层)、@Repository(dao层)、@Component(组件):作用于类。这四个注解作用都是一样的,把类注入bean中,作用等于<bean id="" >...</bean>。为了层次结构命名不同,组件是不好归类时使用。
方法的注入
@Bean:作用于方法,将方法返回的对象注册为一个 Bean(class=返回值类型的全限定名),然后交给Spring容器,能够动态获取一个Bean对象
属性的注入
@Autowired、@Resource:@Resource注解和@Autowired注解作用于属性,等价于<property ></property>。 在接口只有一个实现类的时候,两个注解可以互相替换,效果相同。@Resource注解是Java自身的注解,@Autowired注解是Spring的注解,所以一般使用@Autowired。
@Resource注解默认按照名称进行匹配,@Resource注解有两个重要的属性,分别是name和type,name有值则按byName自动注入,type有值按byType自动注入。
@Autowired注解只根据type进行注入,不会去匹配name。如果只根据type无法辨别注入对象时,就需要配合使用@Qualifier注解或者@Primary使用。
@Qualifier:指定按照名称去装配 bean,和@Autowired一起
@Primary:存在多个类型的实例时,哪个主要就标记哪个,标记在@Autowired要实现的bean上
AOP
不改变原有代码的情况下实现代码功能的增强。通过代理类为原始类增加额外的功能,代理类最终执行的是被代理类的方法。底层是用代理模式实现AOP,springAOP实现有三种方式。
切面 = 切入点表达式(四种,常用execution和注解) + 通知方法
切入点表达式:如果目标对象满足切入点表达式的判断,则spring自动为其创建代理对象
步骤:
1.导入依赖
<!--支持切入点表达式-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
<!--支持aop相关注解-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version> 1.9.19 </version>
</dependency>
织入点语法
Aspectj织入点语法: |
|
execution(public * *(..)) | 任何类的任何返回值的任何方法 |
execution(* set*(..)) | 任何类的set开头的方法 |
execution(* com.xyz.service.AccountService.*(..)) | 任何返回值的规定类里面的方法 |
execution(* com.xyz.service..*.*(..)) | 任何返回值,规定包或规定包子包的任何类任何方法 |
execution(* com.xyz.service.*.*(..)) | 任何返回值,规定包的任何类任何方法 |
通知(advice)
- Before advice(前置通知@Before):连接点前面执行,不能终止后续流程,除非抛异常
- After returning advice(后置通知@AfterReturning):连接点正常返回时执行,有异常不执行
- Around advice(环绕通知@Around):围绕连接点前后执行,也能捕获异常处理
- After advice(最终通知@After):连接点退出时执行,无论是正常退出还是异常退出
- After throwing advice(异常通知@AfterThrowing):连接点方法抛出异常时执行
实现接口
创建实现类实现 MethodBeforeAdvice/AfterReturningAdvice/MethodInterceptor/ThrowsAdvice 这四个接口,并实现方法。
注意:在异常接口实现类中不会提示重写的方法,但必须按照格式编写
//(中括号表示里面的参数要不全都存在,不要全都不存在),方法名、修饰符也不能改
public void afterThrowing([Method, args, target], ThrowableSubclass){}
<!--spring配置类的bean-->
<bean id="beforeAdvice" class="com.xk.aop01.BeforeAdvice"/>
<bean id="afterAdvice" class="com.xk.aop01.AfterAdvice"/>
<bean id="aroundAdvice" class="com.xk.aop01.AroundAdvice"/>
<bean id="exceptionAdvice" class="com.xk.aop01.ExceptionAdvice"/>
<!--aop配置-->
<aop:config>
<!--切点配置-->
<aop:pointcut id="pointCut" expression="execution(* com.xk.dao.impl.*.*(..))"/>
<!--通知配置-->
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="pointCut"/>
<aop:advisor advice-ref="afterAdvice" pointcut-ref="pointCut"/>
<aop:advisor advice-ref="aroundAdvice" pointcut-ref="pointCut"/>
<aop:advisor advice-ref="exceptionAdvice" pointcut-ref="pointCut"/>
</aop:config>-->
定义切面
步骤:
1.在一个类里定义切面方法 2.beans.xml配置
<bean id="aspectTest" class="com.xx.aop02.AspectTest"/>
<!--aop配置-->
<aop:config>
<!--切面配置-->
<aop:aspect ref="aspectTest">
<!--切点配置-->
<aop:pointcut id="pointCut2" expression="execution(* com.xx.dao.impl.*.*(..))"/>
<!--通知(增强)配置,参数JoinPoint/ProceedingJoinPoint(around专属的)-->
<aop:before method="before" pointcut-ref="pointCut2"/>
<!--有额外参数的,无额外参数的用<aop:after>-->
<aop:after-returning method="after" pointcut-ref="pointCut2" returning="o"/>
<aop:around method="around" pointcut-ref="pointCut2"/>
<!--异常通知配置-->
<aop:after-throwing method="exception" pointcut-ref="pointCut2" throwing="e"/>
</aop:aspect>
</aop:config>
参考代码:
@Component
@Aspect
public class AspectTest02 {
@Pointcut("execution(* com.xx.dao.impl.*.*(..))")
public void aopTest() {
}
@Around("execution(* com.xx.dao.impl.*.*(..))")
public Object around(ProceedingJoinPoint pj) {
System.out.println(pj.getClass().getName()+"执行前");
Object o;
try {
o=pj.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
System.out.println(pj.getClass().getName()+"执行后");
return o;
}
@Before("execution(* com.xx.dao.impl.*.*(..))")
public void before(JoinPoint p) {
System.out.println(p.getSignature().getName());
}
@AfterReturning(value = "execution(* com.xx.dao.impl.*.*(..))",returning = "o")
public void after(JoinPoint p,Object o) {
System.out.println(p.getSignature().getName()+o);
}
@AfterThrowing(value = "execution(* com.xx.dao.impl.*.*(..))",throwing = "e")
public void exception(JoinPoint jp,Exception e) {
System.out.println(jp);
}
注解方式
xml配置:<!--开启aspectj自动代理--><aop:aspectj-autoproxy/>
1.在一个类里定义切面方法 2.在类上添加@Component(注册bean)@Aspect(定义aop类) 3.在方法上添加@Around/@Before/@AfterReturning(无参用@After)/@AfterThrowing,值和xml里一致
//四个aop注解及其传值
@Around("execution(* com.xk.dao.impl.*.*(..))")
@Before("execution(* com.xk.dao.impl.*.*(..))")
@AfterReturning(value = "execution(* com.xk.dao.impl.*.*(..))",returning = "o")
@AfterThrowing(value = "execution(* com.xk.dao.impl.*.*(..))",throwing = "e")
整合mybatis
整合部分集中在配置文件中,其他部分基本不变
1.导入相关依赖
<!-- spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<!--aspects 切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.20</version>
</dependency>
<!--支持切入点表达式-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
<!--支持aop相关注解-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version> 1.9.19 </version>
</dependency>
<!--使用Spring Test后,只需要通过注解指定Spring配置类-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.20</version>
<scope>test</scope>
</dependency>
<!-- Junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--Log4J日志工具 打印运行日志用的!-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.16.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.16.0</version>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.20</version>
</dependency>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!--mybatis&spring 整合包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.1</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--mybatis分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
2.编写配置文件(ApplicationContext.xml、mybatis-config.xml、db.properties、log4j.properties等)
<!--引入外部的数据源配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置druid数据库连接池-->
<bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${name}"/>
<property name="password" value="${password}"/>
</bean>
<!--sqlSessionFactory-->
<bean id="ssf" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置数据源-->
<property name="dataSource" ref="druid"/>
<!-- 注入mybatis的核心配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--mapper映射文件-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!--扫描所有的接口-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描所有的接口,生成代理对象,注入spring容器-->
<property name="basePackage" value="com.xk.dao"/>
</bean>
主要改动:ApplicationContext.xml
因为整合了druid,db.properties里不能写username;没有整合druid,db.properties里不能写name(这里可能会被识别成微软账户名,最好写复杂点,例如:db.name)
3.分层编写相关业务、功能java类和mapper.xml
dao层:dao接口和mapper.xml文件、entity:实体类、service:接口和实现类。通过controller层或Test类调用实现功能
插件mybatisX
spring事务
事务在逻辑上是一组操作,要么执行,要不都不执行。事务满足ACID特点
步骤
1.xml配置
<!-- 约束-->
xmlns:tx="http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
<!-- 注入事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druid"/>
</bean>
<!--配置事务通知-->
<tx:advice id="tx" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<!--筛选方法名前缀为save/delete的-->
<tx:method name=“save*"/>
<tx:method name=“delete*"/>
</tx:attributes>
</tx:advice>
<!--配置事务的aop-->
<aop:config>
<aop:pointcut id=“servicePoint” expression=“execution(* org.kgc.spring.service.imp.*.*(..))"/>
<aop:advisor advice-ref="tx" pointcut-ref="servicePoint"/>
</aop:config>
2.使用注解
<!-- 开启事务注解支持-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)
捕获异常也可提交事务
传播特性
传播级别定义的是事务的控制范围
传播机制 | 说明 |
PROPAGATION_REQUIRED | 如果当前没有事务就新建事务,已存在事务就加入到这个事务(常见) |
PROPAGATION_SUPPORTS | 支持当前事务,当前没有事务则以非事务方式执行 |
PROPAGATION_MANDATORY | 使用当前事务,当前没有事务则抛出异常 |
PROPAGATION_REQUIRED_NEW | 新建事务,若当前存在事务就把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行,若当前存在事务就把当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,若当前存在事务则抛出异常 |
PROPAGATION_NESTED | 若存在事务则嵌套事务执行,若没有事务则新建事务 |
隔离级别
事务隔离级别定义的是事务在数据库读写方面的控制范围。事务隔离级别是对事务 4 大特性中隔离性的具体体现,使用事务隔离级别可以控制并发事务在同时执行时的某种行为。
Spring 中的事务隔离级别比 MySQL 中的事务隔离级别多了一种,它包含的 5 种隔离级别分别是:
- Isolation.DEFAULT:默认的事务隔离级别,以连接的数据库的事务隔离级别为准。
- Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
- Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
- Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL 数据库默认的事务隔离级别)。
- Isolation.SERIALIZABLE:串行化,可以解决所有并发问题,但性能太低。