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);
}
}
}