注解反射动态代理的简单学习

一.注解

java中注解默认实现annotation接口,一般我们自定义注解的时候主要使用的是两个元注解(其他两个@Documented 与 @Inherited基本没怎么使用过)看下面

1.@Target

主要限制可以应用注解的java元素类型
ElementType.ANNOTATION_TYPE 可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。

2.@Retention

RetentionPolicy.CLASS
RetentionPolicy.SOURCE
RetentionPolicy.RUNTIME

RetentionPolicy.CLASS

仅仅保留在源码中,被编译器忽略,主要应用场景是apt技术,在编译期能够获取注解与注解声明的类包括类中所有成员信息,一般用于生成额外的辅助类。还有语法检查,类似@IntDef这种

RetentionPolicy.SOURCE

会被编译器保留,但是会被jvm忽略,用处主要是用在字节码增强技术,在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。

RetentionPolicy.RUNTIME

用的比较多,主要是通过反射技术动态获取注解元素

简单应用

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface lengthTest {
    int max() default 0;
    int min() default  0;
    String dec() default "";
}

public class AnnLoginModel {

    private int age;

    @lengthTest(max = 10,min = 6,dec = "kkk")
    private String desc;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
    
}

 public static void vaild(Object o) throws Exception{
        Class ls = o.getClass();
        Field[] fields = ls.getDeclaredFields();
        for (Field field:fields) {
            lengthTest lengthTest =  field.getAnnotation(lengthTest.class);
            if(lengthTest!=null){
                if(field.getGenericType().toString().equals("class java.lang.String")){
                    field.setAccessible(true);
                    String i = (String) field.get(o);
                    if(i.length()>lengthTest.min()&&i.length()< lengthTest.max()){
                        System.out.print("没超过");
                    }else{
                        System.out.print("超过");
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        AnnLoginModel annLoginModel = new AnnLoginModel();
        annLoginModel.setAge(2);
        annLoginModel.setDesc("傻子");
        vaild(annLoginModel);
    }


这是注解加反射进行参数校验的简单例子

二.反射

反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和
方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。

 
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
}

activity中

 @ViewInject(R.id.bt)
    Button bt;

    @ViewInject(R.id.tv)
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_annotation);
        MyViewInject.ViewInject(this);

    }

    public static void ViewInject(Activity activity) {
        Class ls = activity.getClass();
        for (Field field : ls.getDeclaredFields()) {
            ViewInject an = field.getAnnotation(ViewInject.class);//判断是否是viewinject注解
            if (an != null) {
                try {
                    field.setAccessible(true);//设置访问权限,应许操作private的属性
                    Method method = ls.getDeclaredMethod("findViewById", int.class);//拿取activity的findviewbyid方法,当然你也可以直接使用ls.findviewbyid
                    View view = (View) method.invoke(activity, an.value());//method(ac,an.value)第一个参数在哪个对象上设置调用,第二个参数方法的参数
                    field.set(activity, view);//反射字段属性赋值
                } catch (Exception e) {

                }
            }
        }

简单例子

1.获取class

一般可以通过类名,对象获取

2.创建实例

可以通过class对象的newinstance,或者构造方法constructor.newInstance
其他方法懒的介绍有点多

3.反射的缺点

Method#invoke 需要进行自动拆装箱
反射需要按照名检索方法和参数,需要检查方法可见性参数一致性,编译器无法对动态调用的代码做优化,比如内联

三.动态代理,静态代理

1.静态代理

一般都是定义一个接口,一个对象实现接口,新建个代理对象,对对象进行控制管理。简单来说通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性。
静态代理缺点,一般代理对象和对象是一对一关系,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代
理对象会出现扩展能力差的问题。

2.动态代理

JDK提供了 Proxy 来完成这件事情,其实就是在运行期动态创建了一个class,然后通过反射创建method,通过
InvocationHandler回调出去


image.png

简单例子

  ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(activity,method);//activity当前activity对象,就是哪里调用onclick方法
                        Object proxyInstance = Proxy.newProxyInstance(activity.getClassLoader(), new Class[]{clickClass}, invocationHandler);//把onclick方法交给invocation处理
                        for (int id : ids) {
                            View view = activity.findViewById(id);
                            Method setter = view.getClass().getMethod(str, clickClass);//获取onclick方法
                            setter.invoke(view, proxyInstance);//使用动态代理,setter就是系统的onclick方法view就是注解的控件id,proxyinstance就是代理类,代理类就是activity写的onclick方法
                        }


public class ListenerInvocationHandler implements InvocationHandler {

    Activity ac;

    Method method;

    public ListenerInvocationHandler(Activity ac,Method method){
        this.ac = ac;
        this.method = method;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return this.method.invoke(ac,args);
    }
}

你可能感兴趣的:(注解反射动态代理的简单学习)