• 【Spring(六)】使用篇:AOP在开发中的使用


    有关Spring的所有文章都收录于我的专栏:👉Spring👈
    目录
    一、前言
    二、演示
    三、切面类中声明通知方法
    四、使用


    相关文章

    【Spring(一)】如何获取对象(Bean)【Spring(一)】如何获取对象(Bean)
    【Spring(二)】java对象属性的配置(Bean的配置)【Spring(二)】java对象属性的配置(Bean的配置)
    【Spring(三)】熟练掌握Spring的使用【Spring(三)】熟练掌握Spring的使用
    【Spring(四)】Spring基于注解的配置方式【Spring(四)】Spring基于注解的配置方式
    【Spring(五)】引入篇:AOP的底层原理之动态代理【Spring(五)】引入篇:AOP的底层原理之动态代理

    一、前言

     通过我们上一节对AOP的引入和底层原理的讲解,我相信大家对AOP有了很深的理解。即使我们还没讲AOP的使用。大家肯定不希望做一个只会API的程序员。只要对原理有了解,一个技术也就能很快的上手。

    二、演示

     我们在正式开始之前,先演示一下基本使用。

    <context:component-scan base-package="com.jl.spring.aop.exer"/>
    <aop:aspectj-autoproxy/>
    
    • 1
    • 2
    public interface Cal {
        public double cal1(int n);
        public double cal2(int n);
    }
    
    • 1
    • 2
    • 3
    • 4
    package com.jl.spring.aop.exer;
    
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    @Component
    public class MyCal implements Cal{
        @Override
        public double cal1(int n) {
            int result=0;
            for (int i = 1; i <= n; i++) {
                result += i;
            }
            System.out.println("1+2+...=" + result);
            return result;
        }
    
        @Override
        public double cal2(int n) {
            int result = 0;
            if (n == 0){
                System.out.println("1+2+...=" + result);
                return result;
            }
            if (n == 1){
                System.out.println("1+2+...=" + 1);
                return 1;
            }
            result = 1;
            for (int i = 1; i <= n; i++) {
                result *= i;
            }
            System.out.println("1+2+...=" + result);
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    package com.jl.spring.aop.exer;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    @Aspect
    @Component
    public class MyCalAspect {
        @Before(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public void showBegin(){
            System.out.println("开始执行计算");
        }
        @AfterReturning(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public void showEndn(){
            System.out.println("结束执行计算");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    测试类

    @Test
    public void MyCalAnnotation(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("mycal.xml");
        Cal bean = ioc.getBean(Cal.class);
        bean.cal1(2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行结果:
    在这里插入图片描述
    我们主要介绍基于注解的AOP的使用

    三、切面类中声明通知方法

    1. 前置通知:@before
    2. 返回通知:@AfterReturning
    3. 异常通知:@AfterThrowing
    4. 后置通知:@After
    5. 环绕通知:@Around

    这五个注解,我们在【使用】中会全部进行讲解。

    四、使用

     和前面几节中使用注解一样,我们想要让Spring容器将我们的类扫描实例化到容器中,我们就得在xml配置文件中告诉Spring容器要扫描的包,同时还要开始基于注解的AOP功能。

    
    <context:component-scan base-package="com.jl.spring.aop.exer"/>
    
    <aop:aspectj-autoproxy/>
    
    • 1
    • 2
    • 3
    • 4

    AOP切入表达式

    exexution([权限修饰符][返回值类型][简单类名/全类名][方法名][参数类名])
    
    • 1

    举例:

    // public修饰符 double返回值类型 com.ji.spring.aop.exer.MyCal全类名 *所有的方法 (..)匹配任意数量、任意类型的参数 	
    execution(public double com.jl.spring.aop.exer.MyCal.*(..))
    
    • 1
    • 2
    1. 切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效。
    2. 切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效。
    3. 切入表达式也可以对没有实现接口的类,进行切入。

    JoinPoint

    JoinPoint在调用前置通知获取到调用方法的签名, 和其它相关信息。

    package com.jl.spring.aop.exer;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    @Aspect
    @Component
    public class MyCalAspect {
        @Before(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public void showBegin(JoinPoint joinPoint){
            Signature signature = joinPoint.getSignature();
            System.out.println(signature.getName() +"开始执行计算");
        }
     	// 其他通知也一样
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行结果:
    在这里插入图片描述

    joinPoint常用的方法

    public void beforeMethod(JoinPoint joinPoint){
        joinPoint.getSignature().getName(); // 获取目标方法名
        joinPoint.getSignature().getDeclaringType().getSimpleName(); // 获取目标方法所属
        类的简单类名
        joinPoint.getSignature().getDeclaringTypeName(); // 获取目标方法所属类的类名
        joinPoint.getSignature().getModifiers(); // 获取目标方法声明类型(public、private、protected)
        Object[] args = joinPoint.getArgs(); // 获取传入目标方法的参数,返回一个数组
        joinPoint.getTarget(); // 获取被代理的对象
        joinPoint.getThis(); // 获取代理对象自己
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Aop返回通知获取结果

    package com.jl.spring.aop.exer;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    @Aspect
    @Component
    public class MyCalAspect {
        @AfterReturning(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))",returning = "res")
        public void showEndn(Object res){
            System.out.println("结束执行计算"+"结果是=" + res);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 在@AfterReturning直接的参数中使用returning来接收返回的结果。通过方法的参数来传递,对此结果可以进行相应的后续处理。
    2. returning底层是通过调用MyCal类的cal1()来获取到结果的。

    结果截图:
    在这里插入图片描述

    AOP异常通知中获取异常

    我们这里制造一个异常,看一下我们的异常通知。

    package com.jl.spring.aop.exer;
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    @Component
    public class MyCal implements Cal{
        @Override
        public double cal1(int n) {
            int result=0;
            for (int i = 1; i <= n; i++) {
                result = result / 0;
            }
            System.out.println("1+2+...=" + result);
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    package com.jl.spring.aop.exer;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    @Aspect
    @Component
    public class MyCalAspect {
    	    @AfterThrowing(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))",throwing = "throwable")
        public void showExceptionLog(Throwable throwable) {
            System.out.println("异常通知"+throwable);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果截图
    在这里插入图片描述

    AOP环绕通知(了解)

    环绕通知可以完成其他四个通知可以做的事情。

    package com.jl.spring.aop.exer;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.List;
    @Aspect //表示是一个切面类[底层切面编程的支撑(动态代理+反射+动态绑定...)]
    @Component //会注入SmartAnimalAspect2到容器
    public class MyCalAspect2 {
    
        //演示环绕通知的使用-了解
        //1. @Around: 表示这是一个环绕通知[完成其它四个通知的功能]
        //2. value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))" 切入点表达式
        //3. doAround 表示要切入的方法 - 调用结构 try-catch-finally
        @Around(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public Object doAround(ProceedingJoinPoint joinPoint) {
            Object result = null;
            String methodName = joinPoint.getSignature().getName();
            try {
                //1.相当于前置通知完成的事情
                Object[] args = joinPoint.getArgs();
                List<Object> argList = Arrays.asList(args);
                System.out.println("AOP环绕通知[-前置通知]" + methodName + "方法开始了--参数有:" + argList);
                //在环绕通知中一定要调用joinPoint.proceed()来执行目标方法
                result = joinPoint.proceed();
                //2.相当于返回通知完成的事情
                System.out.println("AOP环绕通知[-返回通知]" + methodName + "方法结束了--结果是:" + result);
            } catch (Throwable throwable) {
                //3.相当于异常通知完成的事情
                System.out.println("AOP环绕通知[-异常通知]" + methodName + "方法抛异常了--异常对象:" + throwable);
            } finally {
                //4.相当于最终通知完成的事情
                System.out.println("AOP环绕通知[-后置通知]" + methodName + "方法最终结束了...");
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    结果截图:
    在这里插入图片描述

    AOP切入点表达式重用

    package com.jl.spring.aop.exer;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    @Aspect
    @Component
    public class MyCalAspect {
        @Pointcut(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public void myCut(){
            System.out.println("123");
        }
    
        @Before(value = "myCut()")
        public void showBegin(){
            System.out.println("开始执行计算");
        }
    
        @AfterReturning(value = "myCut()")
        public void showEnd(){
            System.out.println("结束执行计算");
        }
    
    
        @AfterThrowing(value = "myCut()")
        public void showExceptionLog() {
            System.out.println("异常通知");
        }
    
        @After(value =  "myCut()")
        public void showFinallyEndLog() {
            System.out.println("最终通知");
        }
       
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    结果截图:
    在这里插入图片描述

    通过切入点表达式的重用可以统一管理切入点。

    切面的优先级问题

    我们可以通过Order(value=值)来控制切面的优先级,值越小,优先级越高。

    package com.jl.spring.aop.exer;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    @Aspect
    @Order(value = 2)
    @Component
    public class MyCalAspect {
    
        @Before(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public void showBegin(){
            System.out.println("开始执行计算");
        }
        @AfterReturning(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public void showEndn(){
            System.out.println("结束执行计算");
        }
    
        @AfterThrowing(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public void showExceptionLog() {
            System.out.println("异常通知");
        }
    
        @After(value = "execution(public double com.jl.spring.aop.exer.MyCal.*(..))")
        public void showFinallyEndLog() {
            System.out.println("最终通知");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    当我们有多个切面类的时候,他的各个切面通知的顺序相当于javaWeb部分过滤器Filter过滤链的那个顺序。

    基于XML配置AOP

    注意:在配置之前,需要将之间用注解配的切面类注销

        
    <bean class="com.jl.spring.aop.exer.MyCalAspect" id="myCalAspect"/>
    <bean class="com.jl.spring.aop.exer.MyCal" id="myCal"/>
    <aop:config>
        <aop:pointcut id="myCut" expression="execution(public double com.jl.spring.aop.exer.MyCal.*(..))"/>
        <aop:aspect ref="myCalAspect">
            <aop:before method="showBegin" pointcut-ref="myCut"/>
            <aop:after-returning method="showEndn" pointcut-ref="myCut"/>
        aop:aspect>
    aop:config>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    package com.jl.spring.aop.exer;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    public class MyCalAspect {
    
        public void showBegin(){
            System.out.println("开始执行计算");
        }
        public void showEndn(){
            System.out.println("结束执行计算");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    测试类

    package com.jl.spring.aop.exer;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @author long
     * @date 2022/9/11
     */
    public class MyCalTest {
        @Test
        public void MyCalAnnotation(){
            ApplicationContext ioc = new ClassPathXmlApplicationContext("mycal.xml");
            Cal bean = ioc.getBean(Cal.class);
            bean.cal1(2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果文章中有描述不准确或者错误的地方,还望指正。您可以留言📫或者私信我。🙏
    最后希望大家多多 关注+点赞+收藏^_^,你们的鼓励是我不断前进的动力!!!
    感谢感谢~~~🙏🙏🙏

  • 相关阅读:
    对 three.js webgl_clipping_stencil 例子的理解
    ubuntu下python安装wx包出错解决办法
    JavaIO流04:对象流、随机存取文件流(RandomAccessFile类)
    java任务跟踪系统
    高性能计算(HPC)存储高校科研应用分析
    pyinstaller 自动更新版本
    2022Linux学习笔记(基础)
    常见排序算法
    Metasploit(MSF)使用
    PXE操作过程 kickstart 无人值守安装
  • 原文地址:https://blog.csdn.net/qq_35947021/article/details/128025334