注解 + 代理 模仿 Retrofit 传参

926 阅读2分钟

前几天学习了一下注解和代理。这里记录一下使用自定义注解 + 动态代理,来模仿 Retrofit 的传参方式。

注解

Java 元注解有四种,这是系统定义的用于定义注解时,区分他们的不同作用。

  • @Target
  • @Retention
  • @Documented
  • @Inherited

@Target

用于描述注解的使用范围(即:被描述的注解可以用在什么地方) 取值类型有:

ElemenetType.CONSTRUCTOR 构造器声明
ElemenetType.FIELD 域声明(包括 enum 实例) 
ElemenetType.LOCAL_VARIABLE 局部变量声明 
ElemenetType.METHOD 方法声明 
ElemenetType.PACKAGE 包声明 
ElemenetType.PARAMETER 参数声明 
ElemenetType.TYPE 类,接口(包括注解类型)或enum声明

以上有些部分没有领悟到用法。。下面用到的也就 PARAMETER , METHOD 两种。

@Retention

定义了该Annotation被保留的时间长短. 取值类型:

RetentionPolicy.SOURCE 注解将被编译器丢弃 ,在源文件中有效(即源文件保留)

RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃 。在class文件中有效(即class保留)

RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。在运行时有效(即运行时保留)

一般常用的是 RUNTIME 类型

@Documented

用于描述其它类型的 Annotation 应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

@Inherited

指示允许子类继承父类中的注解,用于该类的子类。

动态代理

先定义一个接口:

public interface Subject {
    String doSomething();
}

InvocationHandler 实现类:

public class DynamicProxy implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy start perform");
        System.out.println(method.getDeclaringClass());
        return "测试内容";
    }
}

调用:

public static void main(String[] args) {

        DynamicProxy proxy = new DynamicProxy();
        Subject p = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), new Class[]{Subject.class}, proxy);
        System.out.println(p.doSomething());
    }

invoke 方法当 doSomething 方法被调用时,才会执行。

执行结果:

proxy start perform
interface com.lvfq.reflect.proxy.Subject
测试内容

模仿 Retrofit 传参

  • 定义注解:
@Target(ElementType.METHOD) // 方法注解
@Retention(RetentionPolicy.RUNTIME)
@interface Say {
    String value();
}

@Target(ElementType.PARAMETER) //参数注解
@Retention(RetentionPolicy.RUNTIME)
@interface Query {
    String value();
}

@Target(ElementType.PARAMETER)//参数注解
@Retention(RetentionPolicy.RUNTIME)
@interface Filed {
    String value();
}
  • 定义 InvocationHandler 实现类:
static class ProxyHandler implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//            Annotation[] deAn = method.getDeclaredAnnotations();
//            for (int i = 0; i < deAn.length; i++) {
//                Annotation an = deAn[i];
//                if (Say.class.isInstance(an)) {
//                    System.out.println(((Say) an).value());
//                }
//            }
            Say say = method.getAnnotation(Say.class);
            Annotation[][] param = method.getParameterAnnotations();  // 获取参数注解
            String str = "?" + say.value();
            for (int i = 0; i < param.length; i++) {
                Annotation[] p = param[i];
                for (int j = 0; j < p.length; j++) {
                    Annotation a = p[j];
                    if (Query.class.isInstance(a)) {
                        Query query = (Query) a;
                        str += "&" + ((Query) a).value() + "=" + args[i];
                    } else if (Filed.class.isInstance(a)) {
                        str += "@" + ((Filed) a).value() + "=" + args[i];
                    }
                }
            }
//            System.out.println(str);
            return str;
        }
    }
  • 使用注解:
interface Subject {
       @Say("方法")
       String getName(@Query("name") String name, @Filed("nick") String nick);
   }
  • 最终调用:
 public static void main(String[] args) {
        ProxyHandler handler = new ProxyHandler();
        Subject s = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, handler);
        System.out.println(s.getName("发强", "强哥"));
    }
  • 输出结果
?方法&name=发强@nick=强哥