Spring框架下AOP事务的四种通知

spring最重要的六种基本jar:

commons-logging.jar
spring-expression.jar
spring-core.jar
spring-context.jar
spring-beans.jar
spring-aop.jar

本文还需两个jar:aopaliance.jar和aspectjweaver.jar!
如果本文的代码运行不出来,尝试添加以下的jar:(ojdbc的相关jar本文不需要,可以忽略)

在这里插入图片描述
applicationContext.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:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       default-autowire="byName"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">

现在步入正题!!!

AOP有四大通知类型:前置通知,后置通知,异常通知,环绕通知
先创建一个普通类,作为测试的方法

//该包位于Test
class function{
public void A(){
System.out.println("AAAAAAAAA");
}
public void B(int i){
int w = i/0;
System.out.println(w);
}
}

1.前置通知
前置通知会在指定的方法之前执行

//该类位于aop包
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LogBefore implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知啦啦啦...");
    }
}

接着在xml里面配置

<bean id="logBefore" class="aop.LogBefore"></bean>
<!--配置前置通知-->
<aop:config>
    <aop:pointcut id="poioncut" expression="execution(public void Test.function.A())"/>
    <aop:advisor advice-ref="logBefore" pointcut-ref="poioncut"></aop:advisor>
</aop:config>

之后执行A方法之前自动打印"前置通知啦啦啦…"
2.后置通知

//该类位于aop包
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class LogAfter implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知:目标对象:"+o1+",调用的方法名:"+method.getName()+",方法的参数个数:"+objects.length+",方法返回值"+o);
    }
}

配置xml

<bean id="logAfter" class="aop.LogAfter"></bean>
<!--配置前置通知-->
<aop:config>
    <aop:pointcut id="poioncut2" expression="execution(public void Test.function.A())"/>
    <aop:advisor advice-ref="logAfter" pointcut-ref="poioncut2"></aop:advisor>
</aop:config>

之后执行A方法之后自动打印*****
3.异常通知

//该类位于aop包
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class LogException implements ThrowsAdvice {
//根据异常通知接口的定义可以发现,异常通知的实现类 必须编写以下方法:
    //void afterThrowing([Method,args,target],ThrowableSubclass):
public void afterThrowing(Method method, Object[] args, Object target, Throwable ex){
    System.out.println("异常通知:目标对象:"+target+",方法名:"+method.getName()+",方法的参数:"+args.length+"异常类型:"+ex.getMessage());
}
}

配置xml

<bean id="logException" class="aop.LogException"></bean>
<aop:config>
    <aop:pointcut id="poioncut3" expression="execution(public void Test.function.B())"/>
    <aop:advisor advice-ref="logException" pointcut-ref="poioncut3"></aop:advisor>
</aop:config>
``

之后执行B方法遇到异常自动打印*****
4.环绕通知
环绕通知完全可以替代以上的三种通知,并且还会影响其他类型的通知,通知中的皇帝!

//该类位于aop包
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LogAround implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {//环绕通知的本质是通过环绕其实现的
        //方法体1...
        Object result = null ;
        try{
            //方法体2...
            System.out.println("用环绕通知实现的【前置通知】");
            //invocation.proceed()之前的代码:前置通知
            result = methodInvocation.proceed();//控制着目标方法的执行,addStudent
            //result就是目标方法addStudent()方法的返回值
               //invocation.proceed()之后的代码:后置通知
            System.out.println("用环绕通知实现的【后置通知】");
            System.out.println("目标对象target:"+methodInvocation.getMethod().getName()+"方法的参数个数:"+methodInvocation.getArguments().length+"方" +
                    "法的返回值:"+result);
 }catch (Exception e){
        //方法体3
        //异常通知
        System.out.println("用环绕通知实现的【异常通知】");
    }
    return result;
}
}

配置xml

<bean id="logAround" class="aop.LogAround"></bean>
<aop:config>
    <aop:pointcut id="poioncut4" expression="execution(public void Test.function.B()) or execution(public void Test.function.A())"/>
    <aop:advisor advice-ref="logAround" pointcut-ref="poioncut4"/>
</aop:config>

最后运行的结果大家应该能够想象出来。之所以我把它称之为通知中的皇帝,最主要的原因是当把LogAround类的return result 改为return 某个字符串,虽然环绕通知的返回值依旧是null,但我们惊奇的发现后置通知的返回值变成了该字符串!也就是他能修改方法的返回值!

基于注解形式的AOP

第一步,在IOC容器中声明对注解的支持:

<!--开启注解对AOP的支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

第二步编写通知类:
注意下面的@component,如果是通过这种方式将该实例纳入IOC容器,就别忘记配置扫描器~

package aop;

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

import java.lang.reflect.Array;
import java.util.Arrays;


//通过注解,把该类放入IOC容器(下面的注解相当于(<bean id="logAnnotation" class="***"></bean>))
@Component("logAnnotation")
@Aspect
public class LogAspectAnnotation {//@Aspect声明该类是个通知
    @Before("execution(public * serviceimpl.StudentServiceImpl.addStudent(..))")//属性定义切点
    public void myBefore(){//给任何方法加上Before,就会默认是前置通知
        System.out.println("《注解形式的前置通知...》");

    }
//    如果想通过注解形式  实现AOP,如果想获取 目标对象的一些参数,则需要使用一个对象:JoinPoint
    @AfterReturning(pointcut = "execution(public * serviceimpl.StudentServiceImpl.addStudent(..))",returning = "returningValue")
    public void myAfter(JoinPoint jp,Object returningValue){//只有有返回值的通知(后置通知)才需要Object参数拿返回值,还需要告诉Spring,就是在上面加kv对
        System.out.println("《注解形式的后置通知....》目标对象:"+jp.getTarget()+",方法" +
                "名:"+jp.getSignature().getName()+",参数列表:"+ Arrays.toString(jp.getArgs())+",返回值:"+returningValue);
    }

//如果只捕获特定类型的异常通知,则下面的myException的括号中加入 JoinPoint pj,**Exception e    注解后面还需要加上KV对
//@AfterThrowing(pointcut="execution(public * serviceimpl.StudentServiceImpl.chu(int))",throwing="e")
    //打印的时候加e.getMessage()
    @AfterThrowing("execution(public * serviceimpl.StudentServiceImpl.chu(int))")
    public void myException(){
        System.out.println("《注解形式-异常通知》...");
    }



    //注意:想要在注解形式的环绕通知中加入目标对象的参数显示 用的是ProceedingJoinPoint  不是Joinpoint
    @Around("execution(public * serviceimpl.StudentServiceImpl.addStudent(..))  execution(public * serviceimpl.StudentServiceImpl.chu(int))")
    public void myAround(ProceedingJoinPoint jp){
//        方法执行之前:前置通知
        System.out.println("jjjjjj方法执行之前:前置通知");
     try{
//          方法执行时
//            方法执行之后:后置通知
         jp.proceed();
         System.out.println("jjjjjj方法执行之后:后置通知");
     }catch(Throwable e){
//异常通知
         System.out.println("jjjjjj异常通知");
     }finally {
//最终通知
         System.out.println("jjjjjj最终通知");
     }
    }

    //最终通知
    @After("execution(public * serviceimpl.StudentServiceImpl.addStudent(..))")
    public void myFinal(){
        System.out.println("jjjjjj最终通知嗷嗷嗷嗷");
    }
}

好了再没有其他额外配置了!!!!

基于Shema的AOP实现:

通过配置将普通类,变成通知类
首先在applicationContext.xml 中需要aop命名空间:

在这里插入图片描述
类似接口的方式:
1.编写一个普通类:

package aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//schema方式式
public class LogSchema {
    //后置通知
    public void afterReturning(JoinPoint jp,Object returnValue) throws Throwable {//Joinpaint适用于注解和schema方式
        System.out.println("zzzzzzzzz后置通知:目标对象:"+jp.getThis()+",调用的方法名:"+jp.getSignature().getName()+",方法的参数个数:"+jp.getArgs().length+",方法返回值"+returnValue);
    }
    public void before() throws Throwable {
        System.out.println("zzzzzzzzzzzzz前置....");
    }
    public void whenException(JoinPoint jp,NullPointerException e){
        System.out.println("zzzzzzzzzzzzz异常:"+e.getMessage());
    }
//schema方式和注解方式类似  不同之处:注解形式使用了@After schema形式在xml进行了多余的配对



    //注意:环绕通知 会返回目标方法的返回值,因此返回值为Object
    public Object around(ProceedingJoinPoint jp){
        Object result  = null;
        System.out.println("schema的环绕前置");
        try {
            result  = jp.proceed();
            System.out.println("schema的环绕后置"+jp.getSignature().getName()+","+result);
        }catch (Throwable e) {
            System.out.println("schema的环绕异常置");
        }
        return result;
    }
}

但是这次就需要继续配置IOC了:

<!--将准备转为通知的普通类纳入IOC容器-->
<bean id="LogSchema" class="aop.LogSchema"></bean>
<aop:config>
    <aop:pointcut id="pcShema" expression="execution(public void serviceimpl.StudentServiceImpl.addStudent(entity.Student))"/>
    <!--schema方式不需要aop:advisor -->
    <aop:aspect ref="LogSchema">
        <aop:before method="before" pointcut-ref="pcShema"/>
        <aop:after-returning method="afterReturning" returning="returnValue" pointcut-ref="pcShema"/>
        <aop:after-throwing method="whenException" throwing="e" pointcut-ref="pcShema"/>
        <aop:around method="around" pointcut-ref="pcShema"/>
    </aop:aspect>
</aop:config>

OK!!!!!!!

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

键盘歌唱家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值