Springday03 AOPSpring声明式事务Spring编程式事务

Posted halulu.me

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springday03 AOPSpring声明式事务Spring编程式事务相关的知识,希望对你有一定的参考价值。

AOP介绍

AOP (Aspect Oriented Programming) 即面向切面编程

作用

在不修改源码的情况下,可以对目标对象的方法进行增强

特点:

  • 非侵入式编程: 在不修改源码的情况下对已有方法进行增强
  • 提高代码复用: 增强的内容抽象成方法或者对象可重复使用
  • 统一管理维护: 抽象成独立的方法或对象方便后期维护管理

原理:

  • Spring AOP 实现的原理是动态代理技术
  • 底层支持两种动态代理
    • 当目标实现接口时采用JDK动态代理
    • 当目标没有实现接口采用Cglib动态代理(可配置统一使用Cglib)

AOP应用场景

​ 日志记录

​ 事务控制(spring的声明式事务)

​ 性能监控

​ 权限控制

术语:

1、Joinpoint(连接点):

在spring中,连接点指需要增强的所有方法(指的是那些要被增强功能的候选方法对象),spring只支持方法类型的连接点。

2、Pointcut(切入点)

所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。

3、Advice(通知/增强)

所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 通知的类型: 前置通知,后置通知,异常通知,最终通知,环绕通知。

4、Target(目标对象)

被代理的对象. 比如:对业务类增强,目标对象就是业务类

5、Weaving(织入):

织入指的是把增强用于目标对象,创建代理对象的过程。spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入。

6、Proxy(代理):

一个类被AOP织入增强后,即产生一个结果代理类。比如动态代理案例中的经纪人。

7、 Aspect(切面)

切面指的是切入点和通知的结合。

在这里插入图片描述
导入依赖

<!--aop: spring整合第三方的面向切面编程组件-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>

AOP切入点表达式

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

    <bean class="com.halulu.dao.Info" id="info"></bean>
    
    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.halulu..service.*.*(..))"/>
    </aop:config>
	<!--    execution(* com.halulu..service.*.*(..))
     修饰符省略
     返回值类型任意
     在com.halulu的子包中的servece包的任意类的任意方法
     参数任意-->

AOP常用标签说明

1、<aop:config> 作用:声明aop配置。
2、<aop:aspect> 作用:配置切面。
3、<aop:pointcut> 作用:配置切入点表达式。
4、<aop:before> 作用:配置前置通知
5、<aop:after-returning> 作用:配置后置通知
6、<aop:after-throwing> 作用:配置异常通知
7、<aop:after> 作用:配置最终通知
8、<aop:around> 作用:配置环绕通知

注意:

1、在配置文件xml中,后置通知<aop:after>必须在后置通知<aop:after-returning>和异常通知<aop:after-throwing> 之后,否则位置会出错。

2、建议使用环绕通知<aop:around>,功能更强大,位置也不会出错。

环绕通知
在这里插入图片描述
在这里插入图片描述

AOP–xml形式

public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        try {
            System.out.println("前置通知before");
            Object[] args = joinPoint.getArgs();
            System.out.println("args" + args[0] + " + " + args[1]);
            args[1] = 100;
            joinPoint.proceed(args);
            System.out.println("后置通知after-returing");
        } catch (Throwable throwable) {
            System.out.println("异常通知after-throwing");
        }finally {
            System.out.println("最终通知after");
        }
    }
    <bean class="com.halulu.dao.Info" id="info"></bean>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.halulu..service.*.*(..))"/>
        <aop:aspect ref="info">
            <aop:around method="around" pointcut-ref="pt"></aop:around>
        </aop:aspect>
    </aop:config>

AOP–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/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">

    <!--开启IOC注解扫描-->
    <context:component-scan base-package="com.halulu"></context:component-scan>

    <!--开启aop注解扫描-->
    <aop:aspectj-autoproxy/>

</beans>
//切面类
@Aspect //配置当前类为切面类
@Component //创建对象加入IOC
public class LogAspect {

    /*
    * 注解实现通知的注意:
    *    spring框架aop遗留bug介绍:各种通知的注解方式实现其中最终通知与后置通知的顺序是有问题。
    *    解决方案:推荐使用环绕通知注解实现
    * */

    //切入点, 当前的方法名就是切入点表达式的别名
    @Pointcut("execution(* com..service.impl.*.*(..))")
    public void pointcut(){}

    //前置通知方法,并调用切入点表达式方法
    //@Before("pointcut()")
    public void before(){
        System.out.println("前置通知...写入日志");
    }

    //后置通知方法
    //@AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("后置通知...写入日志");
    }

    //异常通知方法
    //@AfterThrowing("pointcut()")
    public void afterThrowing(){
        System.out.println("异常通知...写入日志");
    }

    //最终通知方法
    //@After("pointcut()")
    public void after(){
        System.out.println("最终通知...写入日志");
    }

    //环绕通知的方法
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint){
        Object result = null;
        try{

            //打印前置通知
            System.out.println("[环绕前置通知...写入日志]");

            //获取目标方法的名字
            String methodName = joinPoint.getSignature().getName();
            System.out.println("目标方法名字:"+methodName);

            //获取目标方法的参数列表
            Object[] args = joinPoint.getArgs();
            System.out.println("目标方法参数列表:"+ Arrays.toString(args));


            //执行目标方法
            result = joinPoint.proceed();

            //打印后置通知
            System.out.println("[环绕后置通知...写入日志]");

        }catch (Throwable e){

            //打印异常通知
            System.out.println("[环绕异常通知...写入日志]");

            e.printStackTrace();
            throw new RuntimeException(e);

        }finally {

            //打印最终通知
            System.out.println("[环绕最终通知...写入日志]");
        }

        return result;
    }
}

AOP–纯注解

package com.halulu.confing;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = "com.halulu")
@EnableAspectJAutoProxy
public class BeansConfig {

}

@Aspect
@Component
public class Info {

    @Pointcut("execution(* com.halulu..service.*.*(..))")
    public void pt(){}

    @Around("pt()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        try {
            System.out.println("前置通知before");
            Object[] args = joinPoint.getArgs();
            System.out.println("args" + args[0] + " + " + args[1]);
            joinPoint.proceed(args);
            System.out.println("后置通知after-returing");
        } catch (Throwable throwable) {
            System.out.println("异常通知after-throwing");
        }finally {
            System.out.println("最终通知after");
        }
    }

}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BeansConfig.class)
public class DemoTest {

    @Autowired
    private UserService userService;

    @Test
    public void test(){
        userService.sum(10,10);
    }
}

Spring声明式事务介绍

声明式事务将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。Spring声明式事务管理通过AOP实现。

Spring对不同持久层技术提供了不同的事务实现(事务管理器):

1、基于hibernate的事务管理: HibernateTransactionManager
2、基于JPA的事务管理理:JPATransactionManager
3、基于jdbc、mybatis、连接池的事务管理:DataSourceTransanctionManager

事务的传播行为:
在这里插入图片描述

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

    <!--
    目标:使用spring的声明式事务管理所有业务层的方法添加事务功能

    实现步骤
        1. 开启IOC注解扫描
        2. 加载外部配置文件jdbc.properties
        3. 创建连接池对象
        4. 创建JdbcTemplate,注入连接池对象
        5. 配置spring声明式事务

    -->

    <!--1.开启IOC注解扫描-->
    <context:component-scan base-package="com.halulu"></context:component-scan>

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

    <!--3.创建DataSource连接池对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!--4.创建JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--5.配置spring声明式事务-->
    <!--5.1 创建事务管理器并加入IOC容器-->
    <bean id="transactionManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--5.2 配置事务通知,配不同的方法设置不同的事务传播行为-->
    <tx:advice id="tx" transaction-manager="transactionManager">
        <tx:attributes>
            <!--给不用的方法名配置不同的事务,原则:查询不用事务,增删改使用事务
                <tx:method> 用于配置每个方法
                name="get*" 设置方法名以get开头的,查询方法开头还有 query/find/search/select...
                propagation="SUPPORTS", 设置事务的传播行为,这里不使用事务。如果不设置默认值:REQUIRED
                read-only="true",设置事务只读,查询都配置只读。如果不设置默认值:false, 适合增删改的方法

                <tx:method name="*"/> 剩余的所有其他方法,propagation="REQUIRED",read-only="false",代表所有增删改使用事务
            -->
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="query*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="search*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!--5.3 aop配置,将事务通知给到切入点去增强-->
    <aop:config>
        <aop:advisor advice-ref="tx" pointcut="execution(* com..service.impl.*.*(..))"></aop:advisor>
    </aop:config>
</beans>

注意:

<tx:advice>导入的是xmlns:tx="http://www.springframework.org/schema/tx

Spring声明式事务–xml+注解

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

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

    <!--1.开启IOC注解扫描-->
    <context:component-scan base-package="com.halulu"></context:component-scan>

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

    <!--3.创建DataSource连接池对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property Spring_事务

一文解析Spring编程式和声明式事务实例讲解

AOPSpring大法有多好约定编程

AOPspring 的AOP编程报错:[Xlint:invalidAbsoluteTypeName]error

源码学习之AOPSpring AOP使用

Springday01 maven依赖Spring介绍IOC模块IOC控制反转DI依赖注入SpEL表达式