方便复习,转载于尚硅谷:https://www.yuque.com/tmfl/spring/of3mbz#3e4ec32a
5、Spring的AOP
5.1、AOP的相关概念
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发的一个热点,也是Spring框架的一个重要内容,是函数式编程的一种衍生类型。
简单的说,就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理技术,在不修改源码的基础上,对我们的方法进行增强。
在Spring中,框架会根据目标是否实现了接口来决定采用哪种动态代理方式。
-
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
-
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。 (即被代理类中的所有方法都是连接点,但只有真正被代理增强的方法才是切入点)。
-
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知的类型:前置通知、后置通知、异常通知、最终通知、环绕通知。
-
Introduction(引介):引介是一种特殊的通知,在不修改类代码的前提下, Introduction可为在运行期为类动态的添加一些方法或Field。
-
Target(目标对象):代理的目标对象。
-
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
-
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。
-
Aspect(切面):是切入点和通知(引介)的结合。
5.2、基于xml的aop配置示例
- 引入需要的pom依赖
<!-- spring核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 切入点表达式解析依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
- 编写bean.xml配置文件(需要引入xmlns:aop约束)
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="accountService" class="com.study.service.impl.AccountServiceImpl">
</bean>
<bean id="logger" class="com.study.util.Logger">
</bean>
<aop:config>
<aop:aspect id="logAdvice" ref="logger" >
<aop:before method="printLog"
pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
</aop:before>
</aop:aspect>
</aop:config>
</beans>
配置aop的步骤:
1. 被代理的类的bean:accountService
2. 通知的bean:logger
3. 使用<aon:config>标签表明开始aop的配置
4. 使用<aop:aspect>标签表明开始配置切面
- id属性:给切面提供一个唯一的标识
- ref属性:指定通知类bean的id
5. 在<aop:aspect>标签的内部使用对应标签来配置通知的类型
<aop:before>标签:配置前置通知
- method属性:用于指定Logger类中哪个方法是前置通知
- pointcut属性:用于指定切入点表达式,该表达式含义是对业务层哪些方法进行增强
切入点表达式的写法:
- 关键字:execution(表达式)
- 表达式:
- 完整写法:访问修饰符 返回值 包名.包名.包名...包名.类名.方法名(参数列表)
public void com.study.service.impl.AccountServiceImpl.SaveAccount() - 参数列表
基本类型直接写名称:int
引用类型写包名.类名: java.lang.String
public void com.study.service.impl.AccountServiceImpl.SaveAccount(int)
- 完整写法:访问修饰符 返回值 包名.包名.包名...包名.类名.方法名(参数列表)
全通配写法:* ...*(..)
- 访问修饰符可以省略
void com.study.service.impl.AccountServiceImpl.SaveAccount() - 返回值可以使用通配符* 表示任意类型返回值
- com.study.service.impl.AccountServiceImpl.SaveAccount()
- 包名可以使用通配符表示任意包。但是有几级包,就需要写几个。
- ....AccountServiceImpl.SaveAccount()
- 包名可以使用..表示当前包及其子包
- *..AccountServiceImpl.SaveAccount()
- 类名可以使用通配符*表示任意类
- ...SaveAccount()
- 方法名可以使用通配符*表示任意方法
- ...*()
- 参数列表可以使用通配符*表示任意类型参数,但是必须有参数
- ...()
- 参数列表可以使用通配符..表示有无参数均可,有参数可以是任意类型
- ...*(..)
- 全通配写法:* ...*(..)
5.3、spring四种通知(不含环绕通知)的xml配置
四种通知对应的xml标签:
前置通知:<aop:before>
在切入点方法执行之前执行后置通知:<aop:after-returning>
在切入点方法正常执行之后执行。异常通知:<aop:after-throwing>
在切入点方法执行产生异常之后执行。最终通知:<aop:after>
无论切入点方法是否正常执行,它都会在其后面执行。
每次执行时,后置通知和异常通知永远只能执行一个。
示例:
<aop:config>
<aop:aspect id="logAdvice" ref="logger" >
<!-- 前置通知: 在切入点方法执行之前执行 -->
<aop:before
method="beforePrintLog"
pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
</aop:before>
<!-- 后置通知: 在切入点方法正常执行之后执行。(每次执行时,后置通知和异常通知永远只能执行一个) -->
<aop:after-returning
method="afterReturningPrintLog"
pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
</aop:after-returning>
<!-- 异常通知:在切入点方法执行产生异常之后执行 -->
<aop:after-throwing
method="afterThrowingPrintLog"
pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
</aop:after-throwing>
<!-- 最终通知: 无论切入点方法是否正常执行它都会在其后面执行 -->
<aop:after
method="afterPrintLog"
pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
</aop:after>
</aop:aspect>
</aop:config>
5.4、xml中通用化切入点表达式配置
多次使用的切入点表达式可以配置成一个通用化切入点表达式,在需要的地方进行引用。
使用<aop:pointcut>配置通用化切入点表达式,在通知的标签中使用pointcut-ref属性进行引入。
<aop:pointcut>标签可以写在<aop:aspect>标签内部,此时只有当前切面可以使用。
<aop:pointcut>标签也可以写在<aop:aspect>标签外部,此时就变成了所有切面可以使用。
aop的xml约束要求:aop:pointcut写在aop:aspect标签外部时,必须写在aop:aspect标签上方。
示例:
<aop:config>
<aop:aspect id="logAdvice" ref="logger" >
<aop:before method="beforePrintLog" pointcut-ref="pt1">
</aop:before>
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1">
</aop:after-returning>
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1">
</aop:after-throwing>
<aop:after method="afterPrintLog" pointcut-ref="pt1">
</aop:after>
<aop:pointcut id="pt1"
expression="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())"/>
</aop:aspect>
</aop:config>
5.5、xml中环绕通知的配置和写法
它是Spring框架为我们提供的一种可以在代码中手动控制增强方法合适执行的方式。
Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
示例:
<bean id="accountService" class="com.study.service.impl.AccountServiceImpl">
</bean>
<bean id="logger" class="com.study.util.Logger">
</bean>
<aop:config>
<aop:aspect id="logAdvice" ref="logger" >
<aop:pointcut id="pt1"
expression="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())"/>
<aop:around method="aroundPrintLog" pointcut-ref="pt1"> </aop:around>
</aop:aspect>
</aop:config>
环绕通知方法:
public Object aroundPrintLog(ProceedingJoinPoint proceedingJoinPoint) {
Object resultValue = null;
try {
Object[] args = proceedingJoinPoint.getArgs(); // 获取方法传入的参数
System.out.println("前置通知.....");
resultValue = proceedingJoinPoint.proceed(args); // 调用切入点方法,获取返回值
System.out.println("后置通知.......");
return resultValue;
} catch (Throwable e) { // 此处需要cache的异常类型需要为Throwable
System.out.println("异常通知........");
throw new RuntimeException(e);
} finally {
System.out.println("最终通知......");
}
}
5.6、基于注解的AOP配置
切面类注解:
- @Aspect
声明当前类是一个切面类。
另外,切面类还需使用@Component注解声明是一个bean
切入点注解:
- @Pointcut("execution(表达式)")
作用于方法上。
示例:
@Pointcut("execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())")
private void pt1() {}
通知的注解:
- @Before("切入点方法名()")
前置通知 - @AfterReturning("切入点方法名()")
后置通知 - @AfterThrowing("切入点方法名()")
异常通知 - @After("切入点方法名()")
最终通知 - @Around("切入点方法名()")
环绕通知
示例:
@Before("pt1()")
public void beforePrintLog() {
System.out.println("前置通知...");
}
Spring5.0.2版本,使用注解方式配置aop时,后置通知/异常通知 会在 最终通知 执行之后才执行。
使用注解配置AOP,需在xml或配置类中配置开启注解aop的支持。
xml开启注解aop支持:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
配置类开启注解aop支持:
@Configuration
@ComponentScan("com.study")
@EnableAspectJAutoProxy // 开启注解aop支持
public class SpringConfiguration {
}
6、Spring中的JdbcTemplate
6.1、JdbcTemplate概述
用于和数据库交互的,实现对表的CRUD操作。
它是Spring框架提供的一个对象,是对原始Jdbc API对象的简单封装。Spring框架为我们提供了很多的操作模板类。
操作关系型数据库的:JdbcTemplate、HibernateTemplate
操作noSql数据库的:RedisTemplate
操作消息队列的:JmsTemplate
JdbcTemplate在spring-jdbc-5.0.2.RELEASE.jar中,我们在导包的时候,除了要导入这个jar包,还需要导入和事务相关的spring-tx-5.0.2.RELEASE.jar。
用法示例:
导入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>12.1.0.1-atlassian-hosted</version>
</dependency>
编写测试方法:
public class JdbcTemplateDemo1 {
public static void main(String[] args) {
// 准备数据源: Spring内置数据源
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:@localhost:1521/orcl");
dataSource.setUsername("springTest");
dataSource.setPassword("tiger");
// 创建JdbcTemplate对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.execute("insert into account(id, name, money) values ('04', 'dddd', 200)");
}
}
6.2、JdbcTemplate的CRUD操作
6.2.1、使用IoC管理JdbcTemplate
在bean.xml中配置bean:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver">
</property>
<property name="url" value="jdbc:oracle:thin:@localhost:1521/orcl">
</property>
<property name="username" value="springTest"></property>
<property name="password" value="tiger"></property>
</bean>
6.2.2、新增
示例代码:
jdbcTemplate.update("insert into account (id, name, money) values (?, ?, ?)", "06", "ffff", 1000);
6.2.3、更新
示例代码:
jdbcTemplate.update("update account set name=?, money=? where id=?", "ffff", 800, "06");
6.2.4、删除
示例代码:
jdbcTemplate.update("delete from account where id=?", "06");
6.2.5、查询获取List
自定义数据库实体类的属性和数据库结果集映射。
示例代码:
public class JdbcTemplateDemo3 {
public static void main(String[] args) {
List<Account> accounts =
jdbcTemplate.query("select * from account where money > ?",
new AccountRowMapper(), 300);
accounts.forEach(System.out::println);
}
}
class AccountRowMapper implements RowMapper<Account> {
/**
* 把结果集的数据封装到Account中,然后由Spring把每个Account加到集合中
*/
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getString("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getFloat("money"));
return account;
}
}
使用Spring自带的BeanPropertyRowMapper进行映射
示例代码:
List<Account> accounts =
jdbcTemplate.query("select * from account where money > ?",
new BeanPropertyRowMapper<Account>(Account.class), 300);
accounts.forEach(System.out::println);
6.2.6、查询一个
获取到多个后,判断是否为空,获取第一个
示例代码:
List<Account> accounts =
jdbcTemplate.query("select * from account where id = ?",
new BeanPropertyRowMapper<Account>(Account.class), "03");
System.out.println(accounts.isEmpty()? "无结果" : accounts.get(0));
6.2.7、查询一个值(聚合函数)
使用聚合函数返回一行一列,但不加group by 子句
示例代码:
int count =
jdbcTemplate.queryForObject("select count(*) from account where money > ?",
Integer.class, 200f);
System.out.println(count);
6.3、编写JdbcDaoSupport去除dao层重复代码
Spring中原生就提供了JdbcDaoSupport类,下面的示例只是演示JdbcDaoSupport的实现原理。
原来的dao层每一个实现类内部都需要注入JdbcTemplate,可以编写一个父类JdbcDaoSupport让dao层实现类都继承该类。
JdbcDaoSupport.java
public class JdbcDaoSupport {
private JdbcTemplate jdbcTemplate;
// 只注入dataSource也可以get到jdbcTemplate对象
public void setDataSource(DataSource dataSource) {
jdbcTemplate = createJdbcTemplate(dataSource);
}
private JdbcTemplate createJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
AccountDaoImpl.java
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
@Override
public Account findAccountById(String id) {
List<Account> accounts = super.getJdbcTemplate().
query("select * from account where id=?",
new BeanPropertyRowMapper<>(Account.class), id);
return accounts.isEmpty() ? null : accounts.get(0);
}
@Override
public void updateAccount(Account account) {
// 使用super.getJdbcTemplate()获取到jdbcTemplate对象
super.getJdbcTemplate().
update("update account set name=?, money=? where id=?",
account.getName(), account.getMoney(), account.getId());
}
}
在xml中配置注入dataSource:
<bean id="accountDao" class="com.study.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource">
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver">
</property>
<property name="url" value="jdbc:oracle:thin:@localhost:1521/orcl">
</property>
<property name="username" value="springTest"></property>
<property name="password" value="tiger"></property>
</bean>
7、Spring的声明式事务控制
事务的接口spring-tx-5.0.2.RELEASE.jar中。
Spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。
7.1、Spring中的事务控制API
7.1.1、PlatformTransactionManager
PlatformTransactionManager接口提供事务操作的方法,包含3个具体的操作:
- 获取事务状态信息
TransactionStatus getTransaction(TransactionDefinition definition) - 提交事务
void commit(TransactionStatus status) - 回滚事务
void rollback(TransactionStatus status)
常用的实现类:
org.springframework.jdbc.datasource.DataSourceTransactionManager:使用Spring JDBC或iBatis进行持久化数据时使用。
org.springframework.orm.hibernate5.HibernateTransactionManager:使用Hibernate版本进行持久化数据时使用。
7.1.2、TransactionDefinition
它是事务的定义信息对象,里面有如下方法:
- String getName()
获取事务对象名称 - int getIsolationLevel()
设置事务隔离级别 - int getPropagationBehavior()
获取事务传播行为 - int getTimeout()
获取事务超时时间 - boolean isReadOnly()
获取事务是否只读
事务隔离级别反映事务提交并发访问时的处理态度
-
ISOLATION_DEFAULT
默认级别,采用数据库的隔离级别 -
ISOLATION_READ_UNCOMMITTED
可以读取未提交的数据 -
ISOLATION_READ_COMMITED
只能读取已提交的数据,解决脏读问题 -
ISOLCATION_REPEATABLE_READ
是否读取其他事务提交修改后的数据,解决不可重复读问题 -
ISOLATION_SERIALIZABLE
是否读取其他事务提交添加后的数据,解决幻影读问题。
事务传播行为:
-
REQUIRED
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值) -
SUPPORTS
支持当前事务,如果当前没有事务,就以非事务方式进行(没有事务) -
MANDATORY
使用当前的事务,如果当前没有事务,就抛出异常 -
REQUERS_NEW
新建事务,如果当前在事务中,就把当前事务挂起 -
NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 -
NEVER
以非事务方式运行,如果当前存在事务,抛出异常 -
NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
7.1.3、TransactionStatus
此接口提供的是事务具体的运行状态。
TransactionStatus接口描述了某个时间点上事务对象的状态信息,包含6个具体操作:
-
void flush()
刷新事务 -
boolean hasSavepoint()
获取是否存在存储点(按步提交回滚,类似游戏存档) -
boolean isCompleted()
获取事务是否完成 -
boolean isNewTransaction()
获取事务是否为新事务 -
boolean isRollbackOnly()
获取事务是否回滚 -
void setRollbackOnly()
设置事务回滚
7.2、基于xml的声明式事务配置
编写service层代码,里面不手动加事务代码:
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public Account findAccountById(String id) {
return accountDao.findAccountById(id);
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
Account sourceAccount = accountDao.findAccountByName(sourceName);
Account targetAccount = accountDao.findAccountByName(targetName);
sourceAccount.setMoney(sourceAccount.getMoney() - money);
targetAccount.setMoney(targetAccount.getMoney() + money);
accountDao.updateAccount(sourceAccount);
// int i = 1 / 0;
accountDao.updateAccount(targetAccount);
}
}
在xml配置的步骤:
- 配置事务管理器的bean
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">
</property>
</bean>
- 配置事务通知
使用<tx:advice>配置事务通知。
<tx:advice>标签属性:
- id
给事务通知起一个唯一标识
- transaction-manager
给事务通知提供一个事务管理器引用
示例:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
</tx:advice>
3.配置<aop:config>中通用切入点表达式
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut id="pt1"
expression="execution(* com.study.service.impl.*.*(..))"/>
</aop:config>
在<aop:config>标签中使用<aop:advisor>标签建立切入点表达式和事务通知的对应关系
<aop:config>
<aop:pointcut id="pt1"
expression="execution(* com.study.service.impl.*.*(..))"/>
<!-- 建立切入点表达式和事务通知的对应关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1">
</aop:advisor>
</aop:config>
- 在
<tx:advice>
标签中使用<tx:attributes><tx:method>
标签配置事务的属性,对某些aop切入的某些方法指定事务配置。
<tx:method>
标签的属性:
- name
具体的方法名或者带有通配符的表达式。
例如:get表示所有方法名以get开头的方法 - isolation
指定事务的隔离级别(默认是DEFAULT,表示使用数据库的默认隔离级别) - paropagation
用于指定事务的传播行为,一般增删改使用REQUIRED,查询方法使用SUPPORTS(默认值是REQUIRED,表示一定会有事务) - read-only
用于指定事务是否只读,只有查询方法才能设置true。(默认值是false) - timeout
用于指定事务的超时时间,以秒为单位。(默认值是-1,表示永不超时) - rollback-for
用于指定一个异常。当产生该异常时,事务回滚;产生其他异常时,事务不回滚。
(没有默认值,表示任何异常都回滚) - no-rollback-for
用于指定一个异常。当产生该异常时,事务不回滚;产生其他异常时,事务回滚。
(没有默认值,表示任何异常都回滚)
示例:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- transfer方法的配置 -->
<tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
<!-- 以find开头的方法的配置 -->
<tx:method name="find*" propagation="SUPPORTS" read-only="true">
</tx:method>
<!-- 剩余的所有方法的配置 -->
<tx:method name="*" propagation="REQUIRED" read-only="false">
</tx:method>
</tx:attributes>
</tx:advice>
7.3、基于注解的声明式事务配置
需要引入的xml约束:xmlns:context、xmlns:tx
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
配置步骤:
- 配置事务管理器
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
- 使用
<tx:annotation-driven>
标签开启spring对注解事务的支持
标签属性:
- transaction-manager
事务管理器的bean
<tx:annotation-driven transaction-manager="transactionManager">
</tx:annotation-driven>
- 在需要事务支持的地方使用@Transactional注解
该注解可用于类、方法上,指定该类中所有切入点方法或类中某一个切入点方法的事务属性配置。
该注解有propagation、isolation、readOnly等属性,可对事务的属性进行配置。
如果需要手动回滚,可以使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
例如:
@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService {
}
7.4、纯注解配置的声明式事务配置
- 在resources中创建数据库连接配置文件:jdbcConfig.properties
jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.userName=springTest
jdbc.password=tiger
jdbc.url=jdbc:oracle:thin:@localhost:1521/orcl
- 创建数据库连接配置类
public class JdbcConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.userName}")
private String userName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.password}")
private String password;
@Bean("jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean("dataSource")
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUsername(userName);
dataSource.setPassword(password);
dataSource.setUrl(url);
return dataSource;
}
}
3.创建事务配置类
public class TransactionConfiguration {
@Bean("transactionManager")
public PlatformTransactionManager createTransactionManager(
DataSource datasource) {
return new DataSourceTransactionManager(datasource);
}
}
4.创建总配置类
@Configuration
@ComponentScan("com.study")
@PropertySource("jdbcConfig.properties")
@EnableTransactionManagement // 开启事务支持
@Import({JdbcConfiguration.class, TransactionConfiguration.class})
public class SpringConfiguaration {
}
8、Spring的编程式事务控制
- 配置事务管理器
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">
</property>
</bean>
- 配置事务模板对象
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager">
</property>
</bean>
- 在业务层实现类的代码中加入事务模板对象
public class AccountServiceImpl implements IAccountService {
private TransactionTemplate transactionTemplate;
private IAccountDao accountDao;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public Account findAccountById(String id) {
// 带有返回值的事务模板
return transactionTemplate.execute(new TransactionCallback<Account>() {
@Override
public Account doInTransaction(TransactionStatus transactionStatus) {
return accountDao.findAccountById(id);
}
});
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
// 在事务模板中创建TransactionCallbace对象,把原来的业务逻辑放入该对象的doInTransaction方法中
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
Account sourceAccount = accountDao.findAccountByName(sourceName);
Account targetAccount = accountDao.findAccountByName(targetName);
sourceAccount.setMoney(sourceAccount.getMoney() - money);
targetAccount.setMoney(targetAccount.getMoney() + money);
accountDao.updateAccount(sourceAccount);
// int i = 1 / 0;
accountDao.updateAccount(targetAccount);
return null;
}
});
}
}
9、Spring如何在web应用中使用
9.1、需要额外导入的jar包
- spring-web-5.0.2.RELEASE.jar
- spring-webmvc-5.0.2.RELEASE.jar
9.2、配置文件
同spring在java应用的配置文件一样
9.3、如何创建IOC容器
- java应用中在main方法中直接创建
- web应用应该在web应用被服务器加载时就创建IOC容器
在ServletContextListener#contextInitialized(ServletContextEvent sce)方法中创建IOC容器。
创建IOC容器后,可以把其放在ServletContext(即application域)的一个属性中,供web应用的其他组件访问IOC容器。
Spring配置文件的名字和位置也应该设置为可配置的。将其配置在当前Web应用的初始化参数中较为合适。
9.4、示例代码
- 导入pom依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
2.编写监听器,实现ServletContextListener接口
public class SpringServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
// 获取配置文件的名称
ServletContext servletContext = servletContextEvent.getServletContext();
String configLocation = servletContext.getInitParameter("configLocation");
// 获取IoC容器,并配置到application域中
ApplicationContext ctx =
new ClassPathXmlApplicationContext(configLocation);
servletContext.setAttribute("ApplicationContext", ctx);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
3.编写测试的servlet
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从application域对象得到IoC容器的引用
ServletContext servletContext = getServletContext();
ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute("ApplicationContext");
// 从IoC容器中获取Bean
Person person = ctx.getBean(Person.class);
person.hello();
}
}
- 配置web.xml
<web-app>
<!-- 配置spring配置文件的名称 -->
<context-param>
<param-name>configLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
<!-- 配置自定义的Listener -->
<listener>
<listener-class>com.study.listeners.SpringServletContextListener</listener-class>
</listener>
<!-- 测试servlet -->
<servlet>
<servlet-name>testServlet</servlet-name>
<servlet-class>com.study.servlets.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>
- 配置spring的配置文件: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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置bean的唯一标识和全限定类名 -->
<bean id="person" class="com.study.beans.Person">
<property name="username" value="张三" />
</bean>
</beans>
- 编写javaBean类和测试jsp页面
9.5、Spring提供的Listener
web.xml的配置
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置spring配置文件的名称 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>testServlet</servlet-name>
<servlet-class>com.study.servlets.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>
在servlet中使用IoC容器
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从application域对象得到IoC容器的引用
ServletContext servletContext = getServletContext();
ApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
// 从IoC容器中获取Bean
Person person = ctx.getBean(Person.class);
person.hello();
}
}
标签:事务,spring,void,配置,基础,class,通知,public
From: https://www.cnblogs.com/cc-boy/p/17009432.html