java反射和代理模式

30 篇文章 0 订阅

java反射和代理

1 什么是反射?

通常创建一个对象,都是以new的方式。

A a=new A();

反射则是一开始并不知道自己需要创建的对象,自然不能用new的方式。而是运行时才知道创建的类是什么,并且可以在运行时获取类的成员变量和构造方法等。

为什么这么做?

假如创建的类可能频繁改动或者,本身不知道需要具体创建哪个类时,就可以使用反射,体现了java的灵活性。

Person person=new Teacher();

下次需要改成Student,就需要修改源码,并且重新编译。假如这是已经交付给客户的产品,那么就更加麻烦。如果利用反射就不需要那么麻烦了,只需要修改传入的name,name代表类的全限定名(包名+类名)。name的修改也无需改动代码,只需要增加一个配置文件,比如xml的。到时候只需要手动修改配置文件就可以快速更改。

此外还有一些方法是无法直接调用的,比如@hide函数,也可以通过反射的方式调用。

 public Person createPerson(String name) {
        Person person = null;
        try {
            person = (Person) Class.forName(name).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return person;
 }

反射可以获取类的实例、类的成员变量和方法、并且可以调用它们。

2 Class类

Class类是用来描述类的一个类,听起来有些绕口令。它封装了类的信息,比如成员变量、方法等。

1.获取方式(3种)

(1)obj.getClass()

(2)Object.class

(3)Class.forName(“类的全限定名”)

2.常用方法

(1)forName() 创建一个类

(2)getName() 返回类名

(3)getMethods 返回所有public方法和继承的方法

(4)getDeclaredMethods 返回所有声明的方法,包括私有方法

(5)getMethod(方法名,方法参数) 获取指定方法名和参数的方法。

注意这里的方法参数如果是基本类型,则直接传基本类型.class而不是包装类型.class。如int.class而不是Integer.class

通过**method.invoke(对象实例,参数)**调用该方法。

假如调用私有方法,还需要setAccessible设置为true

(6)getField等和获取变量相关的方法,同上面的(3)(4)(5)

3.缺点

因为是在运行时才创建对象,因此运行速度较慢,开销大。

3 代理模式

1.定义。

给目标提供代理对象,并通过代理对象控制对目标对象的引用。假定顾客想吃汉堡,但是不想直接跑去kfc买,就可以借助外卖员替自己去买汉堡。这里外卖员就是代理对象,kfc是真实对象,顾客是访问者。

好处:(1)避免调用者直接访问对象(有时候对象访问不到),将调用和实现解耦。(2)不违反开闭原则,增强目标功能。

抽象角色:真实对象和代理对象的公共接口

真实对象:代理对象所代理的对象,最终被引用的对象

代理对象:代理真实对象,从而操作真实对象

2.代理模式的分类

代理模式又可以分成静态代理和动态代理。

静态代理 一对一,违反开闭原理,拓展性维护性差。

动态代理 灵活性高,效率低。大型的项目中,一般

3.静态代理

抽象角色 真实角色和代理角色共同实现的方法。

public interface Shop {
    public void saleFood();
}

真实角色

public class KFC implements Shop {
    @Override
    public void saleFood() {
        System.out.println("kfc汉堡");
    }
}

代理角色

public class Delivery implements Shop {
    Shop shop;
    public Delivery(Shop shop){
        this.shop=shop;
    }
    @Override
    public void saleFood() {
        System.out.println("外卖员替顾客买食物");
        shop.saleFood();
        System.out.println("送货上门")//拓展增强saleFoods方法
    }
}

使用

public class main {
    public static void main(String[] args) {
        KFC kfc=new KFC();
        Delivery delivery=new Delivery(kfc);
        delivery.saleFood();
    }
}

//输出
外卖员替顾客买食物
kfc汉堡
送货上门

但是假如顾客除了想买食物,还想买别的物品,比如衣服等,那么saleFood就不满足要求。此时需要创建新的代理类,实现新的接口。每有一种新的需求,就需要重新再写一次,因此代码量较大。

优点:性能好。

缺点:需要实现接口,如果有多个代理需要创建大量代码,拓展性较差。一般是一对一。

4 动态代理

为了满足顾客多种多样的需求,这个时候会借助美团这个平台点各种各样的外卖。顾客通过平台下单,美团会自动派发(创建)订单给骑手(代理类)。

public class MeiTuan implements InvocationHandler {
    Object proxy;
    public void setProxy(Object proxy){
        this.proxy=proxy;
    }

    public Object getProxyInstance() {
    		//	创建代理类
        return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), proxy.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用被代理类的方法
        Object result=method.invoke(this.proxy,args);
        //增强方法
        System.out.println("送货上门");
        return result;
    }
}

假设除了买吃的还想买衣服。就不需要再创建一个骑手类代理。只需要通过一个美团平台就可以实现创建。

public class AnTa implements ClothesShop {
    @Override
    public void saleClothes() {
        System.out.println("卖安踏衣服");
    }
}
MeiTuan meiTuan=new MeiTuan();
meiTuan.setProxy(new KFC());
Shop shop= (Shop) meiTuan.getProxyInstance();
shop.saleFood();
meiTuan.setProxy(new AnTa());
ClothesShop clothesShop = (ClothesShop) meiTuan.getProxyInstance();
clothesShop.saleClothes();

其实在我们使用的框架中,也能常常看见动态代理的使用。比如我们常用的retrofit。

retrofit.create(cls)

 //点开这个方法的源码,可以看到return  Proxy.newProxyInstance这个方法就可以知道这里是借助动态代理的方式
 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

5 源码如何创建代理类

只看关键代码 ,Android在这里修改了一些jdk的代码,主要是注释了一些安全检查。

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
		//省略
 		//获取代理类的class对象
    Class<?> cl = getProxyClass0(loader, intfs);
    try {
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        //判断构造方法的访问权限符
			  if (!Modifier.isPublic(cl.getModifiers())) {
			  				//	这里Android对jdk略有修改,省去了一些步骤,只要是构造方法,将访问限制设置为true。
			  				//而java代码似乎会通过doPrivileged这个方法,如果是私有方法,会抛出uncheck的异常,然后走run方法。
                // BEGIN Android-changed: Excluded AccessController.doPrivileged call.
                /*
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
                */
                cons.setAccessible(true);
                // END Android-removed: Excluded AccessController.doPrivileged call.
            }
        //创建这个proxy的实例
        return cons.newInstance(new Object[]{h});
    } 
    //省略
}

再看getProxyClass0()这个方法是如何创建class对象的

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
  	//	接口最大65535个
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

  
 		// 这个方法返回class对象。如果传入的类构造器已经定义了class对象,那么直接从缓存中获取,否则则通过ProxyClassFactory创建。
    // 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);
}

在看proxyClassCache.get这个方法。这里主要是用了二级缓存,所谓的二级缓存,就是以Map<key,Map<key,value>>的形式,也就是把map当作另一个map的value存放。

public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);

    expungeStaleEntries();

		// 将classload转化为一级缓存的key
    Object cacheKey = CacheKey.valueOf(key, refQueue);
    // 根据一级缓存的key 获取二级缓存,也就是另一个map
    // lazily install the 2nd level valuesMap for the particular cacheKey
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    //如果对应的classload没有二级缓存
    if (valuesMap == null) {
    		//这里采用的cas操作,看起来比较奇怪。明明,这里主要是因为而这个方法本身是高并发调用的场景,所以不能用synchronized关键字加锁。所以它采取的机制是,在修改valuesMap时,会重新读取一下内存中的值,看此时是否被别的线程更新了。如果有值,则通过oldvalueMap更新valuesMap这个的值。
    		//putIfAbsent这个方法在map中不存在这个key时,相当于put方法。如果存在这个key,不覆盖,而是相当于get方法。
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }
    //L1 创建二级缓存的key,通过KeyFactory.apply方法,我发现网上有大量的讲解都错误的认为这里的subKeyFactory是ProxyClassFactory。但凡自己debug打断点看一下也不会人云亦云。
    // create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    //根据二级缓存的key获取value
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;
    //这里死循环遍历查询
    while (true) {
    		//如果二级缓存的value不是null
        if (supplier != null) {
            // supplier might be a Factory or a CacheValue<V> instance
            //L2 获取supplier的value 也就是proxy类的class对象
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        //如果缓存内没有这个supplier,或者是supplier的value为空(因为是弱引用,所以可能被gc回收了),或者干脆就是factory没有成功初始化get返回value,没能存到缓存中,那么就会创建一个新的Factory
        // else no supplier in cache
        // or a supplier that returned null (could be a cleared CacheValue
        // or a Factory that wasn't successful in installing the CacheValue)

        // lazily construct a Factory
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {
        		//	supplier不等于null,但是get的value=null,说明原有的factory失效了,所以需要重新替换缓存中的值。
            if (valuesMap.replace(subKey, supplier, factory)) {
                // successfully replaced
                // cleared CacheEntry / unsuccessful Factory
                // with our Factory
                supplier = factory;
            } else {
                // retry with current supplier
                //重新从缓存内获取现在的supplier值
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

L1注释这里生成了二级缓存的key,可见keyFactory这个方法,主要就是根据实现的接口数量生成不同的key,分别是key0,key1,key2和keyX。这里的key是继承自WeakReference。

@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
    switch (interfaces.length) {
        case 1: return new Key1(interfaces[0]); // the most frequent
        case 2: return new Key2(interfaces[0], interfaces[1]);
        case 0: return key0;
        default: return new KeyX(interfaces);
    }
}

整个二级结构缓存可以见如图所示
在这里插入图片描述

L2这里的get方法调用的是Factory的get方法。我们从前面的分析已经知道这个方法是获取class对象,因此我们只需要关注返回值的变化,也就是value。省略掉其他代码,可以看到是从valueFactory.apply这个方法中获取class对象。而这个valueFactory可以查看一下源码中的赋值,就可以知道是ProxyClassFactory。

public synchronized V get() { // serialize access
    //省略
    V value = null;
    try {
        value = Objects.requireNonNull(valueFactory.apply(key, parameter));
    } finally {
        if (value == null) { // remove us on failure
            valuesMap.remove(subKey, this);
        }
    }
    //省略
    return value;
}

因此再进一步到ProxyClassFactory.apply方法,方法很长,总的来说做了几件事情。

(1)获取接口的class对象(2)获取修饰符(3)获取包名,分别对应了generateProxyClass这个方法里面的三个参数。最后生成了一个二进制数组。再通过defineClass0这个方法获取class对象。defineClass0是一个native方法。至此代理类的完整流程已经走通了。

 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 				

        /*
         * Generate the specified proxy class.
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } 
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值