新建SpringBoot 项目

选择Spring Initializr 点击Next

spring boot 自定义注解使用 springboot自定义注解 解析_java

写好名字,Java version选择8,点击Next

spring boot 自定义注解使用 springboot自定义注解 解析_System_02

选择Web,点击Next


spring boot 自定义注解使用 springboot自定义注解 解析_spring_03

写好名字,点击Finish,项目创建完成。


spring boot 自定义注解使用 springboot自定义注解 解析_System_04

 添加aop依赖

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

自定义注解

创建annotation文件夹(名字随意),然后新建一个注解类 MyAnnotation ,注意这是一个@interface,在 Kind 处选择Annotation

interface :声明了这是一个java 的接口

@interface : 用来修饰 Annotation,请注意,它不是 interface。这个关键字声明隐含了一个信息:它继承了 java.lang.annotation.Annotation 接口,而不是声明了一个 interface。

spring boot 自定义注解使用 springboot自定义注解 解析_System_05

代码如下: 

package com.example.demo.annotation;

import java.lang.annotation.*;


@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {

    String value();
}

关于@Target @Retention @Documented 这几个注解可以参考一下这个文件:


@Component 注解参考文章:

@Pointcut 可以参考一下这个文章:

然后创建切面类,注意:要选择@Aspct,或者直接创建一个class类,然后在类上方添加@Aspect注解。


spring boot 自定义注解使用 springboot自定义注解 解析_System_06



spring boot 自定义注解使用 springboot自定义注解 解析_java_07



代码如下:

package com.example.demo.annotation;

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

import java.util.Arrays;

@Aspect
@Component
public class MyAspect {
    /*
     * 切入点-- @Pointcut --》定义需要切面的地方,表达式参数
     * 匹配top.alanlee.template.controller包及其子包下的所有类的所有方法
     * 切入点方法不用写代码,返回类型为void
     * execution:用于匹配表达式 将符合条件下的方法和合适的参数作为切面引入
     */
    //匹配所有com.example.demo.annotation包下所有类的所有方法
    //@Pointcut("execution(* com.example.demo.annotation.*.*(..))")
    @Pointcut("@annotation(com.example.demo.annotation.MyAnnotation)")
    public void myPointCut(){

    }

    /*
    * 前置通知,目标方法调用前被调用
    * */
    @Before("myPointCut()")
    public void beforePointcut(JoinPoint joinPoint){
        System.out.println("前置通知开始。。。");
        Signature signature = joinPoint.getSignature();
        System.out.println("目标方法的签名:" + signature);
        System.out.println("代理方法的名字:" + signature.getName());
        Object[] args = joinPoint.getArgs();
        System.out.println("获取目标方法的参数信息:" + Arrays.asList(args));
        System.out.println("前置通知结束。。。");
    }

    /**
     * 环绕通知
     * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
     * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     * @param proceedingJoinPoint
    **/
    @Around("myPointCut()")
    public void arroundPointcut(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知开始。。。");
        String name = proceedingJoinPoint.getSignature().getName();
        System.out.println("环绕通知的目标方法名:" + name);
        proceedingJoinPoint.proceed();//执行到这里开始走进来的方法体(必须声明)
        System.out.println("环绕通知结束。。。");
    }

    /*
    最终通知,目标方法执行完之后执行
     */
    @After("myPointCut()")
    public void afterPointcut(){
        System.out.println("后置通知。。。");
    }

    /*
     * 后置返回通知
     * 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
     * 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
     * returning 只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行
     * @param joinPoint
     * @param keys
    * */
    // @AfterReturning(value = "execution(* com.haijiao12138.demo.spring.aop0813.AopController1..*.*(..))", returning = "keys")
    @AfterReturning("myPointCut()")
    public void afterReturn(JoinPoint joinPoint){
        System.out.println("后置返回通知。。。");

    }

    /** 后置异常通知
     * 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
     * throwing 只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
     */
     @AfterThrowing(value = "myPointCut()", throwing = "e")
//    @AfterThrowing("myPointCut()")
    public void afterThrow(JoinPoint joinPoint, NullPointerException e){
         System.out.println("后置异常通知。。。");
    }

}

上面基本上完成了自定义注解的开发,在使用中可以直接使用 @MyAnnotation("") 即可。

这里在方法上加了 @MyAnnotation("") 注解(后面括号不可以省略),如果在定义注解类时,即在 MyAnnotation类里设置默认值就可以只写 @MyAnnotation (因为有默认值,所以可以省略括号里的双引号)。当然,需要设置后面括号里的内容时不可以省略。

public @interface MyAnnotation { String value() default ""; }

代码如下:

package com.example.demo.annotation;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/myAnnotation")
public class TestController {

    @MyAnnotation("")
    @RequestMapping("/test")
    public String test(String key){
        System.out.println("test--myAnnotation:" + key);
        return "test--myAnnotation:" + key;
    }

    @MyAnnotation("")
    @RequestMapping("testAfterThrowing")
    public String testAfterThrowing(String key){
        System.out.println(key);
        throw new NullPointerException(key);
    }

    @MyAnnotation("")
    @RequestMapping("/testAround")
    public String testAround(String key){
        System.out.println("环绕通知:key = " + key);
        return "环绕通知:key = " + key;
    }

}

测试

1-测试test,访问:http://localhost:8080/myAnnotation/test?key=hello

spring boot 自定义注解使用 springboot自定义注解 解析_java_08

 2-测试testAfterThrowing,访问:http://localhost:8080/myAnnotation/testAfterThrowing?key=erroe



3-测试环绕通知testAround,访问:http://localhost:8080/myAnnotation/testAround?key=arround 

spring boot 自定义注解使用 springboot自定义注解 解析_spring boot_09