Spring篇——03. Spring核心AOP和Spring声明式事务

开篇寄语
Spring有两大核心一个是控制反转IOC,一个是面向切面AOP
控制反转IOC也就相当于是一个Spring容器,我们将我们编写代码所需要的内容都存放在Spring的IOC容器中,让Spring来帮助我们管理,我们需要的时候直接调用就好,而不需要在出现new这些关键字来创建对象,他的底层使用了Java基础里面反射的原理。

面向切面AOP也同样是Spring的核心之一,这篇笔记就是用来介绍Spring中面向切面AOP的思想和如何在Spring中使用到这种思想!

同时本篇会讲讲spring的事务管理,因为Spring的事务管理是基于AOP来实现的,可以放在一起讲一讲

自己的知识水平不是很高,写的不好大家看了多多提出,一起交流,一起学习,共同进步!

一. AOP简介

1.1 概述

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
这一段百度百科里面都有的,都是一些概念,面向百度编程!!!
在我们学习了解AOP的时候,我们需要了解静态代理和动态代理,静态代理倒无所谓,但是动态代理一定要了解,在博客中Java基础分类中有提到动态代理,大家不明白的可以去看一看。

1.2 AOP作用和优势

作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少重复代码,提高开发效率,并且便于维护

1.3 AOP的底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

1.4 AOP的动态代理技术

常用的动态代理技术

1.4.1 JDK代理:基于接口的动态代理技术

基于接口的动态代理
需要有一个接口和接口实现类
AOP默认的动态代理技术为JDK代理技术
代码如下

package com.eason.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {
        //1. 创建目标对象
        final Target target = new Target();

        //2. 创建增强对象
        final Advice advice = new Advice();

        //3. 返回值就是动态生成的代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                //目标对象类加载器
                target.getClass().getClassLoader(),
                //目标对象相同的接口字节码对象数组
                target.getClass().getInterfaces(),
                //接口
                new InvocationHandler() {
                    //调用代理对象的任何方法,实质执行的都是invoke方法
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //前置增强
                        advice.before();
                        method.invoke(target,args); //执行目标方法,这里的invoke方法和上面的不同,这里的invoke方法指的是反射
                        //后置增强
                        advice.afterReturning();
                        return null;
                    }
                }
        );
        //调用代理对象的方法
        proxy.save();
    }
}
1.4.2 cglib代理:基于父类的动态代理技术

代码实现

package com.eason.proxy.cglib;

import com.eason.proxy.jdk.TargetInterface;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(final String[] args) {
        //1. 创建目标对象
        final Target target = new Target();

        //2. 创建增强对象
        final Advice advice = new Advice();

        //3. 返回值就是动态生成的代理对象,基于cglib
        //3.1 创建增强器
        Enhancer enhancer = new Enhancer();
        //3.2 设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3.3 设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //执行前置
                advice.before();
                //执行目标
                Object invoke = method.invoke(target, args);
                //执行后置
                advice.afterReturning();
                return null;
            }
        });
        //3.4 创建代理对象,这个是父子关系,可以使用Target创建
        Target proxy = (Target) enhancer.create();
        //调用代理对象的方法
        proxy.save();
    }
}

1.5 AOP相关概念

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:

  • Target(目标对象):代理的目标对象
  • Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点(通俗的来说就是可以被增强的方法叫做连接点,这个方法还未执行,只是说可以作为被增强的方法)
  • Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义(连接点真正配置可以被增强的叫切入点,切点就是连接点被执行之后的增强方法)
  • Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
  • Aspect(切面):是切入点和通知(引介)的结合
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入(就是将切点和增强通知结合的一个过程叫做织入)

1.6 AOP开发明确的事项

1.6.1 需要编写的内容
  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件中,配置织入关系,就是将哪些通知和哪些连接点进行结合
1.6.2 AOP技术实现的内容

Spring框架监控切入点方法的执行。一旦监控到切入点方法被执行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的相应位置,将通知对应的功能织入,完成完整的代码逻辑运行(简而言之就是spring框架会监控我们可以被增强的方法,如果监控到这个方法执行,就会创建一个动态代理,然后会根据增强的类型在代理对象执行相应的方法时执行相应的增强对象的增强)

1.7 AOP知识要点

  • aop:面向切面编程
  • aop底层实现:基于JDK的动态代理 和 基于Cglib的动态代理
  • Pointcut(切入点):被增强的方法 (相当于动态代理里面说的真实角色,业务逻辑的方法)
  • Advice(通知/ 增强):封装增强业务逻辑的方法 (相当于动态代理案例里面的log方法,用于增强切入点的方法)
  • Aspect(切面):切点+通知
  • Weaving(织入):将切点与通知结合的过程

二. 基于XML的AOP开发

2.1 快速入门

导入Maven依赖

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.4.RELEASE</version>
</dependency>
<!--织入关系的包-->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.13</version>
</dependency>

创建目标接口和目标类(内部有切点)
相当于动态代理里面的实现类和实现方法,我们要增强的就是这两个方法

package com.eason.aop;

/**
 * 目标接口,切点方法
 */
public interface TargetInterface {
    void save();
}
package com.eason.aop;

/**
 * 目标类
 */
public class Target implements TargetInterface {

    @Override
    public void save() {
        System.out.println("Target里的save方法打印");
    }
}

创建切面类(内部有增强方法)
这个是增强方法,相当于动态代理里面的通知

package com.eason.aop;

/**
 * 切面类
 */

public class MyAspect {

    public void before() {
        System.out.println("前置增强!");
    }

    public void after() {
        System.out.println("后置增强!");
    }
}

在applicationContext.xml文件中配置

<!--配置目标对象-->
<bean id="target" class="com.eason.aop.Target"></bean>

<!--配置切面对象-->
<bean id="myAspect" class="com.eason.aop.MyAspect"></bean>

<!--配置织入对象:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置增强或者是后置增强)-->
<aop:config>
    <!--声明切面,ref表示引用,切面为切点+通知-->
    <aop:aspect ref="myAspect">
        <!--切面为切点+通知-->
        <!--method:增强方法,pointcut:在执行哪个方法前执行前面的增强方法,相当于是被增强的方法-->
        <!--pointcut:切点表达式-->
        <aop:before method="before" pointcut="execution(public void com.eason.aop.Target.save())"></aop:before>

        <aop:after method="after" pointcut="execution(public void com.eason.aop.Target.save())"></aop:after>
    </aop:aspect>
</aop:config>

2.2 XML配置参数详解

2.2.1 切点表达式写法

表达式语法:

这个记住就好

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

例:execution(public void com.eason.aop.Target.save())

  • 访问修饰符可以省略不写
  • 返回值类型、包名、类名、方法名可以使用星号*代表任意
  • 包名与类名之间一个点. 代表当前包下的类,两个点… 表示当前包及其子包下的类
  • 参数列表可以使用两个… 表示任意个数,任意类型的参数列表

例:

  • execution(public void com.eason.aop.Target.method()) 中规中矩的写法,精确到某一个类中的方法
  • execution(void com.eason.aop.Target.*(…)) 说明到Target类下的任意方法,任意方法执行都会进行增强
  • execution(* com.eason.aop..(…)) 只限定到com.eason.aop包下的任意类的任意方法
  • execution(* com.eason.aop….(…)) 限定到aop包及其子包下的任意类的任意方法
2.2.2 通知的类型

通知的配置语法

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
<!--配置织入对象:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置增强或者是后置增强)-->
<aop:config>
    <!--声明切面,ref表示引用,切面为切点+通知-->
    <aop:aspect ref="myAspect">
        <!--切面为切点+通知-->
        <!--method:增强方法,pointcut:在执行哪个方法前执行前面的增强方法,相当于是被增强的方法-->
        <!--pointcut:切点表达式-->
        <!--前置增强-->
        <aop:before method="before" pointcut="execution(public void com.eason..Target.save())"></aop:before>
        <!--最终增强-->
        <aop:after method="after" pointcut="execution(public void com.eason.aop.*.*(..))"></aop:after>
        <!--环绕增强-->
        <aop:around method="around" pointcut="execution(public void com.eason.aop.*.*(..))"></aop:around>
        <!--异常抛出增强-->
        <aop:after-throwing method="afterThrowing" pointcut="execution(public void com.eason.aop.*.*(..))"></aop:after-throwing>

    </aop:aspect>
</aop:config>
2.2.3 切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。

<!--配置织入对象:告诉spring框架,哪些方法(切点)需要进行哪些增强(前置增强或者是后置增强)-->
<aop:config>
    <!--声明切面,ref表示引用,切面为切点+通知-->
    <aop:aspect ref="myAspect">
        <!--切面为切点+通知-->
        <!--method:增强方法,pointcut:在执行哪个方法前执行前面的增强方法,相当于是被增强的方法-->
        <!--pointcut:切点表达式-->
        <!--抽取切点表达式-->
        <aop:pointcut id="myPointcut" expression="execution(public void com.eason.aop.*.*(..))"/>
        <!--前置增强-->
        <aop:before method="before" pointcut-ref="myPointcut"></aop:before>
        <!--最终增强-->
        <aop:after method="after" pointcut-ref="myPointcut"></aop:after>
        <!--环绕增强-->
        <aop:around method="around" pointcut-ref="myPointcut"></aop:around>
        <!--异常抛出增强-->
        <aop:after-throwing method="afterThrowing" pointcut="execution(public void com.eason.aop.*.*(..))"></aop:after-throwing>

    </aop:aspect>
</aop:config>

三. 基于注解的AOP开发

基于注解就比XML配置文件都要简单了,但是我们在使用注解之前我们必须要理解XML文件的配置,不然的话会很迷的

3.1 快速入门

还是和XML的配置方法一样
创建目标接口和目标类(内部有切点)

package com.eason.aop_anno;
public interface TargetInterface {
    void save();
}
package com.eason.aop_anno;

/**
 * 目标类
 */
public class Target implements TargetInterface {

    @Override
    public void save() {
        System.out.println("Target里的save方法打印");
    }

}

创建切面类(内部有增强方法)

package com.eason.aop_anno;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 切面类
 */
//在类里面使用的注解@Component
@Component("myAspect")
@Aspect //直接就可以标注当前类MyAspect为一个切面类,不用在XML文件下配置了,很方便
public class MyAspect {

    /**
     * 执行前增强
     */
    public void before() {
        System.out.println("前置增强...........");
    }

    /**
     * 最终增强
     */
    public void after() {
        System.out.println("最终增强...........");
    }

    /**
     * 后置增强
     */
    public void after_returning() {
        System.out.println("后置增强...........");
    }

    /**
     * 环绕增强
     */
    //Proceeding JoinPoint:正在执行的连接点 === 切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强.......");
        Object proceed = pjp.proceed();//切点方法
        System.out.println("环绕后增强.......");
        return proceed;
    }

    /**
     * 异常抛出增强
     */
    public void afterThrowing() {
        System.out.println("异常抛出增强........");
    }
}

在切面类中使用注解配置织入关系
使用注解配置织入关系就不用在xml文件里面写那么冗长的配置了
使用了@Before(切入点),@After(切入点),@AfterReturning(切入点),@Around(切入点),@AfterThrowing(切入点)这几个注解在方法上进行注入
其中在@Around(切入点)环绕增强里面需要添加一个切点方法的执行点

Object proceed = pjp.proceed();//切点方法
package com.eason.aop_anno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 切面类
 */
@Component("myAspect")
@Aspect //标注当前类MyAspect为一个切面类
public class MyAspect {

    /**
     * 执行前增强,配置前置增强
     */
    @Before("execution(public void com.eason.aop_anno.*.*(..))")
    public void before() {
        System.out.println("前置增强...........");
    }

    /**
     * 最终增强
     */
    @After("execution(public void com.eason.aop_anno.*.*(..))")
    public void after() {
        System.out.println("最终增强...........");
    }

    /**
     * 后置增强
     */
    @AfterReturning("execution(public void com.eason.aop_anno.*.*(..))")
    public void after_returning() {
        System.out.println("后置增强...........");
    }

    /**
     * 环绕增强
     */
    //Proceeding JoinPoint:正在执行的连接点 === 切点
    @Around("execution(public void com.eason.aop_anno.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强.......");
        Object proceed = pjp.proceed();//切点方法
        System.out.println("环绕后增强.......");
        return proceed;
    }

    /**
     * 异常抛出增强
     */
    @AfterThrowing("execution(public void com.eason.aop_anno.*.*(..))")
    public void afterThrowing() {
        System.out.println("异常抛出增强........");
    }
}

在配置文件中开启组件扫描和 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"
       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/aop https://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


    <!--开启组件扫描-->
    <context:component-scan base-package="com.eason.aop_anno"></context:component-scan>

    <!--AOP自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

3.2 注解切点表达式的抽取

在切面类中添加切点方法

@Pointcut("execution(public void com.eason.aop_anno.*.*(..))")
public void myPointCut() {}

使用注解,然后增强方法进行调用即可
在注解里面加入切点(“MyAspect.myPointCut()”)

/**
 * 执行前增强,配置前置增强
 */
@Before("MyAspect.myPointCut()")
public void before() {
    System.out.println("前置增强...........");
}

/**
 * 最终增强
 */
@After("MyAspect.myPointCut()")
public void after() {
    System.out.println("最终增强...........");
}

/**
 * 后置增强
 */
@AfterReturning("MyAspect.myPointCut()")
public void after_returning() {
    System.out.println("后置增强...........");
}

/**
 * 环绕增强
 */
//Proceeding JoinPoint:正在执行的连接点 === 切点
@Around("MyAspect.myPointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("环绕前增强.......");
    Object proceed = pjp.proceed();//切点方法
    System.out.println("环绕后增强.......");
    return proceed;
}

/**
 * 异常抛出增强
 */
@AfterThrowing("MyAspect.myPointCut()")
public void afterThrowing() {
    System.out.println("异常抛出增强........");
}

四. Spring的事务管理

事务的特性

  • 把一组业务当成一个业务来做:事务都执行成功,要么都执行失败
  • 事务在我们的开发中,必须要保持完整性和一致性

事物的ACID原则

  • 原子性
  • 一致性
  • 隔离性:多个业务可能操作同一个资源,防止数据损坏
  • 持久性:事务一旦提交

简而言之就是上面所说的要么都成功,要么都失败

4.1 基于XML文件的声明式事务控制

4.1.1 概念

Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

4.1.2 作用
  • 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可
  • 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便
4.1.3 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: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/aop http://www.springframework.org/schema/aop/spring-aop.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">

    <!--加载jdbc配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!--配置数据源对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" 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"/>
    </bean>

    <!--为所需要的类注入依赖对象,为dao层的类注入jdbc模板对象依赖-->
    <bean id="accountDao" class="com.eason.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>

    <!--目标对象,内部方法就是切点,我们需要增强的方法-->
    <bean id="accountService" class="com.eason.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao" />
    </bean>

    <!--配置平台事务管理器,当我们集成jdbc来连接数据库的时候就使用DataSourceTransactionManager,这主要取决于dao层所用的技术-->
    <bean id="transactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!--通知  事务的增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManger">
        <!--设置事务的属性信息-->
        <tx:attributes>
            <!--isolation:事务隔离级别,propagation:事务传播行为,timeout:超时时间-->
            <!--这个相当于AOP中的设置增强方法的属性,在执行哪个增强方法的时候就设置哪个事务的不同事务属性来达到我们想要的效果-->
        
            <!--transfer方法-->
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
            <!--update开头的所有方法-->
            <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
            <!--所有方法-->
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

    <!--配置事务的AOP织入-->
    <aop:config>
        <!--事务特有的切面 aop:advisor-->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.eason.service.impl.*.*(..))" />
    </aop:config>

</beans>

这里面涉及到了一些JdbcTemplate操作数据库的一些方法,这里就不提JdbcTemplate了,大家想了解的话可以上网查一查,里面有些方法还是很值得我们去学习的。

tx:method 代表切点方法的事务参数的配置
切点参数详解

  • name:切点方法名称
  • isolation:事务的隔离级别
  • propogation:事务的传播行为
  • timeout:超时时间
  • read-only:是否只读
4.1.4 基于注解的声明式事务控制

声明事务不建议使用注解(个人认为使用XML文件会更好一点),当然仁者见仁,智者见智

编写AccountDao层

package com.eason.dao.impl;


import com.eason.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money=money-? where name=?",money,outMan);
    }

    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}

编写AccountService层

package com.eason.service.impl;


import com.eason.dao.AccountDao;
import com.eason.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("accountService")
@Transactional(isolation = Isolation.DEFAULT) //若在这个地方配的话那么这个类里的所有方法都遵循这个事务控制
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        //开启事务
        accountDao.out(outMan,money);
//        int i = 1/0;
        accountDao.in(inMan,money);
        //提交事务
    }

    @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
    public void xxx() {}
}

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: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/aop http://www.springframework.org/schema/aop/spring-aop.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">

    <!--组件扫描-->
    <context:component-scan base-package="com.eason" />

    <!--加载jdbc配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties" />


    <!--配置数据源对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" 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"/>
    </bean>

    <!--为所需要的类注入依赖对象,为dao层的类注入jdbc模板对象依赖-->
    <!--使用注解进行替代了-->

    <!--目标对象,内部方法就是切点,我们需要增强的方法-->
    <!--使用注解进行替代了-->

    <!--配置平台事务管理器,当我们集成jdbc来连接数据库的时候就使用DataSourceTransactionManager,这主要取决于dao层所用的技术-->
    <bean id="transactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!--通知  事务的增强,需要内部平台管理器-->
    <!--使用注解进行替代了-->

    <!--配置事务的AOP织入-->
    <!--使用注解进行替代了-->

    <!--事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManger"/>
</beans>

注解配置要点

  • 使用 @Transactional 在需要进行事务控制的类或是方法上修饰,注解可用的属性同 xml 配置方式,例如隔离级别、传播行为等。
  • 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。
  • 使用在方法上,不同的方法可以采用不同的事务参数配置。
  • Xml配置文件中要开启事务的注解驱动<tx:annotation-driven transaction-manager=“transactionManger”/>

五. 总结

Spring中面向切面编程AOP很重要,我们在掌握AOP之前需要将Java基础的静态代理技术动态代理技术类反射机制给理解,吃透,不然对于理解AOP的原理可能会有些困难,单单记AOP里面的各种切点,切面等等参数可能会有些生涩,学习动态代理之后就会清晰很多。

同时本篇涉及到了SpringJDBCTemplate的知识,但是没有讲,大家想了解的也可以到Spring官网了解了解或者百度了解了解,里面几个方法还是值得我们去学习的。

对于Spring声明式事务的底层就是AOP思想,要记住事务的ACID原则,事务在我们以后的程序编写中对于数据的保护还是有很大的作用的。

以上就是我对于Spring核心AOP和Spring声明式事务的一些理解,自己的知识水平不是很高,博客里面有些内容可能会有些错误,一些知识点可能也理解的不是很清晰,大家查看了有什么不对的可以多多指出,一起交流,共同学习,共同进步!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值