注解AOP -完整版

65 阅读3分钟

动态代理的分类

JDK动态代理和cglib动态代理

有接口: 使用JDK动态代理,生成接口实现类代理对象

代理对象和目标对象实现用样的接口

没有接口: 使用cglib动态代理,生成子类代理对象

继承被代理的目标类

具体操作:

第一步:引入aop依赖

<dependencies>
    <!--spring aop依赖-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.2</version>
    </dependency>

    <!--spring aspects依赖-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.2</version>
    </dependency>

    <!--spring context依赖-->
    <!--当你引入了此依赖之后,表示将spring的基础依赖引入了-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.2</version>
    </dependency>

    <!--junit-->
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.6.3</version>
    </dependency>

    <!-- log4j2的依赖-->
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.13.3</version>
    </dependency>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.13.3</version>
    </dependency>
    </dependencies>

第二步:创建目标资源:1.接口 2.实现类

接口:

实现类:

第三步:创建切面类 :1.切入点 2.通知类型

接下来,创建一个切面类:

然后配置xml文件

1.引入context aop的相关约束

2.开启组件扫描,以及为了让spring认识我们的注解,我们还要开启aspectj代理

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"

       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">

<!--    开启组件扫描-->
    <context:component-scan base-package="com.atguigu.spring.aop.anno"></context:component-scan>

<!--    为了让spring认识我们的注解,所以我们要开启aspectj自助代理,为目标对象生成代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

回到切面类

接下来设置切入点和通知类型

通知类型包括:前置 返回 异常 后置 环绕

每种通知类型我们都需要建立一个方法,然后在方法上添加注解,用注解表示通知类型

//前置 @Before(value="切入点表达式") 通过切入点表达式来配置切入点
//返回 @AfterReturning()
//异常 @AfterThrowing()
//后置 @After()
//环绕 @Around()

切入点表达式

如图所示:

value="execution(访问修饰符 增强方法返回类型 增强方法所在类的全路径.方法名称(方法参数))"

创建测试类

运行一下...

报错了

原来是实现类没有加上注解@Component

给他加上!

再运行...

成功了

不同通知的属性和参数设置

如图

运行结果:

可以在每个方法里加入JoinPoint

通过加入JoinPoint来获取增强方法的方法名,输入的参数,返回值等信息,也可以加入ProceedingJoinPoint

ProceedingJoinPoint 继承了 JoinPoint ,比 JoinPoint 方法更多,

返回和异常通知 可以在切面表达式中 配置额外的属性 来获取返回值和异常信息

returning = "res"

System.out.println("Logger-->返回通知,方法名称为:"+methodName+",返回值"+res);

 //返回 @AfterReturning()
@AfterReturning(value = "execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))",returning = "res")
public void AfterReturningMethod(JoinPoint joinPoint,Object res){
     String methodName=joinPoint.getSignature().getName();
     System.out.println("Logger-->返回通知,方法名称为:"+methodName+",返回值"+res);
}

throwing = "ex"

System.out.println("Logger-->异常通知,方法名称:"+methodName+",异常信息:"+ex);

//异常 @AfterThrowing()   *目标方法出现异常,这个通知执行,获取目标方法的信息
    @AfterThrowing(value ="execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))",throwing = "ex")
    public void AfterThrowingMethod(JoinPoint joinPoint,Throwable ex){
        String methodName=joinPoint.getSignature().getName();
        System.out.println("Logger-->异常通知,方法名称:"+methodName+",异常信息:"+ex);
    }

完整代码

package com.atguigu.spring.aop.anno;

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;

//切面类
@Aspect  //切面类
@Component  //ioc容器
public class LogAspect {
    //设置切入点和通知类型
    //切入点表达式:execution(访问修饰符 增强方法返回类型 增强方法所在类的全路径.方法名称(方法参数))
    //通知类型:
    //前置 @Before()
    @Before(value = "execution(public int com.atguigu.spring.aop.anno.CalculatorImpl.*(..))") //配置切入点
    public void beforeMethod(JoinPoint joinPoint){
        String methodName=joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("Logger-->前置通知,方法名称:"+methodName+",方法参数:"+ Arrays.toString(args));
    }

    //返回 @AfterReturning()
    @AfterReturning(value = "execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))",returning = "res")
    public void AfterReturningMethod(JoinPoint joinPoint,Object res){
        String methodName=joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名称为:"+methodName+",返回值"+res);
    }

    //异常 @AfterThrowing()   *目标方法出现异常,这个通知执行,获取目标方法的信息
    @AfterThrowing(value ="execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))",throwing = "ex")
    public void AfterThrowingMethod(JoinPoint joinPoint,Throwable ex){
        String methodName=joinPoint.getSignature().getName();
        System.out.println("Logger-->异常通知,方法名称:"+methodName+",异常信息:"+ex);
    }

    //后置 @After()
    @After(value = "execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName=joinPoint.getSignature().getName();
        System.out.println("Logger-->后置通知,方法名称为:"+methodName);
    }
    //环绕 @Around()
    @Around(value = ("execution(* com.atguigu.spring.aop.anno.CalculatorImpl.*(..)))"))
    public void AroundMethod(ProceedingJoinPoint joinPoint){
        String methodName=joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        String string = Arrays.toString(args);
        System.out.println("Logger-->环绕通知,方法名称为:"+methodName);
        Object res=null;
        try{
            System.out.println("环绕方法==目标方法之前完成");

            //调用目标方法
             res = joinPoint.proceed();
            System.out.println("环绕方法==目标方法返回值之后执行");
        }catch (Throwable throwable){
            throwable.printStackTrace();
            System.out.println("环绕方法==目标方法出现异常执行");
        }finally {
            System.out.println("环绕方法==目标方法执行完毕执行");
        }
    }
}