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

SpringBoot AOP的使用

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

SpringBoot AOP的使用

文章目录
  • 1. 简介
  • 2. 术语
  • 3. 常用的AOP通知
  • 4. 引入(Introduction)
  • 5. 五种通知(增强)代码实现
    • 5.1 匹配方法执行连接点
    • 5.2 对注解

1. 简介

  面向切面编程,相对于OOP面向对象编程 Spring的AOP的存在目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能继承和实现接口,且类继承只能单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。

  还有就是为了清晰的逻辑,让业务逻辑关注业务本身,不用去关心其它的事情,比如事务。

  Spring的AOP是通过JDK的动态代理和CGLIB实现的。

2. 术语
  • 通知(有的地方叫增强)(Advice):需要完成的工作叫做通知,就是你写的业务逻辑中需要比如事务、日志等先定义好,然后需要的地方再去用。
  • 连接点(Join point):就是spring中允许使用通知的地方,基本上每个方法前后抛异常时都可以是连接点。
  • 切点(Poincut):其实就是通知和切点的结合,通知和切点共同定义了切面的全部内容,它是干什么的,什么时候在哪执行
  • 引入(Introduction):在不改变一个现有类代码的情况下,为该类添加属性和方法,可以在无需修改现有类的前提下,让它们具有新的行为和状态。其实就是把切面(也就是新方法属性:通知定义的)用到目标类中去
  • 目标(target):被通知的对象。也就是需要加入额外代码的对象,也就是真正的业务逻辑被组织织入切面。
  • 织入(Weaving):把切面加入程序代码的过程。切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入:
    • 编译期:切面在目标类编译时被织入,这种方式需要特殊的编译器
    • 类加载期:切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码
    • 运行期:切面在应用运行的某个时刻被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP就是以这种方式织入切面的。

例子:

public class TestAopService {
    
    void save(){}
    void list(){}
}

TestAopService 中的save()方法前需要开启事务,在方法后关闭事务,在抛异常时回滚事务。

UserService中的所有方法都是连接点(JoinPoint),save()方法就是切点(Poincut)。需要在save()方法前后执行的方法就是通知(Advice),切点和通知合起来就是一个切面(Aspect)。save()方法就是目标(target)。把想要执行的代码动态的加入到save()方法前后就是织入(Weaving)。

3. 常用的AOP通知
  • before(前置通知): 在方法开始执行前执行
  • after(后置通知): 在方法执行后执行
  • afterReturning(返回后通知): 在方法返回后执行
  • afterThrowing(异常通知): 在抛出异常时执行
  • around(环绕通知): 在方法执行前和执行后都会执行

执行顺序

around > before > around > after > afterReturning

4. 引入(Introduction)

配置类

package com.cjz.study.aspect;

import com.cjz.study.service.DoSthService;
import com.cjz.study.service.impl.DoSthServiceImpl;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class IntroductionAspect {

    @DeclareParents(value = "com.cjz..service..*", defaultImpl = DoSthServiceImpl.class)
    public DoSthService doSthService;
}

实现类相关

public interface UserService {

    void testIntroduction();
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void testIntroduction() {
        System.out.println("do testIntroduction");
    }
}

public interface DoSthService {
    void doSth();
}

@Service
public class DoSthServiceImpl implements DoSthService {
    @Override
    public void doSth() {
        System.out.println("do sth ....");
    }
}

test

@Test
    public void testIntroduction(){
        userService.testIntroduction();
        DoSthService doSthService = (DoSthService) userService;
        doSthService.doSth();
    }

输出

do testIntroduction
do sth ....
5. 五种通知(增强)代码实现 5.1 匹配方法执行连接点

配置类

package com.cjz.study.aspect;

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


@Aspect
@Component
public class TestExecutionAspect {

    @Pointcut("execution(* com.cjz.study.service.impl.ExecutionAspectServiceImpl.*(..))")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void beginTransaction() {
        System.out.println("before beginTransaction");
    }

    @After("pointcut()")
    public void commit() {
        System.out.println("after commit");
    }

    @AfterReturning(value = "pointcut()", returning = "returnObject")
    public void afterReturning(JoinPoint joinPoint, Object returnObject) {
        System.out.println("afterReturning");
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        System.out.println("afterThrowing afterThrowing  rollback");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("around 方法执行前");
            //方法执行
            Object object = joinPoint.proceed();
            System.out.println("around 方法执行后");

            return object;
        } catch (Throwable e) {
            e.printStackTrace();
            throw e;
        } finally {
            System.out.println("around");
        }
    }
}

业务相关代代码

public interface ExecutionAspectService {

    Integer testExecutionAspectService(int number);
}


@Service
public class ExecutionAspectServiceImpl implements ExecutionAspectService {
    @Override
    public Integer testExecutionAspectService(int number) {
        if(number ==0){
            throw new RuntimeException();
        }
        return number;
    }
}

测试

 @Test
    public void testExecutionAspectService(){
        executionAspectService.testExecutionAspectService(2);
    }

输出

around 方法执行前
before beginTransaction
afterReturning
after commit
around 方法执行后
around

常用的execution解释

例: execution(* com.cjz..service.*.*(..))
  • execution 表达式的主体
  • 第一个* 代表任意的返回值
  • com.cjzaop所横切的包名
  • 包后面… 表示当前包及其子包
  • 第二个* 表示类名,代表所有类
  • .*(…) 表示任何方法,括号代表参数 … 表示任意参数
例: execution(* com.cjz..service.*Service.add*(String))

com.cjz 包及其子包下的service包下,类名以Service结尾,方法以add开头,参数类型为String的方法的切点。

5.2 对注解

注解

@Documented  //有关java doc的注解
@Target(ElementType.METHOD)//针对方法
@Retention(RetentionPolicy.RUNTIME)//保留时间,这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
public @interface Log {

    String value() default "";
}

配置类

@Aspect
@Component
public class TestAnnotationAspect {

    @Pointcut(value = "@annotation(com.cjz.study.annotation.Log)")
    public void pointcut() {
    }

    @Around(value = "pointcut()", argNames = "joinPoint")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            Method me = ((MethodSignature) joinPoint.getSignature()).getMethod();

            System.out.println("方法执行前");

            Log annotation = me.getAnnotation(Log.class);
            System.out.println("Log注解的value值" + annotation.value());

            //方法执行
            Object proceed = joinPoint.proceed();

            System.out.println("方法执行后");
            return proceed;
        } catch (Throwable throwable) {
            throw throwable;
        } finally {
            System.out.println("around");
        }
    }
}

相关业务类

public interface AnnotationService {

    void testAnnotationAspect();
}
@Service
public class AnnotationServiceImpl implements AnnotationService {


    @Override
    @Log("log")
    public void testAnnotationAspect() {
        System.out.println("testAnnotationAspect");
    }
}

测试

 @Test
    public void testAnnotationService(){
        annotationService.testAnnotationAspect();
    }

输出

方法执行前
Log注解的value值log
testAnnotationAspect
方法执行后
around

更多详细配置请参考 官方文档
AOP 实现Redis缓存数据查询 链接

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

版权所有 (c)2021-2022 wk8.com.cn

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