spring--AOP

参考:
https://www.cnblogs.com/bqh10086/p/10125391.html
https://zhuanlan.zhihu.com/p/355520208

AOP核心概念

1、横切关注点
对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

2、切面(aspect)
类是对物体特征的抽象,切面就是对横切关注点的抽象

3、连接点(joinpoint)
被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut)
对连接点进行拦截的定义

5、通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象
代理的目标对象

7、织入(weave)
将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

AOP作用:打补丁

AOP注解理解:

@Aspect:

用在类上面,定义该类是aop的切面类,在该类里面实现切面操作,还需要被spring管理,一般和@Component连用。

@Pointcut:切入点

定义一个切入点表达式,用来确定哪些类需要代理,有两种用法:

1、使用路径定义:
execution(* aopdemo..(…))代表aopdemo包下所有类的所有方法都会被代理

eg1:@Pointcut(“execution(* aopdemo..(…))”)

eg2 : @Pointcut(value =
“execution(public * cn.org.emcs.userbasecloud…Controller.*(…))”)

具体使用:

2、使用注解定义:@Pointcut("@annotation(com.example.dtest.aop.MyAopAnnotation)")
表示添加了该注解的都是要执行该切面的类:

@Pointcut("@annotation(com.example.dtest.aop.MyAopAnnotation)")
  public void MypointCut(){

    }

注解类:

package com.example.dtest.aop;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented//说明该注解将被包含在javadoc中
//@Retention: 定义注解的保留策略,
@Retention(RUNTIME)// 注解会在class字节码文件中存在,在运行时可以通过反射获取到
//@Target:定义注解的作用目标
@Target({METHOD,PARAMETER})// 方法和方法参数
@Inherited//说明子类可以继承父类中的该注解
public @interface MyAopAnnotation {


}

通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

前置:就是在目标切面方法前执行
@Before(value = “MypointCut()”) ----这里的MypointCut()方法就是前面切入点@Pointcut配置的方法,通过它去找到要执行类的所有方法:

@Before(value = "MypointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("注解测试方法执行前");

// 首先获取方法的签名,joinPoint中有相应的签名信息
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        // 通过方法的签名可以获取方法本身
        Method method = signature.getMethod();
        // 获取方法的参数列表,注意此方法需JDK8+
        Parameter[] parameters = method.getParameters();
        // 获取参数列表的实际的值
        Object[] args = joinPoint.getArgs();
        // 建立参数列表和实际的值的对应关系
//        Pair<Parameter, Object>[] parameterObjectPairs = new Pair[parameters.length];
//        for(int i = 0; i < parameters.length; i++) {
//            parameterObjectPairs[i] = new Pair<>(parameters[i], args[i]);
//        }
//        // 对参数进行遍历
//        for(int i = 0; i < parameterObjectPairs.length; i++) {
//            Pair<Parameter, Object> pair = parameterObjectPairs[i];
//
//        }


    }

后置:就是在目标切面方法后执行

@After(value = "MypointCut()")
    public void after(JoinPoint joinPoint){
        System.out.println("注解测试方法执行后");

        /*拿到请求头:*/
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();
        String token = req.getHeader("token");


    }

异常、最终:

    @AfterThrowing(value = "MypointCut()")
    public void throwMethod(JoinPoint joinPoint){
        System.out.println("注解测试方法异常尝试后执行");
    }
   @AfterReturning(value = "MypointCut()")
    public void returnMehtod(JoinPoint joinPoint){
        System.out.println("注解测试方法返回后执行");
    }

@Around环绕:可自定义目标方法执行的时机

joinPoint.proceed();//执行目标方法
//用新的参数值执行目标方法
result = pjd.proceed(new Object[]{“newSpring”,“newAop”});

 @Around(value = "MypointCut()")
    public void aroundMethod(ProceedingJoinPoint joinPoint){

        System.out.println("注解测试方法环绕方法执行前");
        try{
            joinPoint.proceed();
            System.out.println("注解测试方法环绕方法执行后");
        }catch(Throwable throwable){
            throwable.printStackTrace();
        }

    }

JoinPoint和ProceedingJoinPoint区别:

JoinPoint

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

在这里插入图片描述

ProceedingJoinPoint

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

在这里插入图片描述

Spring AOP 切入点@Pointcut – execution表达式

execution(* com.sample.service.impl..*.*(..))

详述

  • execution(),表达式的主体
  • 第一个“*”符号,表示返回值类型任意;
  • com.sample.service.impl,AOP所切的服务的包名,即我们的业务部分
  • 包名后面的“…”,表示当前包及子包
  • 第二个“*”,表示类名,*即所有类
  • .*(…),表示任何方法名,括号表示参数,两个点表示任何参数类型

案例1:用path作切面路径:

切面类:

package com.example.dtest.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 把这个类声明为一个切面:
 * 1. 使用注解“@Component”把该类放入到IOC容器中
 * 2. 使用注解“@Aspect”把该类声明为一个切面
 *
 * 设置切面的优先级:
 * 3. 使用注解“@Order(number)”指定前面的优先级,值越小,优先级越高
 */
@Order(1)
@Aspect
@Component
public class MyAspectByPath {

    /**
     * Pointcut 切入点
     * 匹配
     * cn.huanzi.qch.baseadmin.sys.*.controller、
     * cn.huanzi.qch.baseadmin.*.controller包下面的所有方法
     *
     * 参考:https://blog.csdn.net/weixin_49930191/article/details/108360340
     * execution(),表达式的主体
     * 第一个“*”符号,表示返回值类型任意;
     * com.sample.service.impl,AOP所切的服务的包名,即我们的业务部分
     * 包名后面的“…”,表示当前包及子包
     * 第二个“*”,表示类名,*即所有类
     * .*(…),表示任何方法名,括号表示参数,两个点表示任何参数类型
     * ————————————————
     * 版权声明:本文为CSDN博主「张同学最帅」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
     * 原文链接:https://blog.csdn.net/weixin_49930191/article/details/108360340
     *execution(* com.sample.service.impl..*.*(..))
     */
    @Pointcut(value = "execution(public * com.example.dtest.controller..*.*(..))")
    public void MypointCut(){

    }

    /**
     * 定义一个切入点表达式,用来确定哪些类需要代理
     * execution(* com.*.*(..))com
     */
//    @Pointcut("execution(* com.*.*(..))")
//    public void MypointCut(){
//
//    }



    @Before(value = "MypointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("path---注解测试方法执行前");

// 首先获取方法的签名,joinPoint中有相应的签名信息
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        // 通过方法的签名可以获取方法本身
        Method method = signature.getMethod();
        // 获取方法的参数列表,注意此方法需JDK8+
        Parameter[] parameters = method.getParameters();
        // 获取参数列表的实际的值
        Object[] args = joinPoint.getArgs();
        // 建立参数列表和实际的值的对应关系
//        Pair<Parameter, Object>[] parameterObjectPairs = new Pair[parameters.length];
//        for(int i = 0; i < parameters.length; i++) {
//            parameterObjectPairs[i] = new Pair<>(parameters[i], args[i]);
//        }
//        // 对参数进行遍历
//        for(int i = 0; i < parameterObjectPairs.length; i++) {
//            Pair<Parameter, Object> pair = parameterObjectPairs[i];
//
//        }


    }

    @After(value = "MypointCut()")
    public void after(JoinPoint joinPoint){
        System.out.println("path---注解测试方法执行后");

        /*拿到请求头:*/
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();
        String token = req.getHeader("token");


    }

    @AfterReturning(value = "MypointCut()")
    public void returnMehtod(JoinPoint joinPoint){
        System.out.println("path---注解测试方法返回后执行");
    }

    @AfterThrowing(value = "MypointCut()")
    public void throwMethod(JoinPoint joinPoint){
        System.out.println("path---注解测试方法异常尝试后执行");
    }

    @Around(value = "MypointCut()")
    public void aroundMethod(ProceedingJoinPoint joinPoint){

        System.out.println("path---注解测试方法环绕方法执行前");
        try{
            joinPoint.proceed();
            System.out.println("path---注解测试方法环绕方法执行后");
        }catch(Throwable throwable){
            throwable.printStackTrace();
        }

    }

}

目录结构:
在这里插入图片描述

执行测试类:

package com.example.dtest.controller;

import com.example.vo.Res;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/Aop")
public class AopTestController {

//    @MyAopAnnotation
    @PostMapping("/test")
    public Res testAop(){

        return Res.success(1, "yes");

    }


}

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

案例1:用注解作切面路径:

注解类:

package com.example.dtest.aop;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented//说明该注解将被包含在javadoc中
//@Retention: 定义注解的保留策略,
@Retention(RUNTIME)// 注解会在class字节码文件中存在,在运行时可以通过反射获取到
//@Target:定义注解的作用目标
@Target({METHOD,PARAMETER})// 方法和方法参数
@Inherited//说明子类可以继承父类中的该注解
public @interface MyAopAnnotation {


}

切面类:

package com.example.dtest.aop;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 把这个类声明为一个切面:
 * 1. 使用注解“@Component”把该类放入到IOC容器中
 * 2. 使用注解“@Aspect”把该类声明为一个切面
 *
 * 设置切面的优先级:
 * 3. 使用注解“@Order(number)”指定前面的优先级,值越小,优先级越高
 */
@Order(1)
@Aspect
@Component
public class MyAspect {

    //将切入点换成了自己定义的注解:在加入注解的地方切入
    @Pointcut("@annotation(com.example.dtest.aop.MyAopAnnotation)")
    public void MypointCut(){

    }






    @Before(value = "MypointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("annotation---注解测试方法执行前");

// 首先获取方法的签名,joinPoint中有相应的签名信息
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        // 通过方法的签名可以获取方法本身
        Method method = signature.getMethod();
        // 获取方法的参数列表,注意此方法需JDK8+
        Parameter[] parameters = method.getParameters();
        // 获取参数列表的实际的值
        Object[] args = joinPoint.getArgs();
        // 建立参数列表和实际的值的对应关系
//        Pair<Parameter, Object>[] parameterObjectPairs = new Pair[parameters.length];
//        for(int i = 0; i < parameters.length; i++) {
//            parameterObjectPairs[i] = new Pair<>(parameters[i], args[i]);
//        }
//        // 对参数进行遍历
//        for(int i = 0; i < parameterObjectPairs.length; i++) {
//            Pair<Parameter, Object> pair = parameterObjectPairs[i];
//
//        }


    }

    @After(value = "MypointCut()")
    public void after(JoinPoint joinPoint){
        System.out.println("annotation---注解测试方法执行后");

        /*拿到请求头:*/
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attributes.getRequest();
        String token = req.getHeader("token");


    }

    @AfterReturning(value = "MypointCut()")
    public void returnMehtod(JoinPoint joinPoint){
        System.out.println("annotation---注解测试方法返回后执行");
    }

    @AfterThrowing(value = "MypointCut()")
    public void throwMethod(JoinPoint joinPoint){
        System.out.println("annotation---注解测试方法异常尝试后执行");
    }

    @Around(value = "MypointCut()")
    public void aroundMethod(ProceedingJoinPoint joinPoint){

        System.out.println("annotation---注解测试方法环绕方法执行前");
        try{
            joinPoint.proceed();
            System.out.println("annotation---注解测试方法环绕方法执行后");
        }catch(Throwable throwable){
            throwable.printStackTrace();
        }

    }



}

测试实现类:

package com.example.dtest.controller;

import com.example.dtest.aop.MyAopAnnotation;
import com.example.vo.Res;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/Aop")
public class AopTestController {

    @MyAopAnnotation
    @PostMapping("/test")
    public Res testAop(){

        return Res.success(1, "yes");

    }


}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值