AOP功能的基本使用,自定义注解的使用,aspect注解切面编程的使用等

注:AOP本质为动态代理,是对被代理对象、方法的增强,所以在无法进行动态代理情况下,AOP会失效。、
本文旨在记录注解、aop的一些写法、方法,以防使用时忘记,不对原理做深入探讨。

自定义注解

采用@interface构建注解,实例如下所示,其中注解@Target表明该自定义注解使用的位置,ElementType.METHOD表示使用在方法上,当然,我们也可以修改此参数让其使用类上、参数上等,@Retention(RetentionPolicy.RUNTIME)表明注解作用在什么时候,RetentionPolicy.RUNTIME表明运行时作用,一般此处均是此写法。
注解内定义成员,采用名字+()形式,注意此处并不是方法,而是代表变量,注意需要指定默认值,在注解中无法使用复杂类作为变量,可使用基本数据类型以及对应的数组。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperateLog {
    String value() default "select";
}

@Aspect实现aop

@Aspect实现aop有两种形式,一种为使用注解,一种为使用ex....表达式,本处介绍使用注解,其主要区别在于注解的方式由注解来标定使用aop的地方,表达式方式可直接标定使用aop的地方
本处借助于上文的注解来实现aop
首先需要导入aop包

	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

即可使用aop,代码及其解释如下

//AOP注解,需要在aop类上标注此注解
@Aspect
@Component//将此类交给spring管理
public class OperateLogAspect {

//    该注解标定切点,即要切入的地方,参数为注解路径,表明在标注有该注解地方使用aop,operateLog()表示被切入点,自定义名字,方法内无需任何语句
    @Pointcut("@annotation(com.yanzi.annotation.OperateLog)")
    public void operateLog(){
        System.out.println("切入点");
    }


//    @Before注解表示被切入前需要做什么,方法名自定义,参数可自选,JoinPoint joinPoint参数为连接点对象,从该对象我们可以获取被注解标记地方的一些信息
//    如被标记方法名,参数值,类名,包名等,详情见下面方法内示例和注释
    @Before("operateLog()")
    public void before(JoinPoint joinPoint){
//        获取签名,进而获得方法名,还可以获得包名等信息
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();
//        获取request和response对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        String requestURI = request.getRequestURI();
//        获取参数
        Object[] args = joinPoint.getArgs();
//        获取参数名
        String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        System.out.println("方法前,方法名为:"+name+" 请求url:"+requestURI+" 请求参数a:"+args[0]);
    }


//    环绕,即在被注解标定方法前和后需要做什么,需要手动执行 proceedingJoinPoint.proceed()来执行被标注方法,
//ProceedingJoinPoint参数为JoinPoint子类,即JoinPoint能使用的该参数亦可,
    @Around("operateLog()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前");
        Object proceed =  proceedingJoinPoint.proceed();
        System.out.println("环绕后");
        return proceed;
    }


//在被标注方法后执行
    @After("operateLog()")
    public void after(JoinPoint joinPoint){
        System.out.println("方法结束");
    }

    //在被标注方法后执行,returning = "result"表明该方法返回值信息,"result"需要与方法中一致,下面异常标注同理
    @AfterReturning(pointcut = "operateLog()",returning = "result")
    public void afterReturn(JoinPoint joinPoint, String result){
        System.out.println("方法结束,截获返回值:"+result);
    }

    //在被标注方法后执行,throwing = "e"表明该方法异常值信息
    @AfterThrowing(pointcut = "operateLog()",throwing = "e")
    public void afterThrow(JoinPoint joinPoint,Throwable e){
        System.out.println("抛出异常,异常消息:"+e.getMessage());
    }
}

当然,我们也可以获取注解中的信息

  • 示例1,通过方法签名来获取
OperateLog operateLog=((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(OperateLog.class);
String value = operateLog.value();
  • 示例2,直接注入,@annotation(oplog)表明当前注解,并定义注解对象oplog,下面的方法将oplog自动注入
@Before("operateLog() && @annotation(oplog) ")
public void before(JoinPoint joinPoint,OperateLog oplog){
   String value = oplog.value();
}