Java-反射和代理

135 阅读3分钟
本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

1.反射概念

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

根据类的全限定名可以从JVM的方法区(类加载后存放在方法区中)获取类所有的信息(名称, 常量,属性,静态代码块,构造器,方法,接口,权限)

一个类的构成:名称, 常量,属性,静态代码块,构造器,方法,接口,权限,所以反射能够操作的东西也无非是这些内容

2.使用反射的方式

  1. Class clazz = object.getClass //通过实例的getClass()方法获取类信息
  2. Class clazz = Object.class    //直接获取
  3. Class clazz = Class.forName("com.mysql.jdbc.Driver");//类加载器获取

这些获取方式本质调用public final native Class<?> getClass() 直接调用本地方法去拿相关信息

3.Java反射真的慢?

由于反射获取属性,方法,类加载等操作都是调用native接口和底层交互,所以导致了操作慢

但是其实JDK里面对反射做了优化

  1. 优化一引入ReflectionData类,缓存类,对于读取Fields,Methods之类的操作方法返回的数据做了一次缓存,除了第一次读取,其他都是内存数据比较快
private Field[] privateGetPublicFields(Set<Class<?>> traversedInterfaces) {
        checkInitted();
        Field[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            //有缓存则返回
            res = rd.publicFields;
            if (res != null) return res;
        }

        // No cached value available; compute value recursively.
        // Traverse in correct order for getField().
        List<Field> fields = new ArrayList<>();
        if (traversedInterfaces == null) {
            traversedInterfaces = new HashSet<>();
        }

        // Local fields
        //没有则调本地方法获取
        Field[] tmp = privateGetDeclaredFields(true);
        addAll(fields, tmp);

        // Direct superinterfaces, recursively
        for (Class<?> c : getInterfaces()) {
            if (!traversedInterfaces.contains(c)) {
                traversedInterfaces.add(c);
                addAll(fields, c.privateGetPublicFields(traversedInterfaces));
            }
        }

        // Direct superclass, recursively
        if (!isInterface()) {
            Class<?> c = getSuperclass();
            if (c != null) {
                addAll(fields, c.privateGetPublicFields(traversedInterfaces));
            }
        }

        res = new Field[fields.size()];
        fields.toArray(res);
        //加入缓存
        if (rd != null) {
            rd.publicFields = res;
        }
        return res;
    }
  1. 优化二 对于Field,Method,Constructor的访问加入了代理类,在调用invoke(),或new Instance()等方法时当次数>15次时,创建代理类实现
     public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
            MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
            this.parent.setDelegate(var3);
        }

        return invoke0(this.method, var1, var2);
    }
    

4.动态代理概念

  • 代理:为其他对象提供一种代理以控制对这个对象的访问

  • 动态代理:程序运行过程中动态创建代理类

5.动态代理实现的两种方式

  1. Java反射方式:实现java.lang.reflect.InvocationHandler接口,且实现invoke()方法

  2. cglib方式:实现CGLIB提供的org.springframework.cglib.proxy.MethodInterceptor,且实现intercept()

代码案例:鹿哈是明星,找他工作先要找经纪人

    public class LuHan implements Star {

    @Override
    public void work() {
        System.out.println("鹿晗工作");
    }
}

JAVA方式:

    //Java反射使用实现InvocationHandler
    public class ManagerProxy implements InvocationHandler {

    private Star star;
    //经纪人不一定只管一个人
    public ManagerProxy(Star star) {
        this.star = star;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("经过经纪人");
        Object o = method.invoke(star, args);
        return o;
    }

    public static void main(String[] args) {
        Star luhan = new LuHan();
        ManagerProxy proxy = new ManagerProxy(luhan);
        Star star = (Star) Proxy.newProxyInstance(
                luhan.getClass().getClassLoader(),
                luhan.getClass().getInterfaces(),
                proxy);
        star.work();
    }
}

CGLIB方式:

    public class ManagerCglibProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("经过经纪人");
        Object o = methodProxy.invokeSuper(object, args);
        return o;
    }

    public static void main(String[] args) {
        ManagerCglibProxy cglibProxy = new ManagerCglibProxy();

        //cglib方式简洁很多,但是Enhancer不能代理final类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(LuHan.class);
        enhancer.setCallback(cglibProxy);
        Star star = (Star) enhancer.create();
        star.work();
    }

}
两次输出:
经过经纪人
鹿晗工作

cglib效率会比java反射方式高,但是cglib方式不能代理finl类