AOP注解方式
开启自动织入支持
在xml中开启
<!-- 开启aop注解支持 -->
<aop:aspectj-autoproxy />
<!-- 开启注解支持,同时强制指定代理机制为cglib -->
<aop:aspectj-autoproxy proxy-target-class="true" />
通过注解开启
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.simple.say")
@EnableAspectJAutoProxy //开启对AOP相关注解的处理
public class AppByAnnotation {}
定义一个类
@Component
@Slf4j
public class SayHelloImpl implements ISayHello {
@Override
public void sayHello() {
log.info("hello world!");
}
@Override
public void sayHello(String name) {
log.info("hello, " + name);
}
}
定义切面类,添加@Aspect注解,@Component注解,因为@Aspect注解没有让Spring作为组件bean扫描的能力,所以我们需要额外添加@Component注解
/**
* 定义一个切面,负责收集方法调用的入参、出参(返回值)
*/
@Slf4j
@Aspect // 表示该类是一个切面
@Component // Aspect切面首先必须也是一个普通的bean
public class MethodInOutAspect {
// 指定该方法是一个环绕通知,通知注解的参数代表引用一个切入点表达式
@Around("com.lanou3g.spring.GlobalPointcut.say_all_method()")
public Object aroundM(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取连接点方法的名称
String methodName = joinPoint.getSignature().getName();
// 获取连接点方法的参数
Object[] args = joinPoint.getArgs();
log.debug("[aroundM] "+methodName+"("+ Arrays.toString(args) +") 开始执行");
Object retuVal = joinPoint.proceed();
log.debug("[aroundM] "+methodName+"("+ Arrays.toString(args) +") 返回值: " + retuVal);
return retuVal;
}
}
定义切入点
/**
* 定义系统中所有用到的切入点表达式
*/
@Component
public class GlobalPointcut {
/**
* 通过@Pointcut注解定义切入点表达式
* 此处表达式含义:拦截com.lanou3g.spring.simple.say包下所有类(包括子包中所有类)中的所有方法
*/
@Pointcut("execution(* com.lanou3g.spring.simple.say..*.*(..))")
public void say_all_method() {}
}
定义通知方法
@Slf4j
@Aspect // 表示该类是一个切面
@Component // Aspect切面首先必须也是一个普通的bean
public class MethodInOutAspect {
@AfterReturning(pointcut = "com.lanou3g.spring.GlobalPointcut.say_all_method()", returning = "ret")
public Object afterRM(Object ret) {
log.debug("[afterRM] 返回值: " + ret);
return ret;
}
}
注解方式入口
/**
* 纯注解方式入口类
*/
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.simple.say")
@EnableAspectJAutoProxy //开启对AOP相关注解的处理
public class AppByAnnotation {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppByAnnotation.class);
ISayHello hello = ctx.getBean(ISayHello.class);
hello.sayHello("JinSaiSai");
}
}
Spring AOP的应用
声明式事务
XML配置方式
配置start
<?xml version="1.0" encoding="UTF-8"?>
<!-- 通过xml配置声明式事务,我们需要添加tx命名空间 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 通过xml方式配置Spring声明式事务 配置start -->
<context:component-scan base-package="com.lanou3g.spring.transaction" />
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="teacherDao" class="com.lanou3g.spring.transaction.dao.TeacherDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
配置数据源
jdbc.url=jdbc:mysql://localhost:3306/zzj100301
jdbc.driver=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root
jdbc.characterEncoding=utf8
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<property name="connectionProperties">
<props>
<prop key="characterEncoding">${jdbc.characterEncoding}</prop>
</props>
</property>
</bean>
配置事务管理器
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
配置事务AOP通知
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="insert*" rollback-for="ArithmeticException" />
<tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
</tx:attributes>
</tx:advice>
定义AOP配置(将上面的通知和表达式组装到一起
<aop:config>
<aop:pointcut id="all_dao_method" expression="execution(* com.lanou3g.spring.transaction.dao.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="all_dao_method" />
</aop:config>
查询数据库中的Student表
package com.lanou3g.spring.transaction.dao;
import com.lanou3g.spring.transaction.bean.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
// 此注解和@Component作用一样, 只是含有特定的语义(一般用来标注dao层的类)
@Repository
public class StudentDaoImpl {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 查询student表所有数据
* @return
*/
public List<Student> queryAll() {
List<Student> Students = jdbcTemplate.query("select * from Student",
new BeanPropertyRowMapper<Student>(Student.class));
return Students;
}
XML方式入口
/**
* 纯XML方式入口类
*/
@Slf4j
public class AppByTransactionXml {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("tx_conf.xml");
testQuery(ctx);
}
static void testQuery(ApplicationContext ctx) {
StudentDaoImpl studentDao = ctx.getBean(StudentDaoImpl.class);
List<Student> studentList = studentDao.queryAll();
for(Student student: studentList) {
System.out.println("id: " + student.getSid()+", " +
"sname: " + student.getSname());
}
}
注解配置方式
开启注解支持
/**
* 纯注解方式入口类
*/
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.transaction.annotation")
// 开启事务相关注解支持
@EnableTransactionManagement
public class AppByTransactionAnnotation {
}
配置数据源
/**
* 配置数据源
*/
@PropertySource("classpath:jdbc.properties")
@Component
public class MyDataSource extends DriverManagerDataSource{
public MyDataSource(@Value("${jdbc.driver}") String driver,
@Value("${jdbc.url}") String url,
@Value("${jdbc.user}") String userName,
@Value("${jdbc.password}") String password,
@Value("${jdbc.characterEncoding}") String characterEncoding) {
super.setDriverClassName(driver);
super.setUrl(url);
super.setUsername(userName);
super.setPassword(password);
Properties conProperties = new Properties();
conProperties.setProperty("characterEncoding", characterEncoding);
super.setConnectionProperties(conProperties);
}
}
配置事务管理器
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.transaction.annotation")
// 开启事务相关注解支持
@EnableTransactionManagement
public class AppByTransactionAnnotation {
/**
* 定义JdbcTemplate对象,Spring给我们封装了所有的JDBC操作
* @param dataSource
* @return
*
* @Bean相当于xml中的
* <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
* <property name="dataSource" ref="dataSource" />
* </bean>
*/
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
/**
* 定义事务管理器bean
* @param dataSource
* @return
*
* @Bean相当于xml中的
* <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
* <property name="dataSource" ref="dataSource" />
* </bean>
*
*/
@Bean("txManager")
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
查询数据库中的Teacher
// 此注解和@Component作用一样, 只是含有特定的语义(一般用来标注dao层的类)
@Setter
@Getter
@Repository //@Compenent
public class TeacherDaoImpl { //<bean id="teacherDaoImpl" class="com.lanou3g.spring.transaction.annotation.dao.TeacherDaoImpl">
@Autowired
private JdbcTemplate jdbcTemplate; //<propertype name="jdbcTemplate" ref="" />
/**
* 查询teacher表所有数据
* @return
*/
// 通过@Transactional 开启事务、设置事务属性
@Transactional(readOnly = true)
public List<Teacher> queryAll() {
int rows = jdbcTemplate.update("delete from teacher;");
System.out.println("影响了" + rows + "行.");
return null;
}
注解方式入口
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.transaction.annotation")
// 开启事务相关注解支持
@EnableTransactionManagement
public class AppByTransactionAnnotation {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean("txManager")
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppByTransactionAnnotation.class);
testQuery(ctx);
}
static void testQuery(ApplicationContext ctx) {
TeacherDaoImpl teacherDao = ctx.getBean(TeacherDaoImpl.class);
List<Teacher> teacherList = teacherDao.queryAll();
for(Teacher teacher : teacherList) {
System.out.println("id: " + teacher.getId()+", tname: "
+ teacher.getTname());
}
}
}
Spring事务的隔离级别和传播行为
七种传播行为
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
---|---|
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 |
常用的两个事务传播属性是1和4,即PROPAGATIONREQUIRED ,PROPAGATIONREQUIRES_NEW
< tx:advice / >默认配置如下:
- 传播行为(propagation)是
REQUIRED
- 隔离级别(isolation level)是
DEFAULT
- 事务默认可读写(read-write)
- 事务超时时间是数据库默认事务超时时间
- unchecked异常
RuntimeException
异常触发事务回滚,checked异常Exception
不会触发回滚
属性 | 是否必须 | 默认值 | 描述 |
---|---|---|---|
name | Yes | 与事务属性关联的方法名称。支持*通配符(如, get* , handle* , on*Event 等 | |
propagation | No | REQUIRED | 事务传播行为 |
isolation | No | DEFAULT | 事务隔离级别 仅当传播行为设置为 REQUIRED 或者 REQUIRES_NEW 时有效. |
timeout | No | -1 | 事务超时时间(单位:秒). 仅当传播行为设置为 REQUIRED 或者 REQUIRES_NEW 时有效. |
read-only | No | false | 设置读写事务或者只读事务. 仅当传播行为设置为 REQUIRED 或者 REQUIRES_NEW 时有效. |
rollback-for | No | 设置多个可以触发事务回滚的异常(多个用英文逗号隔开). 如,com.foo.MyBusinessException,ServletException. | |
no-rollback-for | No | 设置多个禁止触发事务回滚的异常(多个用英文逗号隔开).如,com.foo.MyBusinessException,ServletException. |
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="insert*" rollback-for="ArithmeticException" />
<tx:method name="query*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
五种隔离级别
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="insert*" rollback-for="ArithmeticException" />
<tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
</tx:attributes>
</tx:advice>
参考上面七种传播行为
回滚事务
回滚特定异常
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="insert*" rollback-for="ArithmeticException" /><!--设置回滚特定异常,可以有多个异常,用英文逗号隔开-->
<tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
</tx:attributes>
</tx:advice>
不回滚特定异常
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="insert*" no-rollback-for="ArithmeticException" /><!--设置不回滚特定异常,可以有多个异常,用英文逗号隔开-->
<tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
</tx:attributes>
</tx:advice>