Java注解

可以将注解理解为代码块的注释,但是这种注释却又不同于普通的注释,它可以像Java关键字那样对程序产生影响。那么它的原理是什么呢?先来看看注解的分类,注解按程序从源码到运行阶段可以分为三类:

  1. 不会编译到.class文件中的注解
  2. 不会被加载到虚拟机的注解
  3. 会被编译到.class文件并且会被加载到虚拟机的注解

这篇文章主要说明第三种情况,会被加载到JVM中的注解。首先要介绍一下两个关键的元注解,所谓元注解就是注解类的注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface myAnnotation {
    String value();
}

现在已经创建好了一个自定注解,现在对这个注解类上的两个元注解做下解释:
@Target(ElementType.METHOD)这个注解的作用是指定自定义的注解的作用范围,说白了就是你创建的注解能应用在哪些项上,就比如上面创建的注解只能应用在方法上,如下所示:

class ceshi{
    @myAnnotation("hello")
    private void hello(){}
}

上面的代码没有问题,但是如果注解到ceshi类上,编译器就会报错。

@Retention(RetentionPolicy.RUNTIME)这个注解则是用来指定自定义的注解应该保存到什么时候,比如RetentionPolicy.RUNTIME就表示将注解保留到运行时

现在注解也定义好了,元注解上的参数选项也指定好了,那自定义的注解现在能使用了吗?其实现在还缺少一样重要的东西:注解处理器要知道,注解本身并不会做任何处理,它只是一种标记用来告诉注解处理器被标记的地方需要被处理。现在来开始创建一个完整的自定义注解

第一步:创建一个注解接口,用来对Base64编码的字符串进行解码操作

/**
 * 创建一个注解,用来对Base64编码的字符串进行解码操作
 */
//这个注解可以被应用到方法上
@Target(ElementType.METHOD)
//这个注解会被一只保留到运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

第二步:编写一个接口,接口里面有一个方法,用来返回Base64加密的字符串

/**
 * 响应信息的接口
 */
public interface ResponseInterface {
    //用来返回被加密的信息
    @MyAnnotation("decode")
    String getMessage(String ss);

    void print();
}

getMessage被自定义注解注释了,我会用它来处理方法的返回值

第三步:编写实现类

/**
 * 响应信息实现类
 */
public class ResponseMessage implements ResponseInterface{

    //这里返回一条加密过的信息
    @Override
    public String getMessage(String ss) {
        return Base64.getEncoder().encodeToString("这是一条加密信息".getBytes(StandardCharsets.UTF_8));
    }

    @Override
    public void print() {
        System.out.println("hello");
    }
}

第四步:创建代理对象,对所有被@MyAnnotation("decode")注解注释的方法进行处理

/**
 * 自定义注解处理类
 */
public class AnnotationHandler {
    public static <T> T MyAnnotationHandler(T t) {
        Class<?> cl = t.getClass();
        //创建一个代理对象对加密的方法返回结果进行处理
        return (T) Proxy.newProxyInstance(cl.getClassLoader(), cl.getInterfaces(), (proxy, method, args) -> {
            MyAnnotation a = method.getAnnotation(MyAnnotation.class);
            if (a!=null&&a.value().equals("decode")) {
                String baseStr = (String) method.invoke(t, args);
                System.out.printf("加密前信息:%s,开始解码...\n",baseStr);
                return new String(Base64.getDecoder().decode(baseStr), StandardCharsets.UTF_8);
            } else {
                return method.invoke(t, args);
            }
        });
    }
}

注意:这里使用的代理是JDK动态代理,这个代理是基于接口实现的,所以要将注解应用到接口的抽象方法上,否则代理方法中这行method.getAnnotation(MyAnnotation.class);代码获取不到注解信息。

第五步:测试

/**
 * 自定义注解测试
 */
public class AnnotationTest {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        ResponseInterface message = AnnotationHandler.MyAnnotationHandler(new ResponseMessage());
        //这里的参数没有任何意义,忽视就好
        System.out.println(message.getMessage("ss"));
        message.print();
    }
}

运行结果:
加密前信息:6L+Z5piv5LiA5p2h5Yqg5a+G5L+h5oGv,开始解码…
这是一条加密信息
hello

运行成功!

通过编写运行时可保留的自定义注解,并提供一个机遇反射或动态代理的注解处理器,这种形式是开发者比较常用的自定义注解方式。但是前面说过,自定义注解可以应用于三个阶段,这只是其中一种,只需要知道无论在编译前、编译后、运行时都可以自定义对应的注解和相应的处理器

长路漫漫,加油。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值