注解

170 阅读6分钟

1、介绍

注解是Java5开始引入的新特征,它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。

2、作用

1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等
2、跟踪代码依赖性,实现替代配置文件功能。比如Dagger 2 依赖注入,未来java 开发,将大量注解配置,具有很大用处;
3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。

3、原理

注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的invoke 方法。该方法会从memberValues 这个Map 中索引出对应的值。而memberValues 的来源是Java 常量池。

4、元注解

元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。Java5定义了 4 个注解,分别是 @Documented、@Target、@Retention 和 @Inherited。Java 8 又增加了 @Repeatable 和 @Native 两个注解。

4.1、@Documented

@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 Javadoc 工具提取成文档。默认情况下,Javadoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 Javadoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。

4.2、@Target

@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组,下表为 ElementType 常用的枚举常量。

ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注解
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

4.3、@Retention

@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型,RetentionPolicy 有 3 个枚举常量,如下所示。保留时长后面的包含前面的。

SOURCE:在源文件中有效(即源文件保留)
CLASS:在 class 文件中有效(即 class 保留)
RUNTIME:在运行时有效(即运行时保留)

4.4、@Inherited

@Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。

4.5、@Repeatable

@Repeatable 注解是 Java 8 新增加的,它允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。重复使用赋予不同的值,相当于全都放到集合之中。

4.6、@Native

使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。

5、常见标准的Annotation

1.)Override
java.lang.Override 是一个标记类型注解,它被用作标注方法。它说明了被标注的方法重写了父类的方法,起到了断言的作用。如果我们使用了这种注解在一个没有覆盖父类方法的方法时,java 编译器将以一个编译错误来警示。
2.)Deprecated
Deprecated 也是一种标记类型注解。当一个类型或者类型成员使用@Deprecated 修饰的话,编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的“延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated,但编译器仍然要报警。
3.)SuppressWarnings
SuppressWarning 不是一个标记类型注解。它有一个类型为String[] 的成员,这个成员的值为被禁止的警告名。对于javac 编译器来讲,被-Xlint 选项有效的警告名也同样对@SuppressWarings 有效,同时编译器忽略掉无法识别的警告名。
@SuppressWarnings("unchecked")

6、自定义注解

6.1、规则

自定义注解需要使用到元注解

  1. Annotation 型定义为@interface, 所有的Annotation 会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口;
  2. 参数成员只能用public 或默认(default) 这两个访问权修饰;
  3. 参数成员只能用基本类型byte、short、char、int、long、float、double、boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组;
  4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation 对象,因为你除此之外没有别的获取注解对象的方法;
  5. 注解也可以没有定义成员,,不过这样注解就没啥用了。

6.2、自定义注解实现AOP

1.引入依赖

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

2.定义注解

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface needAuth {
    String value() default "";
}

3.创建你使用AOP要做的事情--通知,比如权限控制啊,调用方法需要自定义的日志服务啊什么的

@Component
@Aspect
public class LogAspect {
 
//    @Pointcut("execution(* com.csj.springdemo.controller..*(..))")
    @Pointcut("@annotation(com.csj.springdemo.annotation.needAuth)")
    public void logPoint(){//这就是个标志,爱叫啥叫啥,给下面用的
    }
 
    @Before("logPoint()")
    public void beforeAop(){
        System.out.println("前置通知...");
    }
    @After("logPoint()")
    public void afterAop(){
        System.out.println("后置通知...");
    }
    @Around("logPoint()")
    public void aroundAop(ProceedingJoinPoint pj) throws Throwable {
        System.out.println("环绕通知前...");
        pj.proceed();//这个有点东西,几个注解的执行顺序有关
        System.out.println("环绕通知后...");
    }
}

注意:@Aspect注解不能少了

4.验证测试

@RestController
@RequestMapping("/aop")
public class SpringAopTestController {
    @Autowired
    SpringAopTestService springAopTestService;
 
    @RequestMapping("/test")
    public void aopTest(){
        springAopTestService.message();
    }
    @RequestMapping("/anoMessage")
    public void anoMessage(){
        springAopTestService.anoMessage();
    }
}

@Service
public class SpringAopTestService {
 
    public void message(){
        System.out.println("执行方法message");
    }
 
    @needAuth()
    public void anoMessage(){
        System.out.println("执行方法needAuth");
    }
}

6.3、自定义注解实现AOP原理

1、调用流程: 图片.png 2、源码执行流程: 图片.png 上图名词解释: 图片.png

原文链接:blog.csdn.net/qq_34056683…
原文链接:blog.csdn.net/elapse_/art…
原文链接:www.cnblogs.com/acm-bingzi/…