Spring AOP讲解

Spring事务原理分析(三)--事务代理调用过程 目录 一、概述 二、事务代理调用过程分析 三、createTransactionIfNecessary()创建事务 四、invocation.proceedWithInvocation()执行增强方法 五、commitTransactionAfterReturning(txInfo)提交事务 六、completeTransactionAfterThrowing(txInfo, ex)异常回滚事务 七、总结 一、概述 通过前面一篇文章,我们已经了解了AOP切面织入生成代理对象的过程,当B 阅读详情

大家好,我是一个爱举铁的程序员Shr。

 

本文介绍Spring框架中AOP(Aspect Oriented Programming面向切面编程)的概念。阅读本篇文章可能需要30分钟。

 

Spring使用AspectJ实现了AOP,提供XML配置方式注解配置方式

 

AOP中用到了代理模式。

 

先讲几个概念。

参考:https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#aop-introduction-defn

 

Aspect(切面):横切关注点的抽象。切入点通知形成了切面

 

Join point(连接点):被拦截到的点。在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点,实际上Join point还可以是field或类构造器。

 

Advice(通知):拦截到Join point之后要做的事情就是通知。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。用来区别在Joint point之前、之后执行代码。

 

Pointcut(切入点):切入点是指对连接点进行拦截的定义。指定一个规则,拦截哪些方法。

 

Introduction(引入):不修改类代码的前提下,在运行期间为类动态地添加一些方法或字段。

 

Target object(目标对象):代理的目标对象。

 

AOP proxy(AOP代理):由AOP框架创建的对象。AOP代理是JDK动态代理或CGLIB代理。

 

Weaving(编织):将aspects应用到target对象并导致proxy对象创建的过程。

 

在上面几个概念中。

连接点可以直接理解成方法。

切入点就是拦截这些方法的规则。

通知就是拦截到方法后所做的事情。

切面就是切入点通知,因为事情可能是在被拦截到的方法之前做的或者之后做的。

 

接下来通过一段代码来更深刻得了解一下这些概念。

 

一、用代码了解AOP的概念

1.1 创建实体类Student

package com.shrmus.spring.aop.pojo;

public class Student {
    private int id;
    private String name;
    private int age;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

 

1.2 目标对象

1.2.1 接口

package com.shrmus.spring.aop.service;

public interface StudentService {
    public void addStudent();
    public void getStudentList();
    public void updateStudent();
}

 

1.2.2 实现类

package com.shrmus.spring.aop.service.impl;

import com.shrmus.spring.aop.service.StudentService;

/**
 * Target object 目标对象
 * <p>Title:StudentServiceImpl</p>
 * <p>Description:</p>
 * @author Shr
 * @date 2018年7月31日上午1:09:48
 * @version
 */
public class StudentServiceImpl implements StudentService{

    /**
     * Joint point 连接点
     * 添加一个学生的信息
     */
    public void addStudent() {
        System.out.println("addStudent()");
    }

    /**
     * Joint point 连接点
     * 查找所有学生信息
     */
    public void getStudentList() {
        System.out.println("getStudentList()");
    }

    /**
     * Joint point 连接点
     * 修改学生信息
     */
    public void updateStudent() {
        System.out.println("updateStudent()");
    }
}

 

1.3 通知

参考官方文档https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#aop-api-advice-types实现一个通知。

package com.shrmus.spring.aop.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * Advice 通知
 * <p>Title:TxAdvice</p>
 * <p>Description:</p>
 * @author Shr
 * @date 2018年7月31日上午1:31:32
 * @version
 */
public class MyAdvice implements MethodInterceptor{
    /**
     * 在被拦截到的方法前后做相应的操作,也就是前置通知,后置通知等
     * @param invocation 在方法调用的时候截取程序
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("调用方法前....");
        System.out.println("调用方法:" + invocation.getMethod().getName());
        // 获取正在调用的方法
        Object proceed = invocation.proceed();
        System.out.println("调用方法后...");
        return proceed;
    }
}

 

这里值得一提的是方法拦截器org.aopalliance.intercept.MethodInterceptororg.aopalliance.aop.Advice的子接口。

 

而方法调用org.aopalliance.intercept.MethodInvocationorg.aopalliance.intercept.Joinpoint的子接口。

 

如果是单独实现前置通知或者后置通知等,则需要实现相应的接口,如前置通知的接口org.springframework.aop.MethodBeforeAdvice。具体请参考文档。

 

1.4 测试类

package com.shrmus.spring.aop.test;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;

import com.shrmus.spring.aop.advice.MyAdvice;
import com.shrmus.spring.aop.service.StudentService;
import com.shrmus.spring.aop.service.impl.StudentServiceImpl;

/**
 * 测试类
 * <p>Title:AOPTest</p>
 * <p>Description:</p>
 * @author Shr
 * @date 2018年7月31日上午4:43:08
 * @version
 */
public class AOPTest {
    public static void main(String[] args) {
        // 1、Pointcut 切入点,指定一个规则,拦截哪些方法。
        JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
        pointcut.setPatterns(".*add.*",".*update.*");

//        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
//        pointcut.addMethodName("add*");
//        pointcut.addMethodName("update*");

//        // 2、Advice 通知,表示对拦截到的方法加上新功能(如日志,事务等)
        MyAdvice advice = new MyAdvice();
//
//        // 3、Pointcut 切入点 + Advice 通知 = Aspect 切面(横切关注点)
        Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

        // 4、AOP proxy AOP代理
        // 创建目标对象
        StudentService studentService = new StudentServiceImpl();
        ProxyFactory proxyFactory = new ProxyFactory(studentService);
        // 添加通知或切面,可以添加多个
//        proxyFactory.addAdvice(advice);
        proxyFactory.addAdvisor(advisor);
        // 生成代理对象
        StudentService studentServiceProxy = (StudentService) proxyFactory.getProxy(); 

        // 5、使用代理对象
        studentServiceProxy.addStudent();
        System.out.println();

        studentServiceProxy.getStudentList();
        System.out.println();

        studentServiceProxy.updateStudent();
    }
}

 

第一步

参考官方文档https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#aop-api-pointcuts-regex设置一个Pointcut(切入点),添加拦截方法的规则。另外在注释中还有另一种方式添加切入点。

 

第二步

实例化一个通知对象。

 

第三步

参考官方文档https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#aop-api-advisor实例化一个切面。

 

第四步

参考官方文档https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#aop-prog生成代理对象。

 

第五步

使用代理对象调用方法。

 

控制台输出:

调用方法前....

调用方法:addStudent

addStudent()

调用方法后...

 

getStudentList()

 

调用方法前....

调用方法:updateStudent

updateStudent()

调用方法后...

 

AOP的几个概念就讲到这里了,AOP常用作记录日志,事务管理等。

 

二、在Spring配置文件中的配置

2.1 引入命名空间

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

</beans>

 

引入aop和tx的命名空间,这样就能使用aop和tx开头的标签了。

 

2.2 切入点表达式

参考官方文档https://docs.spring.io/spring/docs/4.3.17.RELEASE/spring-framework-reference/htmlsingle/#aop-pointcuts-examples设置拦截方法的规则。

 

拦截所有public修饰的方法:

execution(public * *(..))

拦截任何set开头的方法:

execution(* set*(..))

拦截com.shrmus.spring.aop.service.impl包下的所有方法:

execution(* com.shrmus.spring.aop.service.impl.*.*(..))

拦截com.shrmus.spring.aop包及其子包下的所有方法:

execution(* com.shrmus.spring.aop..*.*(..))

 

公式就是:execution(修饰符 返回值类型 类的全限定名.方法名(参数))

 

返回值类型可不写。

 

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

    <!-- 设置自动扫描包和扫描bean,扫描包和子包下的类,多个包用逗号隔开 -->
    <!-- <context:component-scan base-package="com.shrmus.spring.ioc"></context:component-scan> -->

    <bean id="studentService" class="com.shrmus.spring.aop.service.impl.StudentServiceImpl"></bean>

    <!-- 配置通知对象 -->
    <bean id="myAdvice" class="com.shrmus.spring.aop.advice.MyAdvice"></bean>

    <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut expression="execution(* com.shrmus.spring.aop.service.impl.StudentServiceImpl.*(..))" id="myPointcut"/>

        <!-- 配置切面 -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="myPointcut"/>

        <!-- 配置通知类型 -->
        <aop:aspect ref="myAdvice">
            <!-- 前置通知 ,在方法执行前运行-->
            <aop:before method="before" pointcut-ref="myPointcut"/>

            <!-- 环绕通知,可以控制方法的执行-->
            <aop:around method="around" pointcut-ref="myPointcut"/>

            <!-- 异常通知,在匹配方法抛出异常退出时执行-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>

            <!-- 最终通知,在匹配方法退出后执行,不论一个方法是如何结束的,最终通知都会运行-->
            <aop:after method="after" pointcut-ref="myPointcut"/>

            <!-- 后置通知,在方法完全执行后运行-->
            <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
        </aop:aspect>

    </aop:config>

</beans>

 

2.4 修改MyAdvice类

在com.shrmus.spring.aop.advice.MyAdvice类中添加几个通知类型的方法。

    public void before() {
        System.out.println("前置通知...");
    }

    public void around() {
        System.out.println("环绕通知...");
    }

    public void after() {
        System.out.println("最终通知...");
    }

    public void afterReturning() {
        System.out.println("后置通知...");
    }

    public void afterThrowing() {
        System.out.println("异常通知...");
    }

 

2.5 测试类

package com.shrmus.spring.aop.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.shrmus.spring.aop.service.StudentService;

public class StudentTest {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-aop.xml");
        StudentService userService = (StudentService) ac.getBean("studentService");

        userService.addStudent();
        System.out.println();

        userService.getStudentList();
        System.out.println();

        userService.updateStudent(); 
    }
}

 

控制台打印结果:

调用方法前....

调用方法:addStudent

前置通知...

环绕通知...

最终通知...

后置通知...

调用方法后...

 

调用方法前....

调用方法:getStudentList

前置通知...

环绕通知...

最终通知...

后置通知...

调用方法后...

 

调用方法前....

调用方法:updateStudent

前置通知...

环绕通知...

最终通知...

后置通知...

调用方法后...

 

2.6 修改配置文件

可以看到控制台没有再打印调用方法时,方法内部打印的语句了,是因为配置了环绕通知。把配置文件中配置环绕通知的语句注释掉。

<!-- 环绕通知,可以控制方法的执行-->
<!-- <aop:around method="around" pointcut-ref="myPointcut"/> -->

 

2.7 修改目标对象

目标对象是com.shrmus.spring.aop.service.impl.StudentServiceImpl,现在我们要测试异常通知。修改getStudentList()方法。

    /**
     * Joint point 连接点
     * 查找所有学生信息
     */
    public void getStudentList() {
        System.out.println("getStudentList()");
        int i = 1/0;
    }

 

2.8 再次测试

测试类的代码不需要修改,直接运行,控制台打印结果:

调用方法前....

调用方法:addStudent

前置通知...

addStudent()

最终通知...

后置通知...

调用方法后...

 

调用方法前....

调用方法:getStudentList

前置通知...

getStudentList()

异常通知...

最终通知...

Exception in thread "main" java.lang.ArithmeticException: / by zero

 

可以看到注释掉环绕通知后,被调用的方法内部的语句打印出来了。

 

而第二个被调用的方法出现异常之后没有再调用后置通知。

 

这些就是通知的类型了。

 

三、事务

前面说过,AOP常用于日志记录和事务处理,接下来讲一讲事务处理。

 

3.1 数据库

CREATE TABLE `jdbc01_student` (
  `stu_id` int(11) NOT NULL auto_increment,
  `stu_name` varchar(20) default NULL,
  `stu_age` int(11) default NULL,
  `stu_gender` int(11) default NULL,
  PRIMARY KEY  (`stu_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

3.2 实体类

package com.shrmus.spring.tx.pojo;

import java.io.Serializable;

public class Student implements Serializable{
    private int id;
    private String name;
    private int age;
    private int gender;

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + "]";
    }

    public Student() {

    }

    public Student(String name, int age, int gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public Student(int id, String name, int age, int gender) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getGender() {
        return gender;
    }
    public void setGender(int gender) {
        this.gender = gender;
    }
}

 

3.3 数据源文件

jdbc.url=jdbc:mysql://localhost:3306/design-pattern-20180602?characterEncoding=utf8
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=shrmus

 

3.4 配置文件

配置文件名是applicationContext-tx.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"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- 加载数据库属性配置文件 -->
    <context:property-placeholder location="classpath:dbconfig.properties" ignore-unresolvable="true" />

    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <!-- 数据库基本信息配置 -->
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- 配置Jdbc模板 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        装配数据源
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean> -->

    <!-- 注入 -->
    <bean id="studentDao" class="com.shrmus.spring.tx.dao.impl.StudentDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

    <bean id="studentService" class="com.shrmus.spring.tx.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"></property>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 配置通知属性 -->
    <tx:advice id="myAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="get*" propagation="REQUIRED" read-only="true" />
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut expression="execution(* com.shrmus.spring.tx.service.impl.StudentServiceImpl.*(..))" id="myPointcut" />
        <!-- 配置切面 -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="myPointcut" />
    </aop:config>

</beans>

 

3.5 Dao

3.5.1 接口

package com.shrmus.spring.tx.dao;

import java.util.List;

import com.shrmus.spring.tx.pojo.Student;

public interface StudentDao {
    public List<Student> getStudentList();
    public void addStudent(Student student);
}

 

3.5.2 实现类

package com.shrmus.spring.tx.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.shrmus.spring.tx.dao.StudentDao;
import com.shrmus.spring.tx.pojo.Student;

/**
 * 
 * <p>Title:StudentDao</p>
 * <p>Description:</p>
 * @author Shr
 * @date 2018年7月31日下午7:12:54
 * @version
 */
public class StudentDaoImpl implements StudentDao{
    private JdbcTemplate jdbcTemplate;

    /**
     * 查找所有学生
     * @return
     */
    public List<Student> getStudentList(){
        String sql = "select stu_id,stu_name,stu_age,stu_gender from jdbc01_student";

        List<Student> studentList = jdbcTemplate.query(sql, new RowMapper<Student>() {
            @Override
            public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
                Integer id = Integer.parseInt(rs.getString(1));
                String name = rs.getString(2);
                Integer age = Integer.parseInt(rs.getString(3));
                Integer gender = Integer.parseInt(rs.getString(4));
                return new Student(id,name,age,gender);
            }
        });
        return studentList;
    }

    /**
     * 添加一条记录
     * @param student
     * @return
     */
    public void addStudent(Student student) {
        String sql = "insert into jdbc01_student(stu_name,stu_age,stu_gender) "
                + "values(?,?,?)";
        jdbcTemplate.update(sql, student.getName(),+student.getAge(),student.getGender());
    }

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}

 

3.6 Service

3.6.1 接口

package com.shrmus.spring.tx.service;

import java.util.List;

import com.shrmus.spring.tx.pojo.Student;

public interface StudentService {
    public void addStudent(Student student);
    public List<Student> getStudentList();
    public void updateStudent();
}

 

3.6.2 实现类

package com.shrmus.spring.tx.service.impl;

import java.util.List;

import com.shrmus.spring.tx.service.StudentService;
import com.shrmus.spring.tx.dao.StudentDao;
import com.shrmus.spring.tx.pojo.Student;

/**
 * 事务
 * <p>Title:StudentServiceImpl</p>
 * <p>Description:</p>
 * @author Shr
 * @date 2018年7月31日下午7:13:13
 * @version
 */
public class StudentServiceImpl implements StudentService{
    private StudentDao studentDao;

    /**
     * 添加一个学生的信息
     */
    public void addStudent(Student student) {
        studentDao.addStudent(student);
    }

    /**
     * 查找所有学生信息
     */
    public List<Student> getStudentList() {
        List<Student> studentList = studentDao.getStudentList();
        return studentList;
    }

    /**
     * 修改学生信息
     */
    public void updateStudent() {
        // TODO 待实现
    }

    public StudentDao getStudentDao() {
        return studentDao;
    }

    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }
}

 

3.7 测试类

package com.shrmus.spring.tx.test;

import java.util.List;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.shrmus.spring.tx.pojo.Student;
import com.shrmus.spring.tx.service.StudentService;

public class StudentTest {

    @Test
    public void addStudent() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-tx.xml");
        StudentService userService = (StudentService) ac.getBean("studentService");
        Student student = new Student("hehe",20,1);
        userService.addStudent(student);
    }

    @Test
    public void getStudentList() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-tx.xml");
        StudentService userService = (StudentService) ac.getBean("studentService");
        List<Student> studentList = userService.getStudentList();
        System.out.println(studentList);
    }
}

 

3.8 验证结果

3.8.1 验证查找

先来看看数据库原来的数据

只有两条数据。

 

执行测试类中的getStudentList()方法,控制台打印结果:

[Student [id=1, name=张三, age=20, gender=1], Student [id=2, name=李四, age=33, gender=0]]

 

查找是没有问题的。

 

3.8.2 验证添加

执行测试类中的addStudent()方法,控制台是没有打印结果的,让我们看看数据库。

数据库中添加了一条数据,说明添加也没问题。

 

但是我们要验证事务是否正常。

 

3.8.3 验证事务管理

在Service的实现类中修改addStudent(Student student)方法。

    public void addStudent(Student student) {
        studentDao.addStudent(student);
        int i = 1/0;
    }

 

还是用原来的方法制造一个异常,然后执行测试类中的addStudent()方法。

 

再查看数据库。

还是3条数据。

 

把制造异常的那条语句注释掉。

        // int i = 1/0;

 

修改测试类的addStudent()方法中的数据。

    @Test
    public void addStudent() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-tx.xml");
        StudentService userService = (StudentService) ac.getBean("studentService");
        Student student = new Student("haha",22,1);
        userService.addStudent(student);
    }

 

执行之后再看数据库。

 

可以看到主键已经不是连续自增的了,就是刚刚那个异常导致添加数据失败。

好了,事务管理就到这里了。

 

总结

首先感谢你能看到总结,说明你还是有耐心的嘛,哈哈哈。

这篇文章主要是回顾之前学Spring的时候懵逼的知识,本人之前在学Spring的时候,配置文件都是直接复制粘贴,什么意思都不明白,现在有点时间再慢慢消化这些知识。

在做事务处理中用到了JdbcTemplate,之前没学过这个,只是听说过,正好在官方文档看到了这个东西,又看了一下。

Spring的IoC和AOP就结束了,如果后面又看了新东西,会继续补充。

 

AOP源代码:

https://github.com/ShrMus/Design-Pattern/tree/master/design-pattern-20180602/src/main/java/com/shrmus/spring/aop

 

事务管理源代码:

https://github.com/ShrMus/Design-Pattern/tree/master/design-pattern-20180602/src/main/java/com/shrmus/spring/tx

 

Spring AOP讲解 Spring AOP(Aspect-Oriented Programming) 即面向切面编程,是一种编程范式,旨在提高代码模块化,使得开发者可以将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,进而在不修改业务代码的前提下增加或修改这些关注点。通知类型包括前置通知(Before)、后置通知(After returning)、异常通知(After throwing)、最终通知(After),以及环绕通知(Around)。它通常是一个代理对象,是AOP框架生成的一个被通知对象的代理实例。 阅读详情

相关推荐

ESWA投稿全流程避坑指南:从LaTeX模板到审稿状态查询(附最新资源链接)

本文提供了一份详尽的ESWA期刊投稿全流程指南,涵盖从LaTeX模板深度适配、Cover Letter撰写,到Editorial Manager系统操作、审稿状态解析等关键环节。重点解析了投稿准备中的常见“雷区”与文件合规性要求,并分享了收到审稿意见后的高效修改与回复策略,旨在帮助研究者规避流程陷阱,提升投稿效率与成功率。

9o8p7i6u5y的博客 778

mybatis-拦截器

mybatis拦截器的作用与简单示例拦截器的机理拦截器的作用拦截器的实现拦截器的原理 拦截器的机理 MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: 在mybatis的mybatis-config.xml文件中配置plugins(插件),能够拦截四个类的部分方法。 Executor (update, query,...

qq_43474806的博客 2836

RK3576 USB3.0 DWC3控制器调试:PHY初始化失败排查指南

DWC3 是 Synopsys 公司推出的 USB3.0/3.1 控制器 IP,支持双角色(DRD)模式,可配置为 Host、Device 或 OTG。

小灰灰的博客 1122

Spring AOP环绕通知

环绕通知所用的架包和前置通知所用的架包一样,并且在bean中添加aop 通知实现接口MethodInterceptor 并且实现其中的public Object invoke(MethodInvocation invocation)方法 环绕通知的功能非常的强大 它可以实现前置通知,后置通知,和异常通知 前置通知和后置通知在try中实现, 使用result = invocation.proceed();来控制目标方法的执行 写在invocation.proceed();前面的通知,为前置通知 写在inv.

qq_43412810的博客 1042

Spring源代码解析(七):Spring AOP中对拦截器调用的实现

前面我们分析了Spring AOP实现中得到Proxy对象的过程,下面我们看看在Spring AOP中拦截器链是怎样被调用的,也就是Proxy模式是怎样起作用的,或者说Spring是怎样为我们提供AOP功能的; 在JdkDynamicAopProxy中生成Proxy对象的时候: Java代码  return Proxy.newProxyInstance(classLoader, proxiedIn...

weixin_33725272的博客 284

Spring aop 源码(上)

Import 注解主要用于导入其他配置类或组件。当你想把一个或多个类作为Bean注册到Spring容器中时,可以使用 @Import 来引入这些类。这个注解通常用在配置类上,可以导入其他配置类或普通的组件类。使用 @Import 注解时,导入的类将作为一个完整的类注册到Spring容器中,即Spring会为这些类创建Bean实例。这个注解对于模块化配置非常有用,因为它允许你将配置分散到多个配置类中,然后将它们组合到一起。

u014474768的博客 1422

Spring AOP讲解

技术,将横切关注点(如日志、事务、权限校验)与核心业务逻辑分离,提升代码的模块化和可维护性。在 Maven 项目中,需添加以下依赖(Spring Boot 项目推荐使用。掌握 Spring AOP 是构建高可维护性企业级应用的关键!是 Spring 框架的核心模块之一,旨在通过。Spring AOP 通过。

FAQEW的博客 887

SpringAOP简单讲解

SpringAOP简单讲解

qq_61313896的博客 2121

Spring 面向切面编程AOP 详细讲解

交叉业务。这些交叉业务几乎是通用的,不管你是做银行账户转账,还是删除用户数据。日志,事务处理,安全,这些都是需要做的。如果在每一个业务处理过程当中,都参杂这些交叉业务第一:交叉业务代码在多个业务流程中反复出现,显然这个交叉业务代码并没有得到充分的复用,并且修改这些交叉业务代码的话,需要修改多处。第二:程序员无法专注核心业务代码的编写,在编写核心业务代码的同时还需要处理这些交叉业务。使用 AOP 可以很轻松的解决以上问题。如下图:可以更好的理解 AOP思想。简单的说AOP:就是将与核心业务。

weixin_61635597的博客 993

SpringAOP面向切面案例讲解AOP核心概念

/接口//实现类//记录程序当前执行执行(开始时间)//业务执行万次i<10000;i++) {//记录程序当前执行时间(结束时间)//计算时间差//输出信息System.out.println("执行万次消耗时间:" + totalTime + "ms");i++) {//调用原始操作System.out.println("执行万次消耗时间:" + totalTime + "ms");

qq_41187116的博客 1007

Spring AOP 详细讲解

Spring AOP讲解文档

2301_79894935的博客 1064

Spring——aop讲解

如果一个类被打上了@Aspect就代表着他是一个切面类当使用注解 @Aspect 标注一个 Bean 后, 那么 Spring 框架会自动收集这些 Bean, 并添加到 Spring AOP 中, 例如:使用@Aspect 注解, 并不能将一个 Java 对象转换为 Bean, 因此我们还需要使用类似 @Component 之类的注解。使用 @Aspect 后, 这个类就会被排除在(自动代理) 机制之外。

xxxmine的博客 1257

Java中Spring AOP讲解

AOP(面向切面编程)通过预编译的方式 和 运行期动态代理的方式来实现程序功能统一维护的一种方式,是OOP(面向对象编程)的延续。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度减低,提高程序的可重用性,同时提高了开发效率。

qq_45830276的博客 371

Springaop讲解

问题1:AOP的作用是什么?问题2:连接点和切入点有什么区别,二者谁的范围大?问题3:请描述什么是切面?AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构OOP(Object Oriented Programming)面向对象编程作用:在不惊动原始设计的基础上为其进行功能增强。简单的说就是在不改变方法源代码的基础上对方法进行功能增强。Spring理念:无入侵式/无侵入式​​。

xn_xy19的博客 812

SpringAop讲解即三种实现

SpringAop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 . spring实现: 导入依赖 <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj&l

code_mzh的博客 1192

Spring 事务

单机版事务处理(Spring支持) 以上是单机版开发的核心步骤,主要是核心是 问题:在单机版系统中开发传统的MVC分层架构中如何保证 Service DAO层共用同一个连接对象呢答: 使用来实现共用一个对象核心要点:通过AOP方式来创建事务 4.1 事务开发的伪代码 通过配置文件的方式,如下 5.事务属性 隔离属性(isolation):隔离属性事务的并发问题传播属性:propagation只读属性:readonly设置事务为只读,提高事务运行的效率超时属性:timeout通过超时属性设置本事务最长

qhancode 501

Spring-AOP

AOP:面向方面编程 一个普通的类想要变成有特定功能的类 有以下四种方法 a.继承类 b.实现接口 c.注解 d.配置 使用xml实现: 实现接口方法 前置通知: 实现步骤: 1.jar aopaliance.jar aspectjweaver.jar 2.配置: ①.首先通过namespace(eclipse中)将aop整进去 ②:其中一个是普通的方法,另一个是通知,普通方法把他所在的类整到IOC 中,通知把他所在的类整到IOC中 ③:将两个方法进行关联 < aop :config>

qq_44729578的博客 277

Spring-AOP源码分析随手记(二)

这次来分析下切面的执行过程。 1.怎么看? 怎么开始看源码呢?就直接从被增强的方法调用那里打断点,看看怎么执行的: 然后就来到了这: 2.初步分析 里面有段: if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProx...

一些学习的记录,主要做个人知识总结整理 487

Spring源码学习(二)------ AOP

AOP有些特有的概念,如:advisor、advice和pointcut等等,使用或配置起来有点绕,让人感觉有些距离感,其实它的实现就是一组标准的设计模式的组合使用:Factory、Proxy、Chain of Responsibility,只要搞清楚这几个设计模式,读AOP的源码是比较容易的。首先看看ProxyFactoryBean这个类,这是AOP使用的入口,从AOP拿到的bean object就是ProxyFactoryBean.getObject得到的,从这条线下去,发现AOP就是通过Proxy模式

走向架构师之路 2万+

Spring5学习笔记(三、AOP开发)

文章目录静态代理设计模式为什么需要代理设计模式?问题代理设计模式概念名词解释代理开发的核心要素编码静态代理存在的问题Sring的动态代理开发Spring动态代理的概念搭建开发环境Spring动态代理的开发步骤动态代理细节分析Spring动态代理详解额外功能的详解MethodBeforeAdvice分析MethodInterceptor(方法拦截器)切入点详解切入点表达式1. 方法切入点表达式2. 类切入点表达式3. 包切入点表达式切入点函数切入点函数的逻辑运算AOP开发AOP的概念AOP的开发步骤切面的名词

小楊同学的笔记本 921

AD7177-2 AD7175-2, AD7172-2 AD717X-X多路复用模数转换器驱动C源码.zip

AD7177-2 AD7175-2, AD7172-2 AD717X-X多路复用模数转换器驱动C源码:* @file AD717X.c* @brief AD717X implementation file.* @devices AD7172-2, AD7172-4, AD7173-8, AD7175-2, AD7175-8, AD7176-2* AD7177-2ad717x_st_reg *AD717X_GetReg(struct ad717x_device *device, uint8_t reg_address){uint8_t i;ad717x_st_reg *reg = 0;if (!device || !device->regs)return 0;for (i = 0; i < device->num_regs; i++){if (device->regs[i].addr == reg_address){reg = &device->regs[i];break;

上一篇: Spring IoC讲解
下一篇: 注解和自定义注解
ShrMuscles
博客等级 码龄10年 49粉丝 23原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值