• Spring AOP 代码示例


    AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等。其实说起aop大家都对其有一定的概念。今天主要是总结一下他代码中的实现方式,以方便后边查看使用。

    说起AOP,首先我们需要先了解其中的几个概念:

    1.通知(Advice)

       AOP在特定的切入点上执行的增强处理,就是你想要的功能,也就是上面说的日志、事务、权限等。有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕),就是在上面这几中类型所实现的功能。比如在使用redis操作前打印日志。

    2.连接点(JoinPoint)

      就是spring允许你使用通知的地方,例如每个方法的前,后(两者都有也行),或抛出异常时,也就是说和方法有关的前后(抛出异常),都是连接点。Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器,如aspectJ。

    3.切入点(Pointcut)

      就是带有通知的连接点,在连接点的基础上来定义切入点,假如一个类里面有5个方法,那就有5个连接点,但是并不想在所有方法上都实现切面的功能,只是想让其中的几个方法在调用之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。在程序中主要体现为书写切入点表达式。

    4.切面(Aspect)

      通常是一个类,里面可以定义切入点和通知。通知说明了干什么和什么时候干(之前,之后,还是异常),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

    5.引入(introduction)

      在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。就是把我们定义的功能用到目标类中。

    6.目标(target)

      包含连接点的对象。也被称作被通知或被代理对象。就是上面引入中所提到的目标类。

    7.代理(proxy)

       AOP框架创建的对象,代理就是目标对象的加强版本。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类。

    8.织入(weaving)

       把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时。

    下面我们用注解的方式实现下AOP的功能。
    增加AOP所需要的依赖包
            <!--AOP依赖包-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>

    定义一个切面类。

    package com.example.demo.aop;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    @Component //让spring能够扫描到
    @Aspect //定义这是一个切面类
    public class LogAspect {
    
        /**
         * modifier-pattern:用于匹配public、private等访问修饰符
         * ret-type-pattern:用于匹配返回值类型,不可省略
         * declaring-type-pattern:用于匹配包类型
         * modifier-pattern(param-pattern):用于匹配类中的方法,不可省略
         * throws-pattern:用于匹配抛出异常的方法
         *
         *
         * 多个表达式之间使用连接符匹配多个条件, 如使用||表示“或”,使用 &&表示“且”
         */
        @Pointcut("@annotation(com.example.demo.annotation.LogAop) &&" +
                "execution(public * com.example.demo.service.impl.AopTestServiceImpl.get*(..))")
        public void log(){
    
        }
    
        /**
         * 匹配com.example.demo.service.impl包下所有类下的方法名以update结尾、参数类型不限的public方法
         */
        @Pointcut("execution(public * com.example.demo.service.impl.*.*update(..))")
        public void say(){
    
        }
    
        @Order(2)
        @Before("log()")
        public void beforeLog2(){
            System.out.println("后执行,增加log()方法...");
        }
    
        @Order(1) // Order 代表优先级,数字越小优先级越高
        @Before("log()||say()") //多个的话用 @Before("log()||say()")
        public void beforeLog1(){
            System.out.println("先执行,增加log()方法...");
        }
    
        /**
         * Advice注解一共有五种,分别是:
         * 1.@Before前置通知
         * 前置通知在切入点运行前执行,不会影响切入点的逻辑
         * 2.@After后置通知
         * 后置通知在切入点正常运行结束后执行,如果切入点抛出异常,则在抛出异常前执行
         * 3.@AfterThrowing异常通知
         * 异常通知在切入点抛出异常前执行,如果切入点正常运行(未抛出异常),则不执行
         * 4.@AfterReturning返回通知
         * 返回通知在切入点正常运行结束后执行,如果切入点抛出异常,则不执行
         * 5.@Around环绕通知
         * 环绕通知是功能最强大的通知,可以在切入点执行前后自定义一些操作。环绕通知需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行
         */
        @Before("say()")
        public void beforeSay(){
            System.out.println("增加say()方法...");
        }
    }

    此类就是一个切面,其中一共定义了两个切入点,一个是log(),一个是say()。在满足切入点要求的方法前会做三中类型的通知,也就是beforeLog21(),beforeLog2(),beforeSay()。为什么是方法前呢,因为用的注解是@Before。其他具体的信息,可以看代码里面的注释。里面用到了一个我们自定义的注解,其代码如下:

    package com.example.demo.annotation;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.CLASS)
    public @interface LogAop {
    
        /**
         * 使用@annotation、@within、@target、@args匹配注解
         * //匹配标注有LogAop注解的方法
         * @Pointcut("@annotation(com.example.demo.annotation.LogAop)")
         * public void matchAnno() {}
         *
         * //匹配标注有LogAop的类底下的方法,要求annotation的Retention级别为CLASS
         * @Pointcut("@within(com.example.demo.annotation.LogAop)")
         * public void matchWithin() {}
         *
         * //匹配标注有LogAop的类底下的方法,要求annotation的Retention级别为RUNTIME
         * @Pointcut("@target(com.example.demo.annotation.LogAop)")
         * public void matchTarget() {}
         *
         * //匹配传入的参数类标注有LogAop注解的方法
         * @Pointcut("@args(com.example.demo.annotation.LogAop)")
         * public void matchArgs() {}
         */
    }

    然后就是我们的业务处理代码,也就是目标对象。

    package com.example.demo.service.impl;
    
    import com.example.demo.annotation.LogAop;
    import com.example.demo.model.Student;
    import com.example.demo.service.GetStudentService;
    import org.springframework.stereotype.Service;
    
    /**
    * @Description: AOP测试
    * @Author:      haoqiangwang3
    * @CreateDate:  2020/1/13
    */
    @Service
    public class AopTestServiceImpl implements GetStudentService {
    
        @LogAop
        @Override
        public Student getStudentInfo() {
            System.out.println("调用业务处理中的get()方法...");
            return null;
        }
    
        @Override
        public int update() {
            System.out.println("调用业务处理中的update()方法...");
            return 0;
        }
    }

    再就是程序的入口,controlle方法

    package com.example.demo.controller;
    
    import com.example.demo.service.impl.AopTestServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
    * @Description: AOP测试controller
    * @Author:      haoqiangwang3
    * @CreateDate:  2020/1/13
    */
    @RestController
    public class AopController {
        @Autowired
        private AopTestServiceImpl aopTestServiceImpl;
    
        @RequestMapping("/boot/getAop")
        public String aopGet(){
            aopTestServiceImpl.getStudentInfo();
            return "success";
        }
    
        @RequestMapping("/boot/updateAop")
        public String aopUpdate(){
            aopTestServiceImpl.update();
            return "success";
        }
    }

    上面的代码是在我之前学习spring boot程序的基础上增加的。以上完成后,就可以测试我们的aop功能了,测试结果就不粘贴了,亲测是可以生效的。

  • 相关阅读:
    git常用命令
    Expected Conditions的常用函数
    Docker(二十)-Docker容器CPU、memory资源限制
    Docker(十九)-Docker监控容器资源的占用情况
    ubuntu制作本地源
    ubuntu下安装软件的三种方法
    把windows的bat用好了,也很不错
    escape()、encodeURI()、encodeURIComponent()区别详解
    如何解决Ajax跨域问题-1
    五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT) – 整理
  • 原文地址:https://www.cnblogs.com/wanghq1994/p/12187186.html
Copyright © 2020-2023  润新知