• java反射和动态代理


    反射

    反射的官方定义是这样的:

        在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制。

    一般创建类的方式:

            Car car = new Car();
            car.setName("宝马");

    而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用:

            Class clz = Class.forName("Car");
            Method method = clz.getMethod("setName", String.class);
            Constructor constructor = clz.getConstructor();
            Object object = constructor.newInstance();
            method.invoke(object, "宝马");

    jdk提供了三种方式获取一个对象的Class,就Car car来说

    1.car.getClass(),这个是Object类里面的方法

    2.Car.Class属性,任何的数据类型,基本数据类型或者抽象数据类型,都可以通过这种方式获取类

    3.Class.forName(""),Class类提供了这样一个方法,让我们通过类名来获取到对象类

    这三种方法用的最多的就是第三种,那么获取到类之后,Class类提供了很多获取类属性,方法,构造方法的api。

    通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。

    第一种:通过 Class 对象的 newInstance() 方法。

            Class clz = Car.class;
            Car car = (Car)clz.newInstance();

    第二种:通过 Constructor 对象的 newInstance() 方法

            Class clz = Car.class;
            Constructor constructor = clz.getConstructor();
            Car car = (Car)constructor.newInstance();

    通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

            Class clz = Car.class;
            Constructor constructor = clz.getConstructor(String.class);
            Car car = (Car) constructor.newInstance("宝马");

    通过反射获取类属性、方法、构造器:

    getDeclaredMethods() 获取所有的方法

    getReturnType() 获得方法的放回类型

    getParameterTypes() 获得方法的传入参数类型

    getDeclaredMethod("方法名",参数类型.class,……) 获得特定的方法

    getDeclaredConstructors() 获取所有的构造方法

    getDeclaredConstructor(参数类型.class,……) 获取特定的构造方法

    getSuperclass() 获取某类的父类

    getInterfaces() 获取某类实现的接口

    getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。 
    getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。

    同样类似的还有getConstructors()和getDeclaredConstructors()、getMethods()和getDeclaredMethods(),这两者分别表示获取某个类的方法、构造函数。

    一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。

    例子:

    Car.java
    public class Car {
        private String name;
    
        public Car(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    如果在Car.java内部创建mian方法测试的时候,是不会报异常的。
    TestReflect.java
    import java.lang.reflect.Field;
    
    public class TestReflect {
        public static void main(String[] args) throws Exception {
            Car car = new Car("宝马");
            Class clz = Car.class;
            Field name = clz.getDeclaredField("name");
    //        name.setAccessible(true);
            System.out.println(name.get(car));//获取当前对象中当前Field的value
        }
    }

    测试报异常:

    Exception in thread "main" java.lang.IllegalAccessException: Class TestReflect can not access a member of class Car with modifiers "private"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
        at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
        at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
        at java.lang.reflect.Field.get(Field.java:390)
        at TestReflect.main(TestReflect.java:9)

    取消注释后正常

     动态代理

    代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。而代理模式又分为静态代理和动态代理。

    静态代理通俗点将就是自己手写一个代理类,而动态代理则不用我们手写,而是依赖于java反射机制。

    Subject.java

    public interface Subject {
        void doSomething();
    }

    RealSubject.java

    public class RealSubject implements Subject {
        @Override
        public void doSomething() {
            System.out.println("doSomething");
        }
    }

    ProxyObject.java

    public class ProxyObject implements Subject {
        @Override
        public void doSomething() {
            System.out.println("proxy Subject");
            new RealSubject().doSomething();
        }
    
        public static void main(String[] args) {
            new ProxyObject().doSomething();
        }
    }

    ProxyObject就是RealSubject的静态代理类,代替了RealSubject完成doSomething的工作。

    Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler

    ProxyHandler.java

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyHandler implements InvocationHandler {
        private Object realObject = null;
    
        public ProxyHandler(Object proxied) {
            this.realObject = proxied;
        }
    
        public Subject getProxy() {
            return (Subject) Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
                    realObject.getClass().getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before invoke");
            return method.invoke(realObject, args);
        }
    
        public static void main(String[] args) {
            ProxyHandler proxyHandler = new ProxyHandler(new RealSubject());
            Subject subject = proxyHandler.getProxy();
            subject.doSomething();
        }
    }

    动态代理,必须先实现这个InvocationHandler接口,然后通过Proxy生成一个代理对象。

    方法里面有一个Proxy类,这个Proxy类提供了很多方法,这里我们用的是newProxyInstance方法,它有三个参数,第一个是被代理类的类构造器,第二个指的是被代理类的接口,也就是Subject接口,第三个是实现这个代理的类,这里就是本类。具体的来说,这个方法执行了下面三步:

    1.生成一个实现了参数interfaces里所有接口且继承了Proxy的代理类的字节码,然后用参数里的classLoader加载这个代理类。

    2.使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,将我们自定义的InvocationHandler的子类传入。

    3.返回这个代理类实例,因为我们构造的代理类实现了interfaces(也就是我们程序中传入的realObject.getClass().getInterfaces())里的所有接口,因此返回的代理类可以强转成Subject类型来调用接口中定义的方法。

    可以添加打印生成的代理对象的相关信息已方便分析:

    新的ProxyHandler.java

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyHandler implements InvocationHandler {
        private Object realObject = null;
    
        public ProxyHandler(Object proxied) {
            this.realObject = proxied;
        }
    
        public Subject getProxy() {
            return (Subject) Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
                    realObject.getClass().getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before invoke");
            return method.invoke(realObject, args);
        }
    
        public static void main(String[] args) {
            ProxyHandler proxyHandler = new ProxyHandler(new RealSubject());
            Subject subject = proxyHandler.getProxy();
    
            //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口
            System.out.println(subject instanceof Proxy);
    
            //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口
            System.out.println("subject的Class类是:" + subject.getClass().toString());
            System.out.print("subject中的属性有:");
            Field[] field = subject.getClass().getDeclaredFields();
            for (Field f : field) {
                System.out.print(f.getName() + ", ");
            }
            System.out.print("
    " + "subject中的方法有:");
            Method[] method = subject.getClass().getDeclaredMethods();
            for (Method m : method) {
                System.out.print(m.getName() + ", ");
            }
    
            System.out.print("
    " + "subject的父类是:" + subject.getClass().getSuperclass());
            System.out.print("
    " + "subject实现的接口是:");
            Class<?>[] interfaces = subject.getClass().getInterfaces();
            for (Class<?> i : interfaces) {
                System.out.print(i.getName() + ", ");
            }
    
            System.out.println("
    
    " + "运行结果为:");
            subject.doSomething();
        }
    }

    运行得到结果:

    true
    subject的Class类是:class com.sun.proxy.$Proxy0
    subject中的属性有:m1, m3, m2, m0, 
    subject中的方法有:equals, toString, hashCode, doSomething, 
    subject的父类是:class java.lang.reflect.Proxy
    subject实现的接口是:Subject, 
    
    运行结果为:
    before invoke
    doSomething

    分析Proxy类源代码

    newProxyInstance方法功能:

    (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类. 
    (2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下

        protected Proxy(InvocationHandler h) {
            Objects.requireNonNull(h);
            this.h = h;
        }

    为了分析$Proxy0,可以生成 Proxy0的class文件,然后反编译出代码。

    生成class文件的函数:

        public static void getProxy0Code() {
            byte[] classFile = ProxyGenerator.generateProxyClass("Proxy0", Subject.class.getInterfaces());
            try {
                File file = new File("Proxy0.class");
                FileOutputStream fos = new FileOutputStream(file);
                fos.write(classFile);
                fos.flush();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
            }
        }

    通过jd-gui反编译出源代码

    来看一下这个继承了Proxy的$Proxy0的源代码: 

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class Proxy0 extends Proxy implements Subject {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        public Proxy0(InvocationHandler paramInvocationHandler) {
            super(paramInvocationHandler);
        }
    
        public final boolean equals(Object paramObject) {
            try {
                return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
    
        public final void doSomething() {
            try {
                this.h.invoke(this, m3, null);
                return;
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
    
        public final String toString() {
            try {
                return (String) this.h.invoke(this, m2, null);
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
    
        public final int hashCode() {
            try {
                return ((Integer) this.h.invoke(this, m0, null)).intValue();
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
                m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]);
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
                return;
            } catch (NoSuchMethodException localNoSuchMethodException) {
                throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
            } catch (ClassNotFoundException localClassNotFoundException) {
                throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
            }
        }
    }

    接着把得到的$Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.doSomething()方法时,就调用 了$Proxy0类中的doSomething()方法,进而调用父类Proxy中的h的invoke()方法.即 InvocationHandler.invoke()。 

     newProxyInstance生成代理对象的详细分析过程参考

    如果不是继承接口的类可以用cglib来实现代理,cglib提供更加丰富的功能,比如可以提供多个callback,在根据设置的filter选择合适的代理实现。

    当然首先要引入cglib包

            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>2.2.2</version>
            </dependency>

    定义正式的业务类

    public class RealSubject {
        public void doSomething() {
            System.out.println("doSomething");
        }
    }

    定义代理类

    public class ProxyHandler implements MethodInterceptor {
    
        public Object getInstance(Class<?> target) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target);
            // 设置回调方法
            enhancer.setCallback(this);
            // 创建代理对象
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
            System.out.println("Before Method Invoke");
            proxy.invokeSuper(object, objects);
            System.out.println("After Method Invoke");
            return object;
        }
    
        public static void main(String[] args) {
            ProxyHandler proxyHandler = new ProxyHandler();
            RealSubject instance = (RealSubject) proxyHandler.getInstance(RealSubject.class);
            instance.doSomething();
        }
    }

    查看生成的代理对象

    如果想查看生成代理对象的源码,可以在创建对象前添加

          System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://class");

    这样就会在指定的目录生成对应包的类

    运行结果:

    当有断点的时候会打印多次,而且次数还不一样,可能是IDA有些什么处理吧。

  • 相关阅读:
    7.内网渗透之windows认证机制
    10.Python之Ansible自动化运维常用模块
    9.python 系统批量运维管理器之Fabric模块
    8.python 系统批量运维管理器之pexpect模块
    7.python实现高效端口扫描器之nmap模块
    6.python探测Web服务质量方法之pycurl模块
    5.python之pip安装模块失败
    4.python 系统批量运维管理器之paramiko模块
    3.python 发送邮件之smtplib模块
    微慕WordPress小程序增强版
  • 原文地址:https://www.cnblogs.com/grasp/p/10877000.html
Copyright © 2020-2023  润新知