动态代理在创建HiveMetaStoreClient上的运用
Posted 咬定青松
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理在创建HiveMetaStoreClient上的运用相关的知识,希望对你有一定的参考价值。
本文首发微信公众号:码上观世界
Hive MetaStore 在版本2.x和3.x中有较大改变,包括常用接口类的参数,比如这个对外使用的HiveMetaStoreClient,其构造方法参数类型由之前的HiveConf类型改成了Configuration,由此出现了不同版本的兼容问题。
#HiveMetaStoreClient.class
public HiveMetaStoreClient(Configuration conf) throws MetaException
this(conf, null, true);
public HiveMetaStoreClient(Configuration conf, HiveMetaHookLoader hookLoader) throws MetaException
this(conf, hookLoader, true);
public HiveMetaStoreClient(Configuration conf, HiveMetaHookLoader hookLoader, Boolean allowEmbedded)
...
不同引擎在适配不同版本的HiveMetaStoreClient采用了不同的办法,比如有的扩展原始的ThriftMetastoreClient,有的通过动态代理生成相应的HiveMetaStoreClient,本文来研究如何通过动态代理的方式创建HiveMetaStoreClient。
动态代理
Java Dynamic Proxy 是JDK提供的一种重要的代理机制,通过动态代理,可以通过一个方法服务多个方法调用,常用于门面(Facade)设计模式,是框架开发者的利器。
动态代理跟InvocationHandler相关联,用于Wapper实际方法执行之前的逻辑,比如:
public class DynamicInvocationHandler implements InvocationHandler
private static Logger LOGGER = LoggerFactory.getLogger(
DynamicInvocationHandler.class);
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
LOGGER.info("Invoked method: ", method.getName());
return 42;
示例代码创建了一个InvocationHandler实例,只是打印将要执行的具体方法名称。其方法invoke在每次执行代理实例的方法时都会被调用,它有3个参数:
proxy:代理实例
method:实际执行的方法
args:实际执行的方法参数
返回值可以是调用被代理方法的返回值。
代理实例可以通过下面API来创建:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
其三个参数分别是:
loader:定义代理类的类加载器,可取自InvocationHandler实例所在类加载器
interfaces:代理类将要实现的接口集合
h:上文介绍的InvocationHandler实例
理论上,我们可以实现任意接口的代理类,比如我们创建Map类型的代理类:
Map proxyInstance = (Map) Proxy.newProxyInstance(
DynamicProxyTest.class.getClassLoader(), new Class[] Map.class , new DynamicInvocationHandler());
可见,proxyInstance 是Map类的实例,一旦创建了代理实例,我们将可以按照通常的接口来调用:
proxyInstance.put("hello", "world");
在示例中DynamicInvocationHandler的invoke只是实现了代理方法的部分逻辑,并没有执行实际被代理类的方法,如果要调用实际的方法,还需要绑定实际方法所在类对象,我们将DynamicInvocationHandler略微修改,关联上实际类HashMap:
public static class DynamicInvocationHandler implements InvocationHandler
private static Logger LOGGER = LoggerFactory.getLogger(
DynamicInvocationHandler.class);
Map map;
public DynamicInvocationHandler(Map map)
this.map=map;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
LOGGER.info("Invoked method:,args:", method.getName(),args);
return method.invoke(this.map,args);
示例中,在DynamicInvocationHandler构造方法中传入实际的类对象,然后在invoke方法中调用其Method。
此时再调用proxyInstance的put方法,才能让put发生“实际存储”的效果:
@Test
public void testDynamicProxy()
Map hashMap= Maps.newHashMap();
Map proxyInstance = (Map) Proxy.newProxyInstance(
DynamicInvocationHandler.class.getClassLoader(),
new Class[] Map.class ,
new DynamicInvocationHandler(hashMap));
proxyInstance.put("hello","world");
HiveMetaStoreClient
HiveMetaStoreClient的接口类是IMetaStoreClient,创建该接口的代理类,我们首先需要关联一个InvocationHandler,同时在InvocationHandler中绑定实际被代理对象HiveMetaStoreClient,由于HiveMetaStoreClient存在版本兼容问题,这里我们没法直接实例化其对象,只好通过反射方式在运行期间生成,为此我们将HiveMetaStoreClient的构造方法参数提取出来,通过InvocationHandler实例的构造方法参数传入:
constructorArgTypes:构造方法参数类型
constructorArgs:构造方法参数值
msClientClass:HiveMetaStoreClient.class对象
然后通过反射动态创建IMetaStoreClient base实例对象:
public MetaStoreClientHandler(Class<?>[] constructorArgTypes,
Object[] constructorArgs,
Class<? extends IMetaStoreClient> msClientClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException
Constructor meth = msClientClass.getDeclaredConstructor(constructorArgTypes);
meth.setAccessible(true);
//private final IMetaStoreClient base;
this.base = (IMetaStoreClient) meth.newInstance(constructorArgs);
最后再在invoke中实现具体的代理逻辑,包括触发实际方法调用。
public static class MetaStoreClientHandler implements InvocationHandler
private final IMetaStoreClient base;
...
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
//something before
return method.invoke(this.base, args);
有了MetaStoreClientHandler实例,我们再看如何调用:
1. 提取HiveMetaStoreClient构造方法参数类型和参数值(以三个参数的方法为例),并构造MetaStoreClientHandler对象:
//或者 HiveConf.class
Class[] constructorArgTypes = new Class[]Configuration.class, HiveMetaHookLoader.class, Boolean.class;
Object[] constructorArgs = new Object[]this.hiveConf, (HiveMetaHookLoader) tbl -> null, true;
2. 通过Proxy.newProxyInstance创建代理
IMetaStoreClient client= (IMetaStoreClient) Proxy.newProxyInstance(
MetaStoreClientHandler.class.getClassLoader(), HiveMetaStoreClient.class.getInterfaces(), handler);
3. 调用代理方法
Database database= new DatabaseBuilder().setCatalogName("hdfs202_299").setName("test_db").create(client,hiveConf);
List<String> list=client.getAllDatabases();
总结
本文简单回顾了Java动态代理技术,并介绍了如果通过动态代理来解决Hive Metastrore版本兼容问题,这也是一些引擎解决版本问题的基本思路。
参考
https://www.baeldung.com/java-dynamic-proxies
https://www.baeldung.com/java-8-lambda-expressions-tips
https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html
以上是关于动态代理在创建HiveMetaStoreClient上的运用的主要内容,如果未能解决你的问题,请参考以下文章