代理、反射、注解、hook

代理

通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,扩展目标对象的功能。
代理对象拦截真实对象的方法调用,在真实对象调用前/后实现自己的逻辑调用
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。

动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换。

/**
* subject(抽象主题角色):
* 真实主题与代理主题的共同接口。
*/
interface Subject {
??? void sellBook();
}

/**
* ReaISubject(真实主题角色):
* 定义了代理角色所代表的真实对象。
*/
public class RealSubject implements Subject {

??? @Override
??? public void sellBook() {
??????? System.out.println("出版社卖书");
??? }
}

/**
* Proxy(代理主题角色):
* 含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真实主题对象之前或者之后执行某些操  作,而不是单纯返回真实的对象。
*/

public class ProxySubject implements Subject {

??? private RealSubject realSubject;

??? @Override
??? public void sellBook() {
??????? if (realSubject == null) {
??????????? realSubject = new RealSubject();
??????? }
??????? sale();
??????? realSubject.sellBook();
??????? give();
??? }

??? public void sale() {
??????? System.out.println("打折");
??? }

??? public void give() {
??????? System.out.println("送优惠券");
??? }
}

public class Main {

??? public static void main(String[] args) {

??????? //静态代理(我们自己静态定义的代理类)
??????? ProxySubject proxySubject = new ProxySubject();
??????? proxySubject.sellBook();

??????? //动态代理(通过程序动态生成代理类,该代理类不是我们自己定义的。而是由程序自动生成)
??????? RealSubject realSubject = new RealSubject();
??????? MyHandler myHandler = new MyHandler();
??????? myHandler.setProxySubject(realSubject);
??????? Subject subject = (Subject)
Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
?????????realSubject.getClass().getInterfaces(), myHandler);
??????? subject.sellBook();
??? }
}

public class MyHandler implements InvocationHandler {
??? private RealSubject realSubject;

??? public void setProxySubject(RealSubject realSubject) {
??????? this.realSubject = realSubject;
??? }

??? /**
???? * @param proxy?  指代我们所代理的那个真实对象
???? * @param method?? 指代的是我们所要调用真实对象的某个方法的Method对象
???? * @param args??  指代的是调用真实对象某个方法时接受的参数
???? * @reurn
???? * @throws Throwable
???? */
??? @Override
??? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
??????? sale();
??????? proxy = method.invoke(realSubject, args);
??????? give();
??????? return proxy;
??? }

??? public void sale() {
??????? System.out.println("打折");
??? }

??? public void give() {
??????? System.out.println("送优惠券");
??? }
}


HOOK

hook翻译就是钩子。而「钩子」的意思,就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件。

Hook 的选择点:
静态变量和单例,因为一旦创建对象,它们不容易变化,非常容易定位。
Hook 过程:
寻找 Hook 点,原则是静态变量或者单例对象,尽量 Hook public 的对象和方法。
选择合适的代理方式,如果是接口可以用动态代理。
偷梁换柱——用代理对象替换原始对象。
Android 的 API 版本比较多,方法和类可能不一样,所以要做好 API 的兼容工作

应用

  • Hook指定应用注入广告
  • 修复bug
  • App登录劫持

    登录界面上面的用户信息都存储在EditText控件上,然后通过用户手动点击“登录”按钮才会将上面的信息发送至服务器端去验证账号与密码是否正确。这样就很简单了,黑客们只需要找到开发者在使用EditText控件的getText方法后进行网络验证的方法,Hook该方法,就能劫持到用户的账户与密码了

hook练习


AOP(AspectJ)

正如面向对象编程是对常见问题的模块化一样,面向切面编程是对横向的同一问题进行模块化,比如在某个包下的所有类中的某一类方法中都需要解决一个相似的问题,可以通过AOP的编程方式对此进行模块化封装,统一解决
关于AOP的具体解释,可以参照维基百科。而AspectJ就是面向切面编程在Java中的一种具体实现。

Join point:程序中执行代码插入的点,例如方法调用时或者方法执行时。

AOP编程的具体使用场景
日志记录
持久化
行为监测
数据验证
缓存
...

比如埋点,记录方法执行的时长。可以定义注解。aop可以过滤所有被"这个注解"标记的方法和构造器。然后可以可以根据他提供的方法(注解),讲我们想要埋点的日志插入进去。

反射和注解
反射:对于任何一个对象,都能够调用它的任何一个方法和属性,包括私有的。这种动态获取的方法就叫反射。
注解:降低项目的耦合度;自动完成一些规律性的代码;自动生成java代码,减轻开发者的工作量。
@Retention:注解保留的生命周期
@Target:注解对象的作用范围。
创建一个注解遵循: public @interface 注解名 {方法参数}


@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface getViewTo {
    int value() default  -1;
}

public class MainActivity extends AppCompatActivity {

    @getViewTo(R.id.textview)
    private TextView mTv;

    @getViewTo(R.id.button)
    private Button mBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //通过注解生成View;
        getAllAnnotationView();
    }

    /**
     * 解析注解,获取控件
     */
    private void getAllAnnotationView() {
        //获得成员变量
        Field[] fields = this.getClass().getDeclaredFields();

        for (Field field : fields) {
          try {
            //判断注解
            if (field.getAnnotations() != null) {
              //确定注解类型
              if (field.isAnnotationPresent(GetViewTo.class)) {
                //允许修改反射属性
                field.setAccessible(true);
                GetViewTo getViewTo = field.getAnnotation(GetViewTo.class);
                //findViewById将注解的id,找到View注入成员变量中
                field.set(this, findViewById(getViewTo.value()));
              }
            }
          } catch (Exception e) {
          }
        }
      }

}

注解和反射效率问题
反射先new类class,然后在从类里面new对象。Class.getMethod(...)还要查找所有的方法。
而注解编译期间就完成了注解的反射工作,?jvm只是读取。


反射机制

JAVA反射机制是在运行状态中,对于任意一个类 (class文件),都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。?
动态获取类中信息,就是java反射 。可以理解为对类的解剖。

Person

public class Person {

    private int age;
    private String name;

    public Person(String name,int age) {
        super();
        this.age = age;
        this.name = name;

        System.out.println("Person param run..."+this.name+":"+this.age);

    }
    public Person() {
        super();

        System.out.println("person run");

    }

    public void show(){
        System.out.println(name+"...show run..."+age);
    }

    private void privateMethod(){
        System.out.println(" method run ");
    }

    public void paramMethod(String str,int num){
        System.out.println("paramMethod run....."+str+":"+num);

    }
    public static void staticMethod(){
        System.out.println(" static method run......");
    }
}

要想要对字节码文件进行解剖,必须要有字节码文件对象.
如何获取字节码文件对象呢?
获取Class对象的三种方式

public class ReflectDemo {

    /**
     * @param args
     * @throws ClassNotFoundException
     */
    public static void main(String[] args) throws ClassNotFoundException {
        getClassObject_3();
    }

        /*
     * 获取字节码对象的方式:
     * Object类中的getClass()方法的。
     * 想要用这种方式,必须要明确具体的类,并创建对象。
     * 麻烦 .
     *
     */
    public static void getClassObject_1(){

        Person p = new Person();
        Class clazz = p.getClass();

        Person p1 = new Person();
        Class clazz1 = p1.getClass();

        System.out.println(clazz==clazz1);
    }

    /*
     * 方式二:
     * 任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。
     * 相对简单,但是还是要明确用到类中的静态成员。
     * 还是不够扩展。
     *
     */
    public static void getClassObject_2() {

        Class clazz = Person.class;

        Class clazz1 = Person.class;
        System.out.println(clazz==clazz1);
    }

    /*
     * 方式三:
     * 只要通过给定的类的 字符串名称就可以获取该类,更为扩展。
     * 可是用Class类中的方法完成。
     * 该方法就是forName.
     * 这种方式只要有名称即可,更为方便,扩展性更强。
     */
    public static void getClassObject_3() throws ClassNotFoundException {

        String className = "cn.test.bean.Person";

        Class clazz = Class.forName(className);

        System.out.println(clazz);
    }

}

获取Class中的构造函数

public class ReflectDemo2 {

    /**
     * @param args
     * @throws Exception
     * @throws InstantiationException
     * @throws ClassNotFoundException
     */
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, Exception {
        createNewObject_2();
    }

    public static void createNewObject_2() throws Exception {

//      cn.test.bean.Person p = new cn.test.bean.Person("小强",39);

        /*
         * 当获取指定名称对应类中的所体现的对象时,
         * 而该对象初始化不使用空参数构造该怎么办呢?
         * 既然是通过指定的构造 函数进行对象的初始化,
         * 所以应该先获取到该构造函数。 通过字节码文件对象即可完成。
         * 该方法是:getConstructor(paramterTypes);
         *
         */
        String name = "cn.test.bean.Person";
        //找寻该名称类文件,并加载进内存,并产生Class对象。
        Class clazz = Class.forName(name);
        //获取到了指定的构造函数对  象。
        Constructor constructor = clazz.getConstructor(String.class,int.class);

        //通过该构造器对象的newInstance方法进行对象的初始化。
        Object obj = constructor.newInstance("小明",38);

    }

    public static void createNewObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException{

        //早期:new时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存,
//      并创建该字节码文件对象,并接着创建该字节文件的对应的Person对象.
//      cn.test.bean.Person p = new cn.test.bean.Person();

        //现在:
        String name = "cn.test.bean.Person";
        //找寻该名称类文件,并加载进内存,并产生Class对象。
        Class clazz = Class.forName(name);
        //如何产生该类的对象呢?
        Object obj  = clazz.newInstance();
    }
}

获取Class中的字段

    /*
     * 获取字节码文件中的字段。
     */
    public static void getFieldDemo() throws Exception {

        Class clazz = Class.forName("cn.test.bean.Person");
        Field field = null;//clazz.getField("age");//只能获取公有的,
        field = clazz.getDeclaredField("age");//只获取本类,但包含私有。
        //对私有字段的访问取消权限检查。暴力访问。
        field.setAccessible(true);
        Object obj = clazz.newInstance();
        field.set(obj, 89);

        Object o = field.get(obj);
        System.out.println(o);
//      cn.test.bean.Person p = new cn.test.bean.Person();
//      p.age = 30;

    }

获取Class中的方法

    public static void getMethodDemo_3() throws Exception {
        Class clazz = Class.forName("cn.test.bean.Person");
        Method method = clazz.getMethod("paramMethod", String.class,int.class);
        Object obj = clazz.newInstance();
        method.invoke(obj, "小强",89);

    }

    public static void getMethodDemo_2() throws Exception {

        Class clazz = Class.forName("cn.test.bean.Person");

        Method method = clazz.getMethod("show", null);//获取空参数一般方法。

//      Object obj = clazz.newInstance();
        Constructor constructor = clazz.getConstructor(String.class,int.class);
        Object obj = constructor.newInstance("小明",37);

        method.invoke(obj, null);

    }

    /*
     * 获取指定Class中的所有公共函数。
     */
    public static void getMethodDemo() throws Exception {

        Class clazz = Class.forName("cn.test.bean.Person");

        Method[] methods  = clazz.getMethods();//获取的都是公有的方法。
        methods = clazz.getDeclaredMethods();//只获取本类中所有方法,包含私有。
        for(Method method : methods){
            System.out.println(method);
        }
    }

如何获得泛型类的真实类型
通过Class类上的 getGenericSuperclass() 或者 getGenericInterfaces() 获取父类或者接口的类型,然后通过ParameterizedType.getActualTypeArguments()

public class RealType<T>{

    private Class<T> clazz;
    // 使用反射技术得到T的真实类型
    public Class getRealType(){
        // 获取当前new的对象的泛型的父类类型
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        // 获取第一个类型参数的真实类型
        this.clazz = (Class<T>) pt.getActualTypeArguments()[0];
        return clazz;
    }

}

原文地址:https://www.cnblogs.com/sixrain/p/10960830.html

时间: 2024-10-10 10:33:08

代理、反射、注解、hook的相关文章

java反射注解的用途

很多人都知道java的反射机制和注解技术.反射(Reflection)就是加载类,并解剖出类的各个组成部分:而Annotation 其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行.那么两者结合能够产生出什么? 首先来看下面一个程序 回想我们以前产生Connection的方法,我是都是在配置文件中配置好driver,url,username,password,而现在我们利用反射加注解可以利用方法上注解

反射注解的程序举例

一要定义注解(第三方提供) 二要使用注解(我们就使用) 三要反射注解(框架读取它) 注意: 反射注解的要求是注解的保密策略必须是RUNTIME 反射注解需要从作用目标上返回 1.类上的注解需要使用Class来获取 2.方法上的注解需要使用Method来获取 3.构造器上的注解需要使用Construcator来获取 4.成员上的注解需要使用Field来获取 package 反射注解; import java.lang.annotation.Retention; import java.lang.a

[记录] java反射+注解

框架开发之Java注解的妙用 JAVA反射与注解 Java基础 :反射.注解.代理.线程池.依赖的学习和理解 Java反射与注解 原文地址:https://www.cnblogs.com/zad27/p/12121589.html

java动态代理详解,并用动态代理和注解实现日志记录功能

动态代理的概念 动态代理是程序在运行过程中自动创建一个代理对象来代替被代理的对象去执行相应的操作,例如, 我们有一个已经投入运行的项目中有一个用户DAO类UserDao用来对User对象进行数据库的增删改查操作,但是有一天,要求在对用户的增删改查操作时记录相应的日志,这是怎么办呢?难道我们去直接修改UserDao的源代码,然后在UserDao的每个方法中加入日志记录功能,这显然是不合理的,它违背了java的OCP原则,即对修改关闭对扩张开放.比如改现有的代码如下: 接口类 public inte

java 反射注解信息

Table,用类表示数据库的表 @Target(value= {ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Table{ String value(); } 类中的属性,每个属性表示一个字段 @Target(value= ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interf

插件化知识详细分解及原理 之代理,hook,反射

上一篇我们说了Binder机制,通过aidl的demo和系统源码的对比进行了运行过程的分析,这一篇我们说代理模式及反射,之前说过了,只是为了梳理插件化需要了解的知识点,所以不会特别深的去讲解. 代理模式: 也叫做委托模式,分为静态代理和动态代理.代理模式也是平时比较常用的设计模式之一,代理模式有代码简洁,高扩展性的特性.主要目的就是为访问者提供一个代理,以达到限制某个对象的访问,也就是说想访问一个对象,其实我给你的是一个代理,不让你直接使用我.估计不理解的人会问为什么使用代理模式,他限制了对象的

框架学习前基础加强 泛型,注解,反射(泛型&注解)应用案例,IOC,Servlet3.0,动态代理,类加载器

泛型 1. 泛型类 :具有一个或多个类型变量的类,称之为泛型类! class A<T> { } 2. 在创建泛型类实例时,需要为其类型变量赋值 A<String> a = new A<String>(); * 如果创建实例时,不给类型变量赋值,那么会有一个警告! 3. 泛型方法 :具有一个或多个类型变量的方法,称之为泛型方法! class A<T> { public T fun(T t1) {} } fun()方法不是泛型方法!它是泛型类中的一个方法! pu

理解Android中的注解与反射

前言 最近一段时间在研究EventBus和Retrofit 的过程中,都遇到了注解这个概念.由于在学习Java的时候对这方面没有深入了解过,所以看起相关的代码来,总会有点不知其所以然,这里就注解和反射的使用做一下总结. 这里我们先从反射说起,了解了反射的意义及用法后,我们在看看注解的使用,最后叙述一下在Android开发中是怎样结合使用注解与反射. 反射 Java反射(Reflection)定义 Java反射机制是指在运行状态中 对于任意一个类,都能知道这个类的所有属性和方法: 对于任何一个对象

day19_java基础加强_动态代理+注解+类加载器

一.动态代理 1.1.代理模式 ? ? 什么是代理模式及其作用? ? ? ? ? Proxy Pattern(即:代理模式),23种常用的面向对象软件的设计模式之一.? ? ? ? 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.? ? ? ? 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用.? ? 优点:? ? ? ? (1) 职责清晰,真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件

java注解和反射的结合使用

首先反射注解,那么保留策略必须是Runtime,也就是@Retention(RetentionPolicy.RUNTIME) ①定义一个注解类 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface MyAnnotation { int value(); } ②在定义一个类使用注解类 public class MyBean { @MyAnnotation(20) private int