美文网首页
spring-aop 应用

spring-aop 应用

作者: wang_cheng | 来源:发表于2020-04-01 16:46 被阅读0次

Aop- 面向切面编程-->通俗讲 对业务方法做一些增强(比如日志输出,事务控制,异常的处理等。。)

举个例子:
比如想看下接口的业务执行时间,可能第一会想到在最开始和最结束的地方统计时间,算出时间差
如果统计所有接口,那就很没必要,其实这个和业务本身是没关系的,应该抽离出去,这时aop就派上用场了
它能在指定的地方(比如执行方法前,或者执行方法后)自动的去调用

aop一些术语(其他地方搬过来的,方便理解)
1.通知(Advice)
  就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。

2.连接点(JoinPoint)

这个更好解释了,就是spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,只要记住,和方法有关的前前后后(抛出异常),都是连接点。

3.切入点(Pointcut)

上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。

4.切面(Aspect)

切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。

5.引入(introduction)

允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗

6.目标(target)

引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。

7.代理(proxy)

怎么实现整套aop机制的,都是通过代理,这个一会给细说。

8.织入(weaving)

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

关键就是:切点定义了哪些连接点会得到通知

一些aop相关介绍
AspectJ指示器
当我们查看这些Spring支持的指示器时,注意只有execution指示器是唯一的执行匹配,而其他的指示器都是用于限制匹配的。这说明execution指示器是我们在编写切点定义时最主要使用的指示器

image.png
execution 表达式,还有其他不同的写法---->可以百度哦
1)通过方法签名定义切点
execution(public * * (..)) 匹配所有目标类的public方法,第一个 * 代表返回类型,第二个 * 代表方法名,而..代表任意入参的方法;
execution(* *To(..))l 匹配目标类所有以To为后缀的方法。第一个 * 代表返回类型,而 * To代表任意以To为后缀的方法;
2)通过类定义切点
execution( * com.qqqq.test. * (..))l第一个 * 代表返回任意类型,com.qqqq.test. * 第二个 * 代表Test接口中的所有方法;
3)通过类包定义切点

注解 作用

  • @Aspect 把当前类标识为一个切面

  • @Pointcut
    Pointcut是织入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。

  • @Around 环绕增强,目标方法执行前后分别执行一些代码

  • @AfterReturning 返回增强,目标方法正常执行完毕时执行

  • @Before 前置增强,目标方法执行之前执行

  • @AfterThrowing 异常抛出增强,目标方法发生异常的时候执行

  • @After 后置增强,不管是抛出异常或者正常退出都会执行

JoinPoint 对象
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.

常用API
方法名 功能
Signature getSignature(); 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs(); 获取传入目标方法的参数对象
Object getTarget(); 获取被代理的对象
Object getThis(); 获取代理对象

ProceedingJoinPoint对象
ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中,
添加了以下两个方法。

 Object proceed() throws Throwable;////执行目标方法 
 Object proceed(Object[] var1) throws Throwable;//传入的新的参数去执行目标方法 

直接上代码操作
定义一个切面类

@Component
@Aspect
@Slf4j
public class ControllerAop {
}

定义一个方法,声明切入点表达式,其他的增强方法可以调用这个方法

@Pointcut("execution(* com.cheng.testone.service.*.*(..))")
    public void PointcutDeclaration() {}

完整代码

@Component
@Aspect
@Slf4j
public class ControllerAop {

    /**
     *
     */
    @Pointcut("execution(* com.cheng.testone.service.*.*(..))")
    public void PointcutDeclaration() {}


    //前置通知,方法执行之前执行
    @Before("PointcutDeclaration()")
    public void testBefore(JoinPoint joinPoint){
        log.info("Before===========>测试方法执行前");
        Object[] args = joinPoint.getArgs();
        log.info("入参:"+Arrays.toString(args));
        log.info("目标方法名为:" + joinPoint.getSignature().getName());
        log.info("目标方法所属类的简单类名:" +joinPoint.getSignature().getDeclaringType().getSimpleName());
        log.info("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
        log.info("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
    }

    //后置通知,方法执行之后执行(不管是否发生异常)
    @After("PointcutDeclaration()")
    public void AfterMethod(JoinPoint jp) {
        log.info("After==============>>方法执行后");
        String methodName = jp.getSignature().getName();
        Object[] args = jp.getArgs();
        System.out.println("AfterMethod  The method    "+ methodName +"   parameter is  "+ Arrays.asList(args));
    }

    //返回通知,方法正常执行完毕之后执行
    @AfterReturning(value="PointcutDeclaration()",returning="result")
    public void AfterReturningMethod(JoinPoint jp,Object result) {
        log.info("AfterReturning=============返回通知");
        String methodName = jp.getSignature().getName();
        Object[] args = jp.getArgs();
        System.out.println("AfterReturningMethod  The method   "+ methodName +"   parameter is  "+Arrays.asList(args)+"return-> "+result);
    }

    //异常通知,在方法抛出异常之后执行
    @AfterThrowing(value="PointcutDeclaration()",throwing="e")
    public void AfterThrowingMethod(JoinPoint jp,Exception e) {
        log.info("AfterThrowing==============>>异常通知");
        String methodName = jp.getSignature().getName();
        System.out.println("AfterThrowingMethod  The method   "+ methodName +"exception :"+e);
    }

    /**
     * 环绕通知
     * ProceedingJoinPoint对象是JoinPoint的子接口,该对象用在@Around的切面方法中,常用有以下两个方法
     * @param pjp
     * @return
     */
    @Around("execution(public com.cheng.testone.model.entity.ResultBean *(..))")
    public Object handlerResultBeanControllerMethod(ProceedingJoinPoint pjp) {
        long startTime = System.currentTimeMillis();
        ResultBean<?> result = null;
        try {
            log.info("【环绕增强中的--->前置增强】");
            StringJoiner argsBuf = new StringJoiner(",");
            Object[] args = pjp.getArgs();
            Arrays.stream(args).filter(Objects::nonNull).forEach(arg -> argsBuf.add(arg.toString()));
            log.info(pjp.getSignature() + "request param :" + argsBuf.toString());
            //执行目标方法
            result = (ResultBean)pjp.proceed();
            log.info("【环绕增强中的--->返回增强】");
            log.info(pjp.getSignature() + "use time:" + (System.currentTimeMillis() - startTime) + "ms");
        } catch (Throwable e) {
            //异常通知
            result = handlerResultBeanException(pjp, e);
            log.info("【环绕增强中的--->异常增强】");
        }
        log.info("【环绕增强中的--->后置增强】");
        return result;
    }

    private ResultBean<?> handlerResultBeanException(ProceedingJoinPoint pjp, Throwable e) {
        ResultBean<?> result = new ResultBean();
        if (e instanceof ServiceException) {
            log.error(pjp.getSignature() + " error {}",e.getMessage());
            result.setMsg(((ServiceException) e).getMsg());
            result.setCode(((ServiceException)e).getCode());
        } else {
            log.error(pjp.getSignature() + " error ", e);
            result.setMsg("系统异常");
            result.setCode(300);
        }
        return result;
    }
}


相关文章

  • spring-aop 应用

    Aop- 面向切面编程-->通俗讲 对业务方法做一些增强(比如日志输出,事务控制,异常的处理等。。) 举个例子:比...

  • 【spring入门到精通】Spring体系结构

    Spring 框架基本涵盖了企业级应用开发的各个方面,它包含了 20 多个不同的模块。spring-aop ...

  • 自定义注解,aop+redis,实现controller接口频率

    1,环境配置 引入aop的jar包compile 'org.springframework:spring-aop:...

  • spring-4.3.4.RELEASE集成AOP 实战

    一、依赖引入 (包括 spring-aop 以及 aspectj) 二、切面配置代码 (使用 javaConfig...

  • spring-AOP

    Aspects,切面 spring-aop 是spring-aspects的上层建筑 targetClass Me...

  • spring-aop

    aop概念aop概念aop术语AOP实现方式1、spring-aop(使用xml文件实现AOP)2、AspectJ...

  • spring-aop

    Aop-面向切面编程,在程序中主要用来解决一些系统层面上的问题,比如:日志,事务,权限等。 aop的一些基本概念:...

  • spring-aop

    1, aop的两种实现机制动态代理:利用jdk/cglib动态代理,性能弱一丢丢 jdk动态代理:所有的方法调用被...

  • spring-aop

  • Spring-AOP

    AOP的概念 面向切面的编程,切面用于描述分散在对象、类或者函数中的横向关注点,通过分离这些关注点,把解决特定领域...

网友评论

      本文标题:spring-aop 应用

      本文链接:https://www.haomeiwen.com/subject/owrauhtx.html