Java—反射机制实战及动态代理总结

关注WX:CodingTechWork,一起学习进步。

引言

  反射在Java技术应用中是老生常谈的事了,我们每次都是知道个皮毛,这个反射可以动态获取类的信息,比如类的属性和方法,但是在平时疯狂写CURD业务代码时,却很少关注或接触使用到反射机制。当然,这并不代表反射没落,反射无用,其实,在高级开发中,还是大量使用了反射机制的,比如我们常说的动态代理、注解等场景都是需要依靠反射来实现。

反射介绍

概念

  反射是在运行状态中,对于任意一个类,我们都能够通过反射机制获取到这个类的所有属性方法,对于任意一个对象,我们都可以通过反射机制调用它的任意一个属性和方法,包括private类型。这种能够动态获取类的信息和动态调用对象的属性和方法的功能就是JAVA中的反射机制。

作用

  1. 反射机制可以操作字节码文件,可以读取和修改字节码文件。
  2. 反射机制可以操作代码片段,可以操作class文件。

反射的类

说明
java.lang.Class 该类表示JVM运行时类或接口信息,每个class都有对应的Class对象,代表整个类
java.lang.reflect.Field 表示字节码中的属性字节码,代表类中的成员变量
java.lang.reflect.Method 表示字节码中的方法字节码,代表类中的方法
java.lang.reflect.Constructor 表示字节码中的构造方法字节码,代表类中的构造方法

获取Class的4种方法

  1. 通过类.class获取
Class<?> pClass = PersonBean.class;
  1. 通过Class.forName()方法传入类的全路径获取:
Class<?> pClass = Class.forName("com.xxx.PersonBean");
  1. 通过对象实例getClass()方法获取
PersonBean p = new PersonBean();
Class<?> pClass = p.getClass();
  1. 通过类加载器loadClass()方法传入类的全路径获取:
Class<?> pClass = ClassLoader.getSystemClassLoader().loadClass("com.xxx.PersonBean");

反射实例化对象

//反射获取Class
Class<?> c = Class.forName("com.xxx.PersonBean");
//实例化对象
Object obj = c.newInstance();

反射访问私有属性和方法

需要通过setAccessible(boolean flag)方法将类的封装打破。

privateMethod.setAccessible(true);
privateMethod.invoke(obj);

Class类的方法列表

方法名 说明
public T newInstance() 创建对象实例
public String getName() 返回全路径类名
public String getSimpleName() 返回类名,不带路径
public Field[] getFields() 返回类中public修饰的属性
public Field[] getDeclaredFields() 返回类中所有属性
public Field getDeclaredField(String name) 根据属性名name获取指定的属性
public native int getModifiers() native方法,获取属性的修饰符列表,一般配合Modifier类的toString(int xxx)方法使用
public Method[] getDeclaredMethods() 返回类中所有实例方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes) 根据方法名name和方法形参获取指定方法
public Constructor<?>[] getDeclaredConstructors() 返回类中所有构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 根据方法形参获取指定构造方法
public native Class<? super T> getSuperclass() 返回调用类的父类
public Class<?>[] getInterfaces() 返回调用类实现的接口集合

Field类的方法列表

方法名 说明
public String getName() 返回属性名
public int getModifiers() 获取属性的修饰符列表,一般配合Modifier类的toString(int xxx)方法使用
public Class<?> getType() 获取属性类型,一般配合Class类的getSimpleName()方法使用
public void set(Object obj, Object value) 设置属性值
public Object get(Object obj) 读取属性值

Method类的方法列表

方法名 说明
public String getName() 获取方法名
public int getModifiers() 获取方法的修饰符列表,一般配合Modifier类的toString(int xxx)方法使用
public Class<?> getReturnType() 获取方法类型,一般配合Class类的getSimpleName()方法使用
public Class<?>[] getParameterTypes() 获取方法的修饰符列表,一般配合Class类的getSimpleName()方法使用
public Object invoke(Object obj, Object… args) 调用方法

反射实战

目标类(PersonBean)

package com.test.selfcoding.bean;

/**
 * @ClassNAME PersonBean
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class PersonBean {

    private String name;

    private int age;

    public PersonBean() {

    }

    public PersonBean(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void printPublicPersonInfo(String name) {
        System.out.println("person name is: " + name);
    }

    private void printPrivatePersonInfo(String name, int age) {
        System.out.println("person name is: " + name + ", age is: " + age);
    }

    private void printPersonName() {
        System.out.println("origin name is: " + name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

调用类(ReflectTest)

package com.test.selfcoding.service;

import com.test.selfcoding.bean.PersonBean;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @ClassNAME ReflectionTest
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class ReflectionTest {


    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获取类Class对象并创建对应实例
        Class<?> targetClass = Class.forName("com.test.selfcoding.bean.PersonBean");
        Object obj = targetClass.newInstance();

        //获取所有域
        System.out.println("========fields=========");
        Field[] fields = targetClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        //获取所有构造函数
        System.out.println("========constructors=========");
        Constructor[] constructors = targetClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        //获取所有方法
        System.out.println("========method=========");
        Method[] methods = targetClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }

        //获取指定的public方法并调用
        System.out.println("获取指定的public方法并调用");
        Method publicMethod = targetClass.getDeclaredMethod("printPublicPersonInfo", String.class);
        publicMethod.invoke(obj, "xiaoming");

        //获取指定的private方法并调用
        System.out.println("获取指定的private方法并调用");
        Method privateMethod = targetClass.getDeclaredMethod("printPrivatePersonInfo", String.class, int.class);
        privateMethod.setAccessible(true);
        privateMethod.invoke(obj, "xiaohong", 20);

        //获取指定的参数
        System.out.println("获取指定的参数");
        Field nameField = targetClass.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(obj, "wangyuan");
        Method printNameMethod = targetClass.getDeclaredMethod("printPersonName");
        printNameMethod.setAccessible(true);
        printNameMethod.invoke(obj);

    }
}


执行结果

========fields=========
name
age
========constructors=========
public com.test.selfcoding.bean.PersonBean()
public com.test.selfcoding.bean.PersonBean(java.lang.String,int)
========method=========
printPublicPersonInfo
printPrivatePersonInfo
printPersonName
getAge
setAge
getName
setName
获取指定的public方法并调用
person name is: xiaoming
获取指定的private方法并调用
person name is: xiaohong, age is: 20
获取指定的参数
origin name is: wangyuan

代理

  什么是代理?代理proxy,其实就是为目标对象提供一个代理对象来控制访问方式,且能够在目标对象实现基础上提供额外的扩展功能。比如说,华为公司通过手机代理商去售卖手机,衣服原工厂通过各个代理商去售卖衣服,房地产通过中介公司售卖房子,等等。

接口和被代理类

接口

定义一个接口:手机厂商

package com.test.selfcoding.proxy.staticproxy;

public interface Phone {
    void sellPhone();
}

被代理类

被代理类1:华为手机厂商

package com.test.selfcoding.proxy.staticproxy;

/**
 * @ClassNAME HuaweiPhone
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class HuaweiPhone implements Phone {

    @Override
    public void sellPhone() {
        System.out.println("sell huawei phone!");
    }
}

被代理类2:小米手机厂商

package com.test.selfcoding.proxy.staticproxy;

/**
 * @ClassNAME XiaoMiPhone
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class XiaoMiPhone implements Phone{

    @Override
    public void sellPhone() {
        System.out.println("sell xiaomi phone");
    }
}

静态代理

  在学习动态代理之前,我们先学习一下,静态代理我们一般是怎样实现的。

静态代理类

代理类1:华为手机代理商

package com.test.selfcoding.proxy.staticproxy;

/**
 * @ClassNAME HuaweiPhoneProxy
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class HuaweiPhoneProxy implements Phone {

    private Phone phone;

    public HuaweiPhoneProxy() {
        this.phone = new HuaweiPhone();
    }

    @Override
    public void sellPhone() {
        //代理商提供其他服务
        System.out.println("华为手机售前");
        //代理接口的操作
        phone.sellPhone();
        System.out.println("华为手机售后");
    }
}

被代理类2:小米手机代理商

package com.test.selfcoding.proxy.staticproxy;

/**
 * @ClassNAME XiaoMiPhoneProxy
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class XiaoMiPhoneProxy implements Phone{

    private Phone phone;

    public XiaoMiPhoneProxy () {
        this.phone = new XiaoMiPhone();
    }


    @Override
    public void sellPhone() {
        //代理商提供其他服务
        System.out.println("小米手机售前");
        //代理接口的操作
        phone.sellPhone();
        System.out.println("小米手机售后");
    }
}

静态代理调用

package com.test.selfcoding.proxy.staticproxy;

/**
 * @ClassNAME StaticProxyTest
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class StaticProxyTest {
    public static void main(String[] args) {
        //代理华为
        Phone huaweiProxy = new HuaweiPhoneProxy();
        huaweiProxy.sellPhone();

        //代理小米
        Phone xiaomiProxy = new XiaoMiPhoneProxy();
        xiaomiProxy.sellPhone();
    }
}

静态代理结果

华为手机售前
sell huawei phone!
华为手机售后
小米手机售前
sell xiaomi phone
小米手机售后

被代理类被传递给代理类后,代理类在执行具体方法时通过所持有的被代理类完成调用。我们可以看到每次代理只能为一个类服务,需要多个代理类来实现被代理类的访问。

动态代理

  看完静态代理,我们发现其缺点是随着手机厂商增多,代理类需要不断地增多,于是我们就用到了动态代理。

动态代理处理器

package com.test.selfcoding.proxy.staticproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @ClassNAME PhoneProxyHandler
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class PhoneProxyHandler implements InvocationHandler {
    private Object object;
    private String name;
    public PhoneProxyHandler (Object object, String name) {
        this.object = object;
        this.name = name;
    }

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

        System.out.println("Before invoke " + method.getName());
        System.out.println(name + "手机售前");
        method.invoke(object, args);
        System.out.println(name + "手机售后");
        System.out.println("After invoke " + method.getName());
        return null;
    }
}

动态代理调用

package com.test.selfcoding.proxy.staticproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @ClassNAME DynamicProxyTest
 * @Description TODO
 * @Author Andya
 * @Version 1.0
 */
public class DynamicProxyTest {

    public static void main(String[] args) {
        //动态代理华为
        Phone huaweiPhone = new HuaweiPhone();
        InvocationHandler huaweiHandler = new PhoneProxyHandler(huaweiPhone,"华为");
        //创建华为手机的动态代理类
        Phone huaweiProxy = (Phone) Proxy.newProxyInstance(huaweiPhone.getClass().getClassLoader(), huaweiPhone.getClass().getInterfaces(), huaweiHandler);
        huaweiProxy.sellPhone();

        //动态代理小米
        Phone xiaomiPhone = new XiaoMiPhone();
        InvocationHandler xiaomiHandler = new PhoneProxyHandler(xiaomiPhone,"小米");
        //创建小米手机的动态代理类
        Phone xiaomiProxy = (Phone) Proxy.newProxyInstance(huaweiPhone.getClass().getClassLoader(), huaweiPhone.getClass().getInterfaces(), xiaomiHandler);
        xiaomiProxy.sellPhone();
        
    }
}

动态代理调用结果

Before invoke sellPhone
华为手机售前
sell huawei phone!
华为手机售后
After invoke sellPhone
Before invoke sellPhone
小米手机售前
sell xiaomi phone
小米手机售后
After invoke sellPhone

通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例,针对不同的被代理类进行控制。

动态代理newProxyInstance()方法的源码

 @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //判空
        Objects.requireNonNull(h);
        
        //获取入参一组接口
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        //此处即为代理类对象的生成
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            //通过constructorParams获取代理类的构造函数
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //若私有,通过设置setAccessible打破封装
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //创建代理实例
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

动态代理getProxyClass0()源码


    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容