反射,注解,动态代理,依赖注入控制反转

一.反射调用方法,获取成员变量值

假如我们有这样一个类:

public class Hello {

    public String name = "张三";

    private int age = 43;

    public String getSex( String aaa) {

        return aaa;
    }

    private String getClassName() {

        return "还在九年义务教育";
    }

}
复制代码


要求不去创建对象去调用里边的方法和获取里边的值,我们可以通过反射去获取成员变量里边的值和调用里边的方法:

1.1 调用public修饰的方法:

try {
    //获取类的字节码
    Class<Hello> clz = Hello.class;
    //获取公共方法对象
    Method mt = clz.getMethod("getSex", String.class);
    //绑定调用方法,获取返回值
    String invoke = (String) mt.invoke(clz.newInstance(), "eeeeee");
    //打印返回值
    Log.e("invoke", invoke);
} catch (Exception e) {
}复制代码

1.2 调用private修饰的方法:

try {
    //获取类的字节码
    Class<Hello> clz = Hello.class;
    //获取私有方法对象
    Method getClassName = clz.getDeclaredMethod("getClassName");
    //打开修饰符限制
    getClassName.setAccessible(true);
    //调用方法
    String invoke = (String) getClassName.invoke(clz.newInstance());
    //打印返回值
    Log.e("invoke", invoke);
} catch (Exception e) {
    e.printStackTrace();
}复制代码

1.3 获取public 修饰的成员变量:

try {
    //获取类的字节码
    Class<Hello> clz = Hello.class;
    //获取公共属性对象
    Field name = clz.getField("name");
    //获取公共属性的名字
    String name1 = name.getName();
    //获取公共属性的值
    Object name2 = name.get(clz.newInstance());
    Log.e("rrrrrrrrre",name1+":"+name2);
} catch (Exception e) {
    e.printStackTrace();
} 复制代码

1.4 获取private修饰的成员变量:

try {
    //获取类的字节码
    Class<Hello> clz = Hello.class;
    //获取私有属性对象
    Field age = clz.getDeclaredField("age");
    //获取私有属性的名字
    String age1 = age.getName();
    //打开权限修饰符开关
    age.setAccessible(true);
    //获取私有属性的值
    int age2 =  age.getInt(clz.newInstance());
    //打印获取到的值
    Log.e("rrrrrrrrre",age1+":"+age2);
} catch (Exception e) {
    e.printStackTrace();
} 复制代码

二. 注解的知识点

2.1.注解

就是 :使用 @interface 标识一下,它也是一种类型 ,同类和接口枚举一样如:

public @interface TestAnnotation {


}复制代码

2.2.元注解

要想让自定义注解管用,需要用的元注解 。元注解就是可以注解到自定义注解上的注解 ,元注解有一下:

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

2.3 @Retention 解释说明了自定义注解的保留期,取值有:

  •  RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 
  •  RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  •  RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

2.4 @Documented 文档,我也不知道干什么用的

2.5 @Target 解释说明了自定义注解的作用范围,有下面的取值:

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

2.6@Inherited Inherited 

是继承的意思,但是它并不是说注解本身可以继承而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。

2.7@Repeatable 重复的意思

2.8 注解只有属性,没有方法,写法: int id() default -1; 名字是:id,类型是int ,默认值是-1 ,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {

    public int id() default -1;

    public String msg() default "Hi";

}复制代码

2.9 注意:

  •  如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内 
  • 注解没有任何属性,应用这个注解的时候,括号都可以省略


三 注解的获取

假如注解要求运行时也可以获取,那么我们怎么获取呢? 通过反射,介绍Class里边的一些方法:

如有类Test 和自定义注解TestAnnotation

//是否有被TestAnnotation注解
boolean annotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if (annotation) {
    //获取注解对象
    TestAnnotation annotation1 = Test.class.getAnnotation(TestAnnotation.class);
    //获取被注解的值
    Log.e("rrrrrr", "" + annotation1.id() + "=========" + annotation1.msg());
}复制代码

四.反射和注解有什么用

仿照ButterKnife写一个类似的功能,但是原理不一样,主要是为了理解反射我注解,当然反射和注解还有很多很大的用处,我能力有限,理解不了那么深,开始贴代码:

布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">
<TextView
    android:layout_width="match_parent"
    android:id="@+id/tv"
    android:text="aaaa"
    android:layout_height="wrap_content" />
</android.support.constraint.ConstraintLayout>复制代码

程序:

@BindRes(R.layout.activity_test)
public class TestAnnotation extends AppCompatActivity {
    @Bind(R.id.tv)
    TextView mTextView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Inject.inJect(this);
        mTextView.setText("XiFanYin");
    }
    @BindClick(R.id.tv)
    public void onClick2(View view) {
        if (view.getId() == R.id.tv) {
            Toast.makeText(this, "点击", Toast.LENGTH_SHORT).show();
        }
    }
}复制代码

自定义注解:

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

}复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindClick {
    int[] value();

}复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindRes {
    int value();
}
复制代码


然后就是关键的一个类,通过反射去找控件赋值,设置点击事件:

public class Inject {
    public static void inJect(AppCompatActivity activity) {

        try {
            bindRes(activity);//绑定布局
            bindView(activity);//查找控件
            bindClick(activity);//查找控件

        } catch (Exception e) {

            e.printStackTrace();
        }
    }

    private static void bindClick(AppCompatActivity activity) {
        Class<? extends AppCompatActivity> clz = activity.getClass();
        Method[] methods = clz.getMethods();//得到所有方法
        for (Method method : methods) {
            if (method.isAnnotationPresent(BindClick.class)) {//方法下是否有BindClick注解
                BindClick annotation = method.getAnnotation(BindClick.class);
                int[] value = annotation.value();
                for (int id : value) {
                    View view = activity.findViewById(id);
                    view.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            method.setAccessible(true);
                            try {
                                method.invoke(activity, view);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();

                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }


            }
        }

    }

    private static void bindRes(AppCompatActivity activity) {
        Class<? extends AppCompatActivity> clz = activity.getClass();
        BindRes annotation = clz.getAnnotation(BindRes.class);
        if (annotation != null) {
            int value = annotation.value();
            activity.setContentView(value);
        }
    }

    public static void bindView(AppCompatActivity activity) throws IllegalAccessException {
        Class<? extends AppCompatActivity> clz = activity.getClass();
        Field[] fields = clz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {

            if (fields[i].isAnnotationPresent(Bind.class)) {
                Bind bind = fields[i].getAnnotation(Bind.class);
                int value = bind.value();
                View view = activity.findViewById(value);
                fields[i].setAccessible(true);

                fields[i].set(activity, view);

            } else {
                System.out.println("没有");
            }
//            System.out.println(fields[i]);
        }
    }
}
复制代码

就实现了类似ButterKnife的功能。

四.动态代理

作用:在不修改被代理对象的源码上,进行功能的增强。

定义一个可以卖东西营业执照接口

public interface Sell {

    void maidongxi();

}复制代码

有一个人想买红旗渠烟,就要实现这个营业执照

public class hongqiqu implements Sell {
    @Override
    public void maidongxi() {
        Log.e("rrrrrrrrrr", "我能卖红旗渠");
    }
}复制代码

过了一段时间,他想挣的更多,就像去卖酒。然后我们需要修改hongqiqu类的代码,如果对于项目而言,要么去继承,要么去使用包装者模式去扩展,很少修改本来的类,今天我们使用动态代理去实现:

public class GuitaiA implements InvocationHandler {

    private Object pingpai;

    public GuitaiA(Object pingpai) {
        this.pingpai = pingpai;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        Log.e("rrrrrrr","我能卖酒了");
        method.invoke(pingpai, objects);
        return null;

    }
}复制代码

看着这个模式有点像包装者模式,只不过加了反射调用,简单的修改都能让他不再去关系去调用什么方法名字是什么。

然后载主界面中,

//红旗渠对象
hongqiqu hongqiqu = new hongqiqu();
//传递到里边
InvocationHandler jingxiao = new GuitaiA(hongqiqu);
//获取对台代理接口
Sell dynamicProxy= (Sell) Proxy.newProxyInstance(hongqiqu.class.getClassLoader()
        , hongqiqu.class.getInterfaces()
        , jingxiao);
dynamicProxy.maidongxi();复制代码

发现没有修改hongqiqu类中的代码,他就可以即卖红旗渠,又可以卖酒

这就是动态代理,把丰富的功能抽离出来,传入谁,我就去丰富拥有我这个功能!完美解耦~

五,依赖注入和控制反转

假如你要出远门,你可以选择火车或者汽车,按照常规编码:

如果选择火车,代码应该这样写

public class Person {

    private HotCar car;

    public Person() {
        car = new HotCar();
    }


    public void chumen() {
        car.drive();

    }
}复制代码

如果选择汽车,代码修改成这样:

public class Person {
//    private HotCar hotCar;
    private Car car;

    public Person() {
        car = new Car();
//        hotCar = new HotCar();
    }


    public void chumen() {
        car.drive();
//        hotCar.drive();
    }
}复制代码

每次选择不同的交通工具,都要修改,能不能不修改?可以这样写:

5.1 抽象接口:

public interface Driveable {
    //驾驶
    void drive();
}
复制代码

5.2.人传入接口

public class Person {


    private Driveable driveable;

    public Person(Driveable driveable) {
        this.driveable = driveable;
    }

    public void chumen() {

        driveable.drive();
    }
}
复制代码

5.3 调用

Car car = new Car();
HotCar hotCar = new HotCar();
Person p = new Person(hotCar);
p.chumen();复制代码

这里传入什么对象去使用什么方法,通过接口去解耦,而不用每次都去修改person类,让我们更专注与原则什么样的交通工具,这个传递的对象就叫依赖注入,控制权同时也进行了转换,不再让person去控制,而是我传入什么,你只能用什么,这就是依赖注入和控制反转。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值