深入理解JAVA注解以及自定义注解

博客(coder的自我修养)原文链接:http://www.imcoder.fun/archives/1583044290621

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。Java 语言中的类、方法、变量、参数和包等都可以被标注。注解可以看作是一种特殊的标记,在程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。本文对 Annotation 进行了整理带你一步一步解开Java注解的神秘面纱并实现自己的自定义注解。

元注解

元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。他们位于java.lang.annotation包中。

元注解下

  • @Target

  • @Retention

  • @Documented

  • @Inherited

@Target:

源码如下

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

它表明该注解可以应用的java元素类型

  • ElementType.TYPE:应用于类、接口(包括注解类型)、枚举
  • ElementType.FIELD:应用于属性(包括枚举中的常量)
  • ElementType.METHOD:应用于方法
  • ElementType.PARAMETER:应用于方法的形参
  • ElementType.CONSTRUCTOR:应用于构造函数
  • ElementType.LOCAL_VARIABLE:应用于局部变量
  • ElementType.ANNOTATION_TYPE:应用于注解类型
  • ElementType.PACKAGE:应用于包
  • ElementType.TYPE_PARAMETER:1.8版本新增,应用于类型变量)
  • ElementType.TYPE_USE:1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)

@Retention:源码如下

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

它需要在什么级别保存该注释信息,用于描述注解的生命周期

  • RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译时被丢弃,不包含在类文件中

  • RetentionPolicy.CLASS:在class文件中有效(即class保留),JVM加载时被丢弃,包含在类文件中,默认值

  • RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),由JVM 加载,包含在类文件中,在运行时可以被获取到

@Documented:

源码如下

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

它表明该注解标记的元素可以被Javadoc或类似的工具文档化,@Documented是一个标记注解,没有成员。

@Inherited源码如下


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解

是不是有点难以理解,我们举个例子

自定义注解 @InheritedTest

package com.example.demo.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedTest {
}

新建父类 Parent

package com.example.demo.test;

import com.example.demo.annotation.InheritedTest;

@InheritedTest
public class Parent {

    public void testMethod(){
        System.out.println("Parent " + Parent.class.isAnnotationPresent(InheritedTest.class));
    }
}

新建子类 Child

package com.example.demo.test;

import com.example.demo.annotation.InheritedTest;

public class Child extends Parent {
    public static void main(String[] args) {
        Child child = new Child();
        child.testMethod();
        System.out.println("Child " + Child.class.isAnnotationPresent(InheritedTest.class));
    }
}

isAnnotationPresent()方法表示指定注释类型的注释是否存在于此元素上,是则返回true,否则返回false。

我们运行子类可以看到如下结果

 

在子类中我们并没有使用 @InheritedTest 注解,结果一样返回了true,下面我们把InheritedTest中的@Inherited注释掉,然后再运行子类结果如下

 

可以看到,现在返回了false。

简单示例


package com.example.demo.annotation;

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Documented()
@Retention(RetentionPolicy.RUNTIME)
public @interface Color {
    String value() default "";
}

以上我们就自定义了一个注解@Color,该注解应用于属性之上,在运行时有效,并且可以生成api文档。

使用方法

package com.example.demo.test;

import com.example.demo.annotation.Color;

public class Cat {

    @Color("黄色")
    private String color;

    public String getColor(){
        return this.color;
    }

    public void setColor(String color){
        this.color = color;
    }

    public static void main(String args[]){
        Cat cat = new Cat();
        System.out.println(cat.getColor());
    }

}

这里我们定义了一个Cat类,里面有一个属性color,我们使用@Color("黄色")给他赋值为黄色,然后执行这个类打印出结果如下

 

什么?怎么为null呢,是不是感觉被忽悠了?因为我们只是定义了这个注解,但是却没写怎么处理被这个注解标记了字段,这个时候打印出来的当然为null了。

现在我们来修改一下Cat类

public static void main(String args[]) throws NoSuchFieldException {
        Cat cat = new Cat();
        Color color = Cat.class.getDeclaredField("color").getAnnotation(Color.class);
        if (color != null) {
            String value = color.value();
            cat.setColor(value);
        }
        System.out.println(cat.getColor());
    }

打印结果如下

 

以上我们简单的处理了下注解,并把处理逻辑放在了main方法中,其实这是不合理的。我们通过用下面的例子,使用Spring AOP面向切面编程思想来自定义日志注解。

自定义注解

自定义日志注解@SysLog


package com.example.demo.annotation;

import java.lang.annotation.*;

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

定义切面

package com.example.demo.aspect;

import com.example.demo.annotation.SysLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
@Slf4j
public class SysLogAspect {

    @Pointcut("@annotation(com.example.demo.annotation.SysLog)")
    private void logPointCut() {

    }

    @Before("logPointCut()")
    private void before(JoinPoint joinPoint){
        String className = joinPoint.getTarget().getClass().getName();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getName();
        Method method = signature.getMethod();
        SysLog sysLog = method.getAnnotation(SysLog.class);
        String value = sysLog.value();
        log.info(className);
        log.info(methodName);
        log.info(value);
        log.info("这里我们就可以自己处理了");
    }

    @After("logPointCut()")
    private void after(){
        log.info("执行之后");
    }
}

测试注解


package com.example.demo.test;

import com.example.demo.annotation.SysLog;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/log")
public class LogTest {

    @SysLog("测试日志打印")
    @RequestMapping("/test")
    public String testLog(){
        return "这里是测试自定义注解日志打印";
    }

}

用postman访问
localhost:8080/log/test结果如下

image

控制台日志打印如下

image

以上我们就完成了自定日志注解的实现,以上例子只是示例,实际开发中还可以又更多功能实现。好了,本文介绍就到这里了,如有错误请提出指正。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值