栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java-SpringBoot AOP-半小时了解AOP知识及使用注解实现AOP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java-SpringBoot AOP-半小时了解AOP知识及使用注解实现AOP

Java-SpringBoot AOP-半小时了解AOP知识及使用注解实现AOP 环境

springboot 2.7.0
jdk 8

AOP是什么?

AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。
面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。
AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

Spring AOP 与 Aspectj

Spring aop 和 AspectJ, 这是 Java 的两个最受欢迎的 aop 框架,springboot 使用AspectJ中的 @Aspect、@Pointcut、@Before、@After、@Around等常用的注解,可以方便实现切入和增强通知等功能。

常用注解
  • @Aspect标识一个切面
  • 切入点:@Pointcut
  • 通知:@Before、@After、@Around、@AfterReturning、@AfterThrowing

注:AOP = 切入点+通知

实现AOP的两种风格 使用切入点注解实现AOP

使用@Pointcut注解可以在一个切面类中,将多个通知的切入点表达式抽取出来。不需要在每个通知注解中重复定义切入点表达式,代码风格直观。

不使用切入点注解AOP

当然在切面类中可以不用定义一个切入点,直接在通知类注解中使用表达式完成切入+通知操作,在切面类中定义一个通知时,此方法还是比较简单的。

切入点表达式

切入点表单时需要结合AOP注解使用,例如 value 中的 execution:

  • @Before(value = “execution(* com.example…getTime(…))”)
    以下为注解支持的表达式类型:
  • execution: 用于匹配方法执行连接点。 这是使用Spring AOP时使用的主要切点标识符。 可以匹配到方法级别 ,细粒度
  • within: 只能匹配类这级,只能指定类, 类下面的某个具体的方法无法指定, 粗粒度
  • this: 匹配实现了某个接口:this(com.xyz.service.AccountService)
  • target: 限制匹配到连接点(使用Spring AOP时方法的执行),其中目标对象(正在代理的应用程序对象)是给定类型的实例。
  • args: 限制与连接点的匹配(使用Spring AOP时方法的执行),其中变量是给定类型的实例。 AOP) where the arguments are instances of the given types.
  • @target: 限制与连接点的匹配(使用Spring AOP时方法的执行),其中执行对象的类具有给定类型的注解。
  • @args: 限制匹配连接点(使用Spring AOP时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注解。
  • @within: 限制与具有给定注解的类型中的连接点匹配(使用Spring AOP时在具有给定注解的类型中声明的方法的执行)。
  • @annotation:限制匹配连接点(在Spring AOP中执行的方法具有给定的注解)。
springboot中注解方式定义AOP 使用@Pointcut注解
package com.example.learnspringbootaop.aop;

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


@Component
@Aspect // 切面 = 切入点 + 通知
public class MyAspect {
    @Pointcut("@annotation(com.example.learnspringbootaop.annotation.TestMyAspect)")
    public void pointcut(){
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("pointcutt开始执行t类:"+this.getClass().getSimpleName()+" 方法:pointcut()");
    }

    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("beforet开始执行t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
    }

    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("aftert查询结束t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
        // 获取执行目标类和方法名等等
    }

    
    @Around("pointcut()")
    public Object aroud(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("aroudt环绕通知-方法执行前t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
        // 必须方法目标方法
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
            System.out.print(this.getClass().getSimpleName()+"t");
        }catch (Throwable ex){
            System.out.print(this.getClass().getSimpleName()+"t");
            System.out.println("aroudt执行时产生异常,error="+ex.toString());
            throw ex;
        }finally {
            System.out.println("aroudt环绕通知-方法执行后t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
        }

        // 将目标方法的返回值进行返回,否则调用目标方法的方法无法获取到返回值
        return proceed;
    }

    @AfterReturning("pointcut()")
    public void doAfterSuccess(JoinPoint joinPoint){
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("AfterSuccesst执行返回通知t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
    }

    @AfterThrowing("pointcut()")
    public void doAfterError(JoinPoint joinPoint){
        System.out.println("AfterErrort执行异常通知t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
    }
}

不使用@Pointcut注解定义
package com.example.learnspringbootaop.aop;

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

// 表示当前的类是一个配置类
@Configuration
//该注解只能用在类上,作用:代表当前类是一个切面类,等价于spring.xml中的标签
//所以现在有了切面,还需要  通知 + 切入点
// 切面 == 通知 + 切面
@Aspect
public class MyAdviceConfig {

    
    // 表示com.example包下的所有类所有方法都执行该前置通知
    @Before(value = "execution(* com.example..getTime(..))")
    public void before(JoinPoint joinPoint) {
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("beforet开始执行t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
    }
    
    @After(value = "execution(* com.example..getTime(..))")
    public void after(JoinPoint joinPoint) {
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("aftert查询结束t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
        // 获取执行目标类和方法名等等
    }
    
    @Around(value = "execution(* com.example..getTime(..))")
    public Object aroud(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("aroudt环绕通知-方法执行前t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
        // 必须方法目标方法
        Object proceed = joinPoint.proceed();
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("aroudt环绕通知-方法执行后t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
        // 将目标方法的返回值进行返回,否则调用目标方法的方法无法获取到返回值
        return proceed;
    }


    @AfterReturning("execution(* com.example..getTime(..))")
    public void doAfterSuccess(JoinPoint joinPoint){
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("AfterSuccesst执行返回通知t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
    }

    @AfterThrowing("execution(* com.example..getTime(..))")
    public void doAfterError(JoinPoint joinPoint){
        System.out.print(this.getClass().getSimpleName()+"t");
        System.out.println("AfterErrort执行异常通知t类:"+joinPoint.getTarget()+" 方法:"+ joinPoint.getSignature().getName());
    }
}

定义一个service类
package com.example.learnspringbootaop.test1;

import com.example.learnspringbootaop.annotation.MyAnnotation;
import com.example.learnspringbootaop.annotation.TestMyAspect;
import org.springframework.stereotype.Service;
import utils.DatetimeUtil;

import java.util.Date;

@Service
public class TestService {
    public String getTime(){
        System.out.println("执行 getTime() 方法代码。。。");
        return DatetimeUtil.getNowTime();
    }

    @TestMyAspect
    public String getTime2(){
        System.out.println("执行 getTime2() 方法代码。。。");
        return DatetimeUtil.getNowTime();
    }

    @MyAnnotation
    public String getTime3(){
        System.out.println("执行 getTime3() 方法代码。。。");
        return DatetimeUtil.getNowTime();
    }

    @TestMyAspect
    public Integer getNumber(){
        System.out.println("执行 getNumber() 方法代码。。。");
        return Integer.parseInt("ABC");
    }
}

测试下注解的效果
package com.example.learnspringbootaop.test1;

import com.example.learnspringbootaop.basic.BasicTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import static org.junit.jupiter.api.Assertions.*;


class TestServiceTest  extends BasicTest {
    @Autowired
    private TestService testService;

    @Test
    void getTime() {
        testService.getTime();
    }

    @Test
    void getTime2() {
        testService.getTime2();
    }
    @Test
    void getTime3() {
        testService.getTime3();
    }
    @Test
    void getNumber() {
        testService.getNumber();
    }
}
package com.example.learnspringbootaop.basic;

import com.example.learnspringbootaop.LearnAopApplication;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = LearnAopApplication.class)
public class BasicTest {


    @Before
    public void before(){
        System.out.println("----------测试开始-------------");
    }

    @After
    public void after(){
        System.out.println("----------测试结束-------------");
    }

}

总结

相比原生Spring AOP需要定义拦截器、切入点等操作,而在Springboot中借助Aspectj的注解可以在一个类中实现aop功能。
虽然Aspectj使用任意一个“通知注解”的切入点表达式,可以将"通知+切入点"合二为一,但由于破坏了单一职责原则,所以个人还是习惯在一个切面中定义一个业务的代码。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1013504.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号