目录
5.2 使用 @ComponentScan 和 @Configuration
一、什么是IOC
1.1 IOC是什么
IOC 又称为控制翻转,将原本需要做的 java 反射, map 存储交给 spring 框架,从而降低各个模块中间的耦合性。
1.2 为什么叫控制反转
假设有 A、B、C、D 四个模块,若 A 模块需要使用到 B 模块的对象
没有IOC容器的时候:
没有 IOC 容器时,A模块需要在初始化或某个节点时 new 一个 B 对象或使用已有的 B 对象,而这个主动权在 A 模块中,A 模块能选择使用哪种 B 模块对象,是 new 一个新的,还是使用已有的
使用IOC容器后:
使用 IOC 容器后,所有的 B 对象都由 IOC 容器统一管理,即当 A 模块要使用 B 对象时,是使用 IOC 容器返回的 B 对象,这个对象的创建和管理权是在 IOC 容器手上的。
控制 -- 反转
控制:结合例子,可以发现对象的创建交给了 IOC 容器实现,即对象的创建和控制权交给了 IOC 容器,这就是Spring中的控制
反转:结合例子,未使用 IOC 容器时,对象的创建权和控制权是在他的调用对象手中的。
使用了 IOC 容器后,对象的创建权和控制权都交给了 IOC 容器,而调用他的对象模块只能使用 IOC 容器返回的对象
1.3 依赖注入
DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
依赖 -- 注入
依赖:组件依赖于 IOC 容器,通过 IOC 容器来提供对象所需要的资源,实现组件间的解耦
注入:调用组件的模块,通过向 IOC 容器获取,得到所需对象的过程,叫做注入
二、Bean 控制
2.1 创建 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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将创建的对象创建给spring来管理 -->
<!-- 创建Bean的三种方式 -->
<!-- 适用默认构造函数创建
在spring的配置文件中使用bean标签,配以id和class属性后,切没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时若类中没有默认构造函数,则对象无法创建-->
<!-- <bean id="accountService" class="com.tom.www.service.impl.AccountServiceImpl"></bean>-->
<!-- 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器) -->
<!-- <bean id="instanceFactory" class="com.tom.www.factory.InstanceFactory"></bean>-->
<!-- <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>-->
<!-- 使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器) -->
<bean id="accountService" class="com.tom.www.factory.StaticFactory" factory-method="getAccountService"></bean>
</beans>
2.2 Bean 的作用域
singletom: 单例的 =》 默认 常用
prototype:多例的 => 常用
request: 作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围,当不是集群环境时,它就是session
=> 一般用于集群
2.3 Bean 的生命周期
三、依赖注入(Dependency Injection)
在上面学习了如何创建一个 Bean 对象,但是若这个 Bean 对象中存在一些属性,我们要如何给他赋值呢?
给被 Ioc 控制了的 Bean 对象赋予属性值,这就叫做依赖注入。
使用依赖注入,能有效降低模块间的耦合性
3.1 能注入的数据类型
- 基本类型和 String
- 其他 bean 类型(在配置文件中或注解中配置过的 bean)
- 复杂类型/集合类型
3.2 注入方式
- 使用构造器注入(少用)
- 使用 set 方式注入
- 使用注解注入
3.2.1 使用构造器注入
<!-- 构造函数注入
使用标签:constructor-arg
标签出现的为止: bean标签内部
标签中的属性:
type: 用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index: 用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引位置从0开始
name(c常用): 用于指定给构造函数中指定名称的参数赋值
======================= 以上三个用于指定给构造函数中哪个参数赋值 =====================
value: 用于提供基本类型和string类型的数据
ref: 用于指定其他的bean类型数据,即在spring的ioc容器中出现过的bean对象
优势:
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
弊端:
改变了 Bean 对象的实例化方式,使我们在创建对象时,若用不到这些数据,也必须提供
-->
<bean id="accountService" class="xyz.tom.www.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="小明"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
3.2.2 使用 set 方式注入
<!-- set方法注入
设计的标签: property
出现的为止: bean标签的内部
标签中的属性:
name(c常用): 用于指定注入时所调用的set方法名称
value: 用于提供基本类型和string类型的数据
ref: 用于指定其他的bean类型数据,即在spring的ioc容器中出现过的bean对象
优势:
创建对象时没有明确的限制,可以使用默认构造函数
弊端:
如果有某个成员必须有值,则获取对象时有可能set方法没有执行
-->
<bean id="accountService2" class="xyz.tom.www.service.impl.AccountServiceImpl2">
<property name="name" value="test"></property>
<property name="birthday" ref="now"></property>
</bean>
<!-- 复杂类型的注入/集合类型的注入
用于给List结构集合注入的标签: list array set
用于给Map结构注入的标签: map props
同类型可以互换,即只要记: list map
-->
<bean id="accountService3" class="xyz.tom.www.service.impl.AccountServiceImpl3">
<property name="myStrs">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>BBB</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="testA">AAA</prop>
<prop key="testB">BBB</prop>
<prop key="testC">CCC</prop>
</props>
</property>
</bean>
四、使用注解操作Bean
4.1 创建 Bean 对象 @Component
4.1.1 作用:
可以使用 @Component 来创建Bean对象
- 作用: 用于将当前类对象存入 spring 容器中
- 属性: value 用于指定 bean 的 id,当不写时,默认使用当前类名,且首字母改为小写
4.1.2 使用
创建 bean.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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知spring创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是在一个名称
为context名称空间和约束中-->
<context:component-scan base-package="xyz.tom"></context:component-scan>
</beans>
创建对应的bean
@Component(value = "accountService")
public class AccountServiceImpl implements AccountService {
public void saveAccount() {
System.out.println("这是 AccountServiceImpl");
}
}
4.1.3 针对三层框架的注解
根据mvc三层框架,spring分别提供了三个注解,这三个注解其实是一样的,只是使用地方有所区别:
- @Controller:一般用在表现层
- @Service:一般用在业务层
- @Repository:一般用在持久层
- @Component:其他
4.2 使用注解注入数据 @AutoWired
4.2.1 @Autowired 自动注入
- 作用: 自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
- 位置: 可以是变量上,也可以是方法上
- 限制: 若没有或 存在多个类型匹配时,均会报错。要求对应的bean对象类型必须唯一
4.2.2 @Qualifier 存在多个对象类型时,可以指定使用哪一个对象类型
如下列代码,均使用了 AccountDao,当准备注入时,发现了多个对象类型
@Repository(value = "accountDao1")
public class AccountDaoImpl implements AccountDao {
public void saveAccount() {
System.out.println("这是 AccountDaoImpl");
}
}
@Repository(value = "accountDao2")
public class AccountDaoImpl2 implements AccountDao {
public void saveAccount() {
System.out.println("这是 AccountDaoImpl222");
}
}
可以通过 @Qualifier 指定使用哪一个对象类型
@Resource(name = "accountDao1")
// @Qualifier(value = "accountDao1")
// @Autowired
private AccountDao accountDao = null;
4.2.3 使用@Resource 直接指定使用哪一个对象类型
@Resource(name = "accountDao1")
// @Qualifier(value = "accountDao1")
// @Autowired
private AccountDao accountDao = null;
4.2.4 使用@Value注入基本类型或string类型数据
结合SpEL使用 SpEL: ${}
4.3 使用注解改变作用范围 @Scope
通过 @Scope 可以设置成单例、多例等
@Service(value = "accountService")
@Scope(value = "prototype")
public class AccountServiceImpl implements AccountService {
xxx....
}
4.4 修改生命周期
使用 @PreDestory 指定销毁方法
@PreDestroy
public void destroy() {
System.out.println("destroy");
}
使用 @PostConstruct 指定初始方法
@PostConstruct
public void init() {
System.out.println("init");
}
4.5 使用案例
結合dbUtil访问数据库
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
bean.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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="xyz.tom"></context:component-scan>
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 连接数据库的必备信息 -->
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://49.233.178.108:3306/mysql?useUnicode=true&characterEncoding=utf8&useSSL=false"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
查询类: accountDao:
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
private QueryRunner runner;
public ArrayList<Account> findAllAccount() {
try {
List list = runner.query("select * from account", new BeanListHandler<Account>(Account.class));
return (ArrayList<Account>) list;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Account findAccountById(Integer id) {
try {
return runner.query("select * from account where id = ?", new BeanHandler<Account>(Account.class), id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void saveAccount(Account account) {
try {
runner.update("insert into account(name,money) values (?, ?)", account.getName(),account.getMoney());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try {
runner.update("update account set name=?,money=? where id=?", account.getName(),account.getMoney(),account.getId());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void delAccount(Integer id) {
try {
runner.update("delete from account where id=?", id);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
五、使用对象的方式实现配置类
5.1 相关知识点
- AnnotationConfigApplicationContext 加载 Bean 对象
- @Configuration 代表 这是一个配置类对象,要被扫描到的
- @ComponentScan 代表要扫描的包路径
- @Import 要导入的包
5.2 使用 @ComponentScan 和 @Configuration
1)改用 AnnotationConfigApplicationContext 获取对象,通过参数中配置的配置类类名,表明这个类是父配置类
@Test
public void testFindAll() {
// 1. 获取容器
// ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
// ApplicationContext ac = new AnnotationConfigApplicationContext(JdbcConfig.class);
// 2. 得到业务层对象
AccountService as = ac.getBean("accountService", AccountService.class);
// 3. 执行方法
ArrayList<Account> accounts = as.findAllAccount();
for (Account account : accounts) {
System.out.println("name = " + account.getName() + " id = " + account.getId() + " money = " + account.getMoney());
}
}
2)使用 @ComponentScan 和 @Configuration 实现加载子配置类
主配置类中:
// 指定扫描的路径
@ComponentScan({"xyz.tom", "config"})
public class SpringConfiguration {
}
子配置类中:
// 表明这是一个配置类,会被扫描器扫到
@Configuration
public class JdbcConfig {
....
}
5.3 使用 @Import 导入子类
@ComponentScan({"xyz.tom"}) // 指定父类路径
@Import(JdbcConfig.class) // 导入子类,此时子类无需加 @Configuration
public class SpringConfiguration {
...
}
5.4 结合 Junit 完成单元测试
5.4.1 使用
主配置类中
- 导入spring整合junit的jar包
- 使用 @RunWith 注解将原有main方法替换,改为spring提供的main方法(提供了IOC控制)
- 告知spring运行期间,spring和ioc的创建是基于xml还是基于注解,并说明位置
- @ContextConfiguration: {
- xml: locations: 指定xml文件的位置,加上classpath关键字,表明在类路径下
- 注解:classess: 指定注解类所在位置
- }
5.4.2 相关代码
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
测试类test.java:
import java.util.ArrayList;
/*
* 使用Junit单元测试,测试我们的配置
* Spring整合junit的配置
* 1. 导入spring整合junit的jar
* 2. 使用Junit提供的一个注解将原有的main方法替换,改成spring提供的
* @RunWith
* 3. 告知spring的运行期,spring和ioc创建是基于xml还是注解的,并说明位置
* @ContextConfiguration
* locations: 指定xml文件的位置,加上classpath关键字,表示在类路径下
* classes: 指定注解类所在位置
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest{
@Autowired
private AccountService as = null;
//
// @Before
// public void init() {
// // 1. 获取容器
// ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
// // 2. 得到业务层对象
// as = ac.getBean("accountService", AccountService.class);
// }
@Test
public void testFindAll() {
// 3. 执行方法
ArrayList<Account> accounts = as.findAllAccount();
for (Account account : accounts) {
System.out.println("name = " + account.getName() + " id = " + account.getId() + " money = " + account.getMoney());
}
}
@Test
public void testFindOne() {
// 3. 执行方法
Account account = as.findAccountById(5);
if( account == null) {
System.out.println("account is null");
} else {
System.out.println("name = " + account.getName() + " id = " + account.getId() + " money = " + account.getMoney());
}
}
@Test
public void testSave() {
Account account = new Account();
account.setName("test");
account.setMoney(1005.22);
// 3. 执行方法
as.saveAccount(account);
}
@Test
public void testUpdate() {
Account account = new Account();
account.setId(3);
account.setName("testUpdate");
account.setMoney(1999.99);
// 3. 执行方法
as.updateAccount(account);
}
@Test
public void testDelete() {
as.delAccount(3);
}
}
主配置类:SpringConfiguration.java
//@Configuration
@ComponentScan({"xyz.tom"})
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
jdbc配置类: jdbcConfig.java
public class JdbcConfig {
@Value("${jdbc.driver}")
private String dirver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/*
* 用于创建一个 QueryRunner 对象
*/
@Bean(name = "runner")
// @Scope(value = "prototype")
public QueryRunner createQueryRunner(DataSource datasource) {
return new QueryRunner(datasource);
}
/*
* 用于创建一个数据源对象
*/
@Bean(name="dataSource")
public DataSource createDataSource() {
ComboPooledDataSource ds = new ComboPooledDataSource();
try {
ds.setDriverClass(dirver);
// System.out.println(url);
// ds.setDriverClass("com.mysql.cj.jdbc.Driver");
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
}
}
六、动态代理
6.1 动态代理的作用
通过动态代理,可以在不修改类代码的情况下,对类进行功能拓展
6.2 通过JDK方式
/**
* 动态代理
* 特点: 字节码随用随创建,随用随加载
* 作用: 不修改源码的基础上,对方法增强
* 分类:
* 基于切口的动态代理
* 基于子类的动态代理
* 基于接口的动态代理
* 设计的类: Proxy
* 提供者: JDK官网
* 如何创建代理对象:
* 使用Proxy类中的newProxyInstance方法
* 创建代理对象的要求:
* 被代理类最少实现一个接口,如果没有则不能使用
* newProxyInstance方法的参数:
* ClassLoader:类加载器
* 用于加载对象字节码的,和被代理对象使用相同的类加载器,固定写法。
* Class[]:字节码数组
* 用于让代理对象和被代理对象,有相同方法
* InvocationHandler:用于提供增强的代码
* 它是让我们写如何代理,一般都是些该接口的实现类,通常是匿名内部类,但不是必须
* 此接口的实现类都是谁用谁写
*/
IProducer proxyProduer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过盖房啊
* 方法参数的含义:
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 提供增强的代码
Object returnValue =null;
// 1. 获取方法执行的参数
Float money = (Float) args[0];
// 2. 判断当前方式是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
proxyProduer.saleProduct(10000f);
6.3 使用 Cglib 配置动态代理
final Producer producer = new Producer();
/**
* 动态代理
* 特点: 字节码随用随创建,随用随加载
* 作用: 不修改源码的基础上,对方法增强
* 分类:
* 基于切口的动态代理
* 基于子类的动态代理
* 基于子类的动态代理
* 设计的类: Enhancer
* 提供者: 第三方cglib库
* 如何创建代理对象:
* 使用Enhancer类中的create方法
* 创建代理对象的要求:
* 被代理类不能是最终类
* create方法的参数:
* Class:字节码
* 用于指定被代理对象的字节码。
* Callback:用于提供增强的代码
*/
IProducer cglibProxyProduer = (IProducer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 执行对象的任何方法一定会经过该方法
* @param proxy
* @param method
* @param args
* 以上三个参数和基于接口的动态代理中invoke方法中的参数是一样的
* @param methodProxy // 当前执行方法的代理对象
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 提供增强的代码
Object returnValue =null;
// 1. 获取方法执行的参数
Float money = (Float) args[0];
// 2. 判断当前方式是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
cglibProxyProduer.saleProduct(12000f);
七、切面
7.1 切面配置和使用
7.1.1 切入点表达式写法
关键字:execution(表达式)
标准的表达式写法:
public void xyz.tom.www.service.impl.AccountServiceImpl.saveAccount()
1)访问修饰符可以省略
void xyz.tom.www.service.impl.AccountServiceImpl.saveAccount()
2)返回值可以使用通配符表示任意返回值
* xyz.tom.www.service.impl.AccountServiceImpl.saveAccount()
3)包名可以使用通配符表示任意包,但是有几级包,就需要写几个
* * *.*.*.*.*.AccountServiceImpl.saveAccount()
4)使用.. 表明当前包及子包
* *..AccountServiceImpl.saveAccount()
5)类名和方法名都可以使用*做通配
* *..*.*()
6)参数列表可以直接写类型:
基本类型写名称: int
引用类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
7)可以用..表示有无参数均可,有参数可以是任意类型
全通配写法: * *..*.*(...)
实际开发中切入点表达式的通常写法: 切到业务层实现类的所有方法
* xyz.tom.www.service.impl.*.*(..)
7.1.2 使用样例
<bean id="logger" class="xyz.tom.www.utils.Logger"></bean>
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<!-- <aop:before method="printLog" pointcut="execution( public void xyz.tom.www.service.impl.AccountServiceImpl.saveAccount())"></aop:before>-->
<aop:before method="printLog" pointcut="execution( * *..*.*(..))"></aop:before>
<!-- <aop:before method="printLog" pointcut="execution( * *..AccountServiceImpl.saveAccount())"></aop:before>-->
</aop:aspect>
</aop:config>
八、事务
8.1 事务级别
事务隔离级别反映事务提交并发询问时的处理态度
事务级别 | 说明 |
ISOLATION_READ_UNCOMMITTED | 可以读取未提交的数据 |
ISOLATION_READ_COMMITTED | 只能读取已提交的数据,解决脏读问题(Oracle的默认级别) |
ISOLATION_REPEATABLE_READ | 是否读取其他事务提交修改后的数据,解决不可重复读问题(MySQL默认级别) |
ISOLATION_SERIALIZABLE | 是否读取其他事务提交添加后的数据,解决幻影读的问题 |
8.2 事务传播行为
事务传播行为 | 说明 | 使用场景 |
PROPAGATION_REQUIRED | 支持当前事务,假设当前没有事务。就新建一个事务 | 增删修 |
PROPAGATION_SUPPORTS | 支持当前事务,假设当前没有事务,就以非事务方式运行 | 查询 |
PROPAGATION_MANDATORY | 支持当前事务,假设当前没有事务,就抛出异常 | |
PROPAGATION_REQUIRES_NEW | 新建事务,假设当前存在事务。把当前事务挂起 | |
PROPAGATION_NOT_SUPPORTED | 以非事务方式运行操作。假设当前存在事务,就把当前事务挂起 | |
PROPAGATION_NEVER | 以非事务方式运行,假设当前存在事务,则抛出异常 | |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |