java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java自定义注解

一文详解Java如何实现自定义注解

作者:kkkkatoq

Java实现自定义注解其实很简单,跟类定义差不多,只是属性的定义可能跟我们平时定义的属性略有不同,这篇文章主要给大家介绍了关于Java如何实现自定义注解的相关资料,需要的朋友可以参考下

一、@interface 关键字

我们想定义一个自己的注解 需要使用 @interface 关键字来定义。

如定义一个叫 MyAnnotation 的注解:

public @interface MyAnnotation { }

二、元注解

  光加上 @interface 关键字 还不够,我们还需要了解5大元注解

 1)  @Retention 指定注解的生命周期    

@Retention(RetentionPolicy.SOURCE)

其中Retention是一个枚举类:

2) @Target指定注解可以修饰的元素类型

@Target(ElementType.Field)

 3)@Documented指定注解会被JavaDoc工具提取成文档。默认情况下,JavaDoc是不包括文档的

 4)@Inherited表示该注解会被子类继承,注意,仅针对类,成员属性、方法并不受此注释的影响。

 5)@Repeatable表示注解可以重复使用,为了解决同一个注解不能重复在同一类/方法/属性上使用的问题。

其中最常用的就是 @Retention 跟 @Target。

三、简单实现

例如实现一个简单,在标记注解的地方打印一句日志。

定义一个 MyAnnotation 注解,并且定义一个属性 message 默认值是 ”aaa“。先将该注解加到字段上,看能不能获取到。

//注解用于字段上
@Target(ElementType.FIELD)
//运行时使用
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String message() default  "aaa";
}

定义一个Student类用于测试: 

@Data
public class Student {
    @JSONField(ordinal =0)
    @MyAnnotation(message = "AAAAAAAAA")
    public String name;
    @MyAnnotation(message = "AAAAAAAAA")
    public Integer score;
}

在字段上标注该注解,然后编写一个main方法获取该注解的属性:

    public static void main(String[] args) {
        Class<?> studentClass = Student.class;
        Field[] fields = studentClass.getDeclaredFields();//获取所有的类成员变量字段
        for (Field field : fields) {
            String fieldName = field.getName(); //获取该类成员变量的名字
            System.out.println("成员变量名是:" + fieldName);
            Annotation[] annotations = field.getAnnotations(); //获取该类成员变量上所有声明周期是运行时的注解
            for (Annotation annotation : annotations) {
                Class<? extends Annotation> annotationType = annotation.annotationType();
                String annotationName = annotationType.getSimpleName();//注解的简短名称
                System.out.println(" 使用的注解是:" + annotationName);
                //判断该注解是不是 MyAnnotation 注解,是的话打印其 id 和 describe 属性
                if (annotationType.equals(MyAnnotation.class)) {
                    MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);
                    String message = myAnnotation.message();
                    System.out.println("    MyAnnotation注解中的message是:" + message);

                }
            }
            System.out.println();
        }
    }

 执行后打印的内容:

以上就是一个注解的简单实现。

四、使用切面执行自定义注解逻辑

在开发中一般加上注解之后会自动执行一些逻辑,大部分实现的原理是使用切面来实现注解的逻辑的。

1) 首先将刚才的注解修改成放在方法上的:

//注解用于方法
@Target(ElementType.METHOD)
//运行时使用
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    String message() default  "aaa";

}

2) 定义一个切面类:

@Component
@Aspect
@Slf4j
public class MyAnnotationAspect {
    /*
     * 这是一个切入点
     * */
    @Pointcut("@annotation(com.demo.aaa.annotation.MyAnnotation)")
    public void cutMethod(){
    }
    /**
     * 切点之前
     */
    @Before("cutMethod()")
    public void before(JoinPoint joinPoint) throws Throwable {
        log.info("============ before ==========");
    }
    /**
     * 切点之后
     */
    @After("cutMethod()")
    public void after() throws Throwable {
        log.info("============ after ==========");
    }
    /**
     * 切点返回内容后
     */
    @AfterReturning("cutMethod()")
    public void afterReturning() throws Throwable {
        log.info("============ afterReturning ==========");
    }
    /**
     * 切点抛出异常后
     */
    @AfterThrowing("cutMethod()")
    public void afterThrowing() throws Throwable {
        log.info("============ afterThrowing ==========");
    }
    @Around("cutMethod() && @annotation(myAnnotation)")
    public Object around(ProceedingJoinPoint point, MyAnnotation myAnnotation) throws Throwable {
        log.info("============ around1 ==========");
        Object  obj= point.proceed(point.getArgs());
        log.info("============ around2 ==========");
        return obj;
    }
}

在使用aop之前需要先引入一个依赖: 

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

   简单说一下各个注解代表什么含义:

 3)将注解放入到接口方法中测试:

    @GetMapping("/aaa")
    @MyAnnotation(message = "成功拉!!!!!!!!!!!!")
    public void test() {
        System.out.println("执行代码逻辑");
    }

   调用接口之后打印

上面就是自定义注解最简单的示例。

五、切点表达式

我们定义切点除了使用 @Pointcut() 之外,我们还有丰富的切点表达式可以定义切点。

1)切点表达式简介      

2)通配符合与逻辑运算符

 @AspectJ 支持三种通配符:

逻辑运算符: 切点表达式由切点函数组成,切点函数之间还可以进行逻辑运算,组成复合切点。

3)切点表达式:

1.arg() :匹配切入点方法的参数类型,匹配的上才是切点。

语法:args(param-pattern)   param-pattern:参数类型的全路径。

注意:要先匹配到某些类,不然会报错,也就是不能单独用

 示例:

@Pointcut("args(java.lang.String)")  //这样就是错的,不能单独使用要匹配到某些类

@Pointcut("within(com.example.demo.service.impl.UserServiceImpl) && args(java.lang.String,java.lang.String)") //要像这样使用 within 先匹配到某个具体的类,在使用args匹配到某个类型参数的方法

2.@args:匹配切入点方法上的参数的类上,参数的类必须要有指定的注解

语法:@args(annotation-type)   annotation-type:注解类型的全路径

注意:也不能单独使用,必须先指定到类,而且匹配参数个数至少有一个且为第一个参数的类含有该注解才能匹配的上

示例:

@Pointcut("within(com.demo.RedisTest) &amp;&amp; @args(com.demo.aaa.annotation.MyAnnotation)")

3.within:匹配切入点的指定类的任意方法,不能匹配接口。

语法:within(declaring-type)   参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕

注意: 这个是指定到具体的类

示例:

//within表达式的粒度为类,其参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕。如下是within表达式的语法:
@Pointcut(within(declaring-type-pattern))

//within表达式只能指定到类级别,如下示例表示匹配com.spring.service.BusinessObject中的所有方法:
@Pointcut(within(com.spring.service.BusinessObject))
      
//within表达式路径和类名都可以使用通配符进行匹配,比如如下表达式将匹配com.spring.service包下的所有类,不包括子包中的类:
@Pointcut(within(com.spring.service.*))

//如下表达式表示匹配com.spring.service包及子包下的所有类:
@Pointcut(within(com.spring.service..*))

4.@within:表示匹配带有指定注解的类。

语法:@within(annotation-type)   注解的全类名

注意:这个是指定到带有某个注解的类

示例:

//如下所示示例表示匹配使用com.spring.annotation.BusinessAspect注解标注的类:
@within(com.spring.annotation.BusinessAspect)

5.@annotation() :匹配带有指定注解的连接点

语法:@annotation(annotation-type)  annotation-type:注解类型的全路径

示例:

@Pointcut("@annotation(com.test.annotations.LogAuto)")

6.execution() 用于匹配是连接点的执行方法,Spring 切面粒度最小是达到方法级别,而 execution 表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的配置,所以是使用最广泛的。

用法:

示例:

modifiers-pattern:方法的可见性修饰符,如 public,protected,private;
ret-type-pattern:方法的返回值类型,如 int,void 等;
declaring-type-pattern:方法所在类的全路径名,如 com.spring.Aspect;
name-pattern:方法名,如 getOrderDetail();
param-pattern:方法的参数类型,如 java.lang.String;
throws-pattern:方法抛出的异常类型,如 java.lang.Exception;
示例:

// 匹配目标类的所有 public 方法,第一个 * 代表返回类型,第二个 * 代表方法名,..代表方法的参数
execution(public * *(..))

// 匹配目标类所有以 User 为后缀的方法。第一个 * 代表返回类型,*User 代表以 User 为后缀的方法
execution(* *User(..))

// 匹配 User 类里的所有方法
execution(* com.test.demo.User.*(..))

// 匹配 User 类及其子类的所有方法
execution(* com.test.demo.User+.*(..)) :

// 匹配 com.test 包下的所有类的所有方法
execution(* com.test.*.*(..))

// 匹配 com.test 包下及其子孙包下所有类的所有方法
execution(* com.test..*.*(..)) :

// 匹配 getOrderDetail 方法,且第一个参数类型是 Long,第二个参数类型是 String
execution(* getOrderDetail(Long, String))

六、切面中获取各个参数

示例:

   @Around(value = "@annotation(basisLogAnnotation)")
    public Object demoAop(ProceedingJoinPoint proceedingJoinPoint, final BasisLogAnnotation basisLogAnnotation) throws Throwable {
 
        logger.debug("执行前:");
 
        Object object = proceedingJoinPoint.proceed();  //执行连接点方法,object:方法返回值
 
        logger.debug("执行后:");
 
 
        // 类名
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        //方法名
        String methodName = proceedingJoinPoint.getSignature().getName();
        //参数(我这里是对象,具体根据个人的参数类型来强转)
        BasisUser basisUser = (BasisUser)proceedingJoinPoint.getArgs()[0];
        return object;
    }

总结  

到此这篇关于Java如何实现自定义注解的文章就介绍到这了,更多相关Java自定义注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文