SpringAOP与事物原理分析

一、SpringAOP概述 

什么事springaop? 面向切面编程
应用场景:权限控制、事务管理、日志打印、性能统计

项目中什么地方使用AOP 代码重复
在不同的方法中,但是需要相同的操作。

AOP
关注点:重复代码

切面:抽取重复代码

切入点:拦截哪些方法

springaop方式:注释、xml

二、SpringAop注解方式

spring.xml中开启事务权限:

<aop:aspectj-autoproxy />

<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!--<bean id="userService" class="cn.aop.service.UserService"></bean>-->
    <context:annotation-config />
    <context:component-scan base-package="cn.aop"/>
    <aop:aspectj-autoproxy />

</beans>

pom.xml

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>3.2.3.RELEASE</spring.version>
    </properties>

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.2</version>
    </dependency>


    <!-- spring aop 包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- @Transactional 事务注解包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- JdbcTemplate -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>

</dependencies>
@Service
public class UserService {
    public void add(){
        //int i =1/0;
        System.out.println("UserService.add...");
    }
}
import cn.aop.service.UserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

/**
 * spring aop 通知有哪些?
 * 前置通知(方法前处理)、后置通知(方法后处理)、异常通知(方法发生异常时处理)、环绕通知(方法前后一起通知)
 * 使用注解方式
 * -- @Aspect 定义切面
 * -- @Component 注入spring容器
 * Created by yz on 2018/03/18.
 */
@Aspect
@Component
public class AOP {
    /**
     * 前置通知 在add()方法前执行
     */
    @Before("execution(* cn.aop.service.UserService.add(..))")
    public void bean(){
        System.out.println("...前置通知...在add()方法前执行");
    }

    /**
     * 后置通知 在add()方法后执行
     */
    @After("execution(* cn.aop.service.UserService.add(..))")
    public void commit(){
        System.out.println("...后置通知...在add()方法后执行");
    }

    /**
     * 运行通知 在后置通知后执行
     */
    @AfterReturning("execution(* cn.aop.service.UserService.add(..))")
    public void afterRun(){
        System.out.println("...运行通知...在后置通知后执行");
    }

    /**
     * 异常通知 在add()方法发生异常后执行
     */
    @AfterThrowing("execution(* cn.aop.service.UserService.add(..))")
    public void afterThrowing(){
        System.out.println("...异常通知...在add()方法发生异常后执行");
    }

    /**
     * 环绕通知 在add()方法之前之后执行
     */
    @Around("execution(* cn.aop.service.UserService.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("我是环绕通知--前");
        // 这个方法表示执行add()方法
        proceedingJoinPoint.proceed();
        System.out.println("我是环绕通知--后");
    }


    public static void main(String[] args) {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) app.getBean("userService");
        userService.add();
    }
}

三、SpringAopXML方式 

<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!--<bean id="userService" class="cn.aop.service.UserService"></bean>-->
    <context:annotation-config />
    <context:component-scan base-package="cn.aop"/>
    <aop:aspectj-autoproxy />

    <!-- 定义切面 -->
    <bean id="aop" class="cn.aop.AopForXml"/>
    <!-- Aop配置 -->
    <aop:config>
        <!-- 定义一个切入点表达式:拦截哪些方法 当前配置拦截UserService里所有的方法-->
        <aop:pointcut id="pt" expression="execution(* cn.aop.service.UserService.*(..))"/>
        <!-- 切面 -->
        <aop:aspect ref="aop">
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pt"/>
            <!-- 前置通知,在目标方法前执行 -->
            <aop:before method="before" pointcut-ref="pt"/>
            <!-- 后置通知,在目标方法后执行 -->
            <aop:after method="after" pointcut-ref="pt"/>
            <!-- 返回后通知也叫运行通知-->
            <aop:after-returning method="afterRun" pointcut-ref="pt"/>
            <!-- 异常通知-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>

</beans>
import cn.aop.service.UserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * spring aop 通知有哪些?
 * 前置通知(方法前处理)、后置通知(方法后处理)、异常通知(方法发生异常时处理)、环绕通知(方法前后一起通知)
 * 使用xml方式
 * Created by yz on 2018/03/18.
 */
public class AopForXml {

    public void before(){
        System.out.println("...前置通知...在add()方法前执行");
    }

    public void after(){
        System.out.println("...后置通知...在add()方法后执行");
    }

    public void afterRun(){
        System.out.println("...运行通知...在后置通知后执行");
    }

    public void afterThrowing(){
        System.out.println("...异常通知...在add()方法发生异常后执行");
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("我是环绕通知--前");
        // 这个方法表示执行add()方法
        proceedingJoinPoint.proceed();
        System.out.println("我是环绕通知--后");
    }

    public static void main(String[] args) {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) app.getBean("userService");
        userService.add();
    }
}

四、Spring事物概述

什么是事务?
Spring事务
Spring传播行为,即一个方法里面又嵌套了一个方法,事务是怎么进行管理?
分布式事务

事务的目的:保证数据的一致性
事务的隔离级别:
原子性:要么全部成功,要么全部失败。
一致性:转账 A用户5000 B用户0 A给B转5000后 A用户0 B用户5000 A+B=5000
隔离性:多个事务之间互不影响,A事务,B事务互不关联
持久性:表示数据一旦到数据库之后,就不能再做其他操作了,比如保存到数据库就保存了。

Spring事务(面试题)
用spring的时候怎么去管理事务

class UserDao{
public void add(){
操作数据库
}
}

@Service
class UserServiceImpl implements IUserService{
private UserDao userDao;
public void add(){
userDao.add();
int i = 1/0;  // 回滚
}
}


spring事务分类:

编程事务:手动事务
声明事务:xml和注解

手动事务 -- 自己去begin、commit
声明事务 -- xml 定义切入点 execution(* cn.aop.service.UserService.*(..)) 进行事务管理

注解方式 -- @Transactional

五、Spring事物环境搭建 

编程事务(手动事务)

原理:获取到该数据源api,数据源api中,会自动封装手动begin、commit、rollback

/**
 * -- @Component 注入到spring 容器
 * Created by yz on 2018/03/18.
 */
@Component
public class TransactionUtils {
    private DataSourceTransactionManager dataSource;

    //开启事务
    public TransactionStatus begin(){
        TransactionStatus transaction = dataSource.getTransaction(new DefaultTransactionDefinition());
        return transaction;
    }
    //提交事务
    public void commit(TransactionStatus transaction){
        dataSource.commit(transaction);
    }
    //回滚事务
    public void rollback(TransactionStatus transaction){
        dataSource.rollback(transaction);
    }
}
/**
 * spring aop 通知有哪些?
 * 前置通知(方法前处理)、后置通知(方法后处理)、异常通知(方法发生异常时处理)、环绕通知(方法前后一起通知)
 * 使用xml方式
 * Created by yz on 2018/03/18.
 */
public class AopForXml {
    private TransactionStatus begin ;

    @Autowired
    private TransactionUtils transactionUtils;

    @Transactional
    public void before(){
        //System.out.println("...前置通知...在add()方法前执行");
    }

    public void after(){
        //System.out.println("...后置通知...在add()方法后执行");
    }

    public void afterRun(){
        //System.out.println("...运行通知...在后置通知后执行");
    }

    /**
     * 发生异常回滚事务
     */
    public void afterThrowing(){
        System.out.println("...异常通知...在add()方法发生异常后执行");
        transactionUtils.rollback(begin);
    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @throws Throwable
     */
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("我是环绕通知--前");
        begin = transactionUtils.begin();
        // 这个方法表示执行add()方法
        proceedingJoinPoint.proceed();
        System.out.println("我是环绕通知--后");
        transactionUtils.commit(begin);
    }

    public static void main(String[] args) {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) app.getBean("userService");
        userService.add();
    }
}
/**
 * Created by yz on 2018/03/18.
 */
@Repository("userDao")
public class UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void add(String name,Integer age){
        String sql = "insert into users(name,age)values(?,?)";
        int update = jdbcTemplate.update(sql, name, age);
        System.out.println("add:"+update);
    }
}
/**
 * Created by yz on 2018/03/18.
 */
public interface IUserService {
    public void add();
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

/**
 * 手动事务
 * 拦截方法,方法之前开启事务,方法之后提交事务,发生异常回滚事务
 * Created by yz on 2018/03/18.
 */
public class UserServiceImpl implements IUserService{
    @Autowired
    private UserDao userDao;

    @Autowired
    private TransactionUtils transactionUtils;

    public void add() {
        TransactionStatus begin = null;
        try {
            // 开启事务
            begin = transactionUtils.begin();
            userDao.add("小明",18);
            int i = 1/0;
            // 提交事务 , 如果发生异常,commit不执行,一百个添加相当于100个事务没被释放,
            // 一直占内存,需要手动回滚事务。
            transactionUtils.commit(begin);
        } catch (Exception e) {
            e.printStackTrace();
            // 发生异常事务回滚
            transactionUtils.rollback(begin);
        }
    }

    /**
     * 使用AOP声明式事务
     * 使用声明事务方式,方法一定不要try,将异常抛出去。或者在catch中throw 异常
     * 不然在try中内部消化异常,AOP无法捕获异常,就无法执行回滚语句。
     */
    public void add2() {
        userDao.add("小明",18);
        int i = 1/0;
    }

    /**
     * 使用注解方式
     * rollbackFor = Exception.class 这种设置是因为Spring的默认回滚RuntimeException,
     * 如果想要回滚Exception时,要设置@Transactional(rollbackFor = Exception.class)
     */
    @Transactional(rollbackFor = Exception.class)
    public void add3() {
        userDao.add("小明",18);
        int i = 1/0;
    }
}

spring.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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!--<bean id="userService" class="cn.aop.service.UserService"></bean>-->
    <context:annotation-config />
    <context:component-scan base-package="cn.aop"/>
    <aop:aspectj-autoproxy />

    <!-- 定义切面 -->
    <bean id="aop" class="cn.aop.AopForXml"/>
    <!-- Aop配置 -->
    <aop:config>
        <!-- 定义一个切入点表达式:拦截哪些方法 当前配置拦截UserService里所有的方法-->
        <aop:pointcut id="pt" expression="execution(* cn.aop.service.UserService.*(..))"/>
        <!-- 切面 -->
        <aop:aspect ref="aop">
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pt"/>
            <!-- 前置通知,在目标方法前执行 -->
            <aop:before method="before" pointcut-ref="pt"/>
            <!-- 后置通知,在目标方法后执行 -->
            <aop:after method="after" pointcut-ref="pt"/>
            <!-- 返回后通知也叫运行通知-->
            <aop:after-returning method="afterRun" pointcut-ref="pt"/>
            <!-- 异常通知-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
    
<import resource="classpath:db.xml"/>
</ beans >

db.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns="http://www.springframework.org/schema/beans"
       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">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/test" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 开启注解事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

六、Spring编程事物

事务开启后,一定要释放,提交释放,或者异常后手动回滚。

七、事物底层原理分析 

手动事务需要在每个方法之前之后手动去开启 提交 回滚,代码非常冗余。
使用AOP管理事务 -- 声明式事务(xml或注解)

声明事务原理剖析:
AOP编程 环绕通知 方法之前或者之后进行执行。

使用声明事务方式,方法一定不要try,将异常抛出去。
业务逻辑层不要使用try,将异常抛出给上一层。或者在catch中throw 异常。
不然在try中内部消化异常,AOP无法捕获异常,就无法执行回滚语句。
控制层进行捕获

事务原理:AOP编程+环绕通知+异常通知

环绕通知主要做begin和commit 异常通知主要做rollback

八、声明事物(XML与注解方式)

@Transactional
public void add3() {
    userDao.add("小明",18);
    int i = 1/0;
}

九、Spring事物传播行为

什么是事务传播行为?事务传递

事务传播行为分7种。


@Service
public class LogService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void addLog(){
        jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')");
        System.out.println("addLog添加完毕");
    }
}
 
/**
 * 操作两张表,默认日志跟着一起回滚
 */
@Transactional
public void add3() {
    logService.addLog();
    userDao.add("小明",18);
    int i = 1/0;
}

事务默认传播行为:REQUIRED

1.@Transactional(propagation = Propagation.REQUIRED)表示支持当前事务,如果当前没有事务,就新建一个事务。logService.addLog()没有事务,但是在add3()事务内,所以一起回滚。

2.@Transactional(propagation = Propagation.REQUIRES_NEW) 表示新建一个事务,不用外面的事务,互不影响。addLog添加成功,add事务回滚。

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addLog(){
    jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')");
    System.out.println("addLog添加完毕");
}
@Transactional(propagation = Propagation.REQUIRED)
public void add() {
    logService.addLog();
    userDao.add("明",18);
    int i = 1/0;
}

3.@Transactional(propagation = Propagation.SUPPORTS) 表示支持当前事务,如果当前有事务,就用当前事务,如果当前没有事务,就以非事务方式执行。

@Transactional(propagation = Propagation.SUPPORTS)
public void addLog(){
    jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')");
    System.out.println("addLog添加完毕");
}
@Transactional(propagation = Propagation.REQUIRED) // 当前有事务,按当前事务执行,addLog()会回滚
public void add() {
    logService.addLog();
    userDao.add("明",18);
    int i = 1/0;
}
public void add() {       // 当前没有事务,按非事务方式执行,addLog()不会回滚
    logService.addLog();
    userDao.add("明",18);
    int i = 1/0;
}

4.@Transactional(propagation = Propagation.NOT_SUPPORTED) 以非事务方式执行操作,如果当前有事务,就把当前事务挂起。addLog()不会回滚。

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void addLog(){
    jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')");
    System.out.println("addLog添加完毕");
}
@Transactional(propagation = Propagation.REQUIRED)
public void add() {
    logService.addLog();
    userDao.add("明",18);
    int i = 1/0;
}

5.@Transactional(propagation = Propagation.MANDATORY) 表示支持当前事务,如果当前没有事务,就抛异常。执行会抛异常。

@Transactional(propagation = Propagation.MANDATORY)
public void addLog(){
    jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')");
    System.out.println("addLog添加完毕");
}
public void add() {
    logService.addLog();
    userDao.add("明",18);
    int i = 1/0;
}
6.@Transactional(propagation = Propagation.NEVER) 以非事务方式执行,如果当前存在事务,就抛异常。执行抛异常。
 
@Transactional(propagation = Propagation.NEVER)
public void addLog(){
    jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')");
    System.out.println("addLog添加完毕");
}
@Transactional(propagation = Propagation.REQUIRED)
public void add() {
    logService.addLog();
    userDao.add("明",18);
    int i = 1/0;
}

7.@Transactional(propagation = Propagation.NESTED) 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。外层失败,addLog()会回滚。

@Transactional(propagation = Propagation.NESTED)
public void addLog(){
    jdbcTemplate.update("insert into log values(null,'"+System.currentTimeMillis()+"')");
    System.out.println("addLog添加完毕");
}
@Transactional(propagation = Propagation.REQUIRED)
public void add() {
    logService.addLog();
    userDao.add("明",18);
    int i = 1/0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值