Java注解

1.注解

Java注解又称为java标注,是在JDK5.0引入的一种注释机制。在Java程序中,无论是类,方法,变量还是包等都可以通过注解进行标注,然后可以通过反射获取标注的内容;
注解在编译器编译时被嵌入到字节码文件中,Java虚拟机将其保留下来,在运行时可以获取到对应的内容,同时支持开发人员自定义注解进行辅助开发;
Java 中自带的注解有:@Override、@ Deprecated 等;
Spring 框架中定义的注解有:@Repository、@Component、@Controller 等;
这些注解均是基于元注解来定义的;

2.元注解

元注解是用于修饰注解的注解,所有的自定义注解均由元注解来辅助定义,可以类比为Java的保留字,自定义注解类似用户定义的Function;
元注解有以下几种:
@Retention - 可以简单理解为设置注解的生命周期,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 表示这个注解可以修饰哪些地方(比如方法、还是成员变量、还是包等等)
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

元注解及其他注解均继承于Annotation接口(其UML图如下):


元注解.png

其接口定义如下:

package java.lang.annotation;
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class annotationType();
}

同时Annotation 包中提供了两个关键枚举类,用于声明注解的生命周期及作用范围:ElementType枚举类 及 RetentionPolicy枚举类

RetentionPolicy枚举类,作用于@Retention,表示注解的生命周期范围

package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,            /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */

    CLASS,             /* 编译器将Annotation存储于类对应的.class文件中,即在 class 文件中有效。默认行为  */

    RUNTIME            /* 编译器将Annotation存储于class文件中,并且可由JVM读入,即在运行时有效 */
}

ElementType枚举类:作用于@Target,表示该注解申明的目标作用位置

package java.lang.annotation;

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */

    FIELD,              /* 字段声明(包括枚举常量)  */

    METHOD,             /* 方法声明  */

    PARAMETER,          /* 参数声明  */

    CONSTRUCTOR,        /* 构造方法声明  */

    LOCAL_VARIABLE,     /* 局部变量声明  */

    ANNOTATION_TYPE,    /* 注释类型声明  */

    PACKAGE             /* 包声明  */
}

2.1 自定义注解

通过元注解,我们可以自定义注解来实现一些功能,例如我们需要监控方法的耗时情况,可以自定义一个Monitor注解,被该注解标注的方法均是需要监控的:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Monitor {

    String getClassName() default  "";

    String getFunctionName() default "";
}

@Target(ElementType.METHOD) 表明注解是作用于方法的,如果用在字段或类上会报错;
@Retention(RetentionPolicy.RUNTIME) 表明注解会在运行时生效,编译期间及生成的字节码文件中均包含该注解信息;
@Inherited 表明注解会被子类继承, 父类中被Monitor修饰的方法,其子类继承的方法也会包含该注解,但如果子类对父类方法进行重写则不再生效;
@Documented 表明注解类会被 JavaDoc 工具提取成文档

2.2 AOP

AOP是Aspect Oriented Programming的缩写,意思是:面向切面编程,它是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,其中常见的术语有:

Target(目标对象): 即要增强的对象
Proxy(代理对象): 增强以后的对象
JoinPoint(连接点): 可以被增强的方法
PointCut(切入点): 要被增强的方法
Weaving(织入): 切入点集成到切面形成代理类的过程
Aspect(切面): 切入点+通知/增强 = 切面(即里面有要被增强的方法,以及增强方法具体的增强代码程序)
Advice(通知/增强): 切面功能的具体实现,即增强的功能定义

以上概念之间的关系可以用下图来表示:


关系图.png

2.3 AOP与自定义注解的结合

在将AOP应用到业务场景时,需要定义具体的切入点,仍然以记录监控耗时场景为例,我们需要声明是对哪些方法进行耗时统计,在Java中提供了相应的表达式来对切入点进行定义:

@Aspect
@Component
public class MonitorAspect {

    @Pointcut("execution(* entity.Student.selfIntroduction())")
    private void studentPointCut() {
    }

    @Around("studentPointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("统计指定方法耗时的具体逻辑实现");
        Object o = null;
        try{
            o = pjp.proceed();
        } catch (Exception ex) {
            throw ex;
        }
        return o;
    }

    @Pointcut("@annotation(annotation.Monitor)")
    private void monitorAnnotationAspect() {
    }

    @Around("monitorAnnotationAspect()")
    public Object aroundV2(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("统计指定方法耗时的具体逻辑实现");
        Object o = null;
        try{
            o = pjp.proceed();
        } catch (Exception ex) {
            throw ex;
        }
        return o;
    }
}

MonitorAspect 切面定义了两个切入点,首先看第一个切入点studentPointCut:该声明是对实体类Student. selfIntroduction方法进行增强,统计该方法的耗时,当要统计耗时的方法较多时,使用该方式的切面表达式会比较繁琐且每增加一个方法的统计均需要在此进行注册;如果结合自定义注解,我们可以很简洁的来实现该功能,通过第二个切入点monitorAnnotationAspect的定义,只要被@Monitor注解修饰的方法,均为切入点,如果后续出现新方法需要统计耗时,仅需使用@Monitor修饰方法变可以达到;

你可能感兴趣的:(Java注解)