Presto UDF原理分析與淺實踐
阿新 • • 發佈:2018-12-21
背景
這兩天在研究Presto的UDF,發現Presto的UDF的方式很特別,後來自己思考了一下實現方式(沒有看原始碼,有可能與Presto實現不同),現在把思路記錄一下。
思路
Presto UDF 最後是將生成的Jar放到到plugin中,並且呼叫實現com.facebook.presto.spi.Plugin介面的類的getFunctions方法,那麼我猜測的步驟應該如下 1.載入plugin資料夾下的依賴Jar 2.找到實現com.facebook.presto.spi.Plugin的類 3.運用反射獲取實際邏輯的Class 下面我將主要的實現類寫到下面,如有不足之處,還請大家指點
package com.levin.presto.plugin; import com.facebook.presto.spi.Plugin; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * author Levin */ public class MyClassLoader { /** * 用來存放所有Class物件 */ private static Set<Class<?>> classes = new LinkedHashSet<Class<?>>(); /** * 用來存放每個Class中的註解物件 */ private static Map<Class<?>, Annotation[]> classAnnotationMap = new HashMap<Class<?>, Annotation[]>(); /** * 用來存放每個Class中方法的註解物件 */ private static Map<Class<?>, Map<Method, Annotation[]>> classMethodAnnoMap = new HashMap<Class<?>, Map<Method, Annotation[]>>(); /** * 使用URLClassLoader載入制定lib下的所有Jar * * @param lib * @return * @throws MalformedURLException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ public static URLClassLoader libLoad(String lib) throws MalformedURLException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { File file_directory = new File(lib); URLClassLoader loader = (URLClassLoader) ClassLoader.getSystemClassLoader(); //使用反射呼叫addURL方法 Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class}); add.setAccessible(true); if (!file_directory.exists()) { throw new RuntimeException("Jar不存在"); } // load路徑下的所有Jar for (File file : file_directory.listFiles()) { URL url = new URL("file:" + file.getAbsolutePath()); add.invoke(loader, new Object[]{url}); } return loader; } /** * 載入Jar包並且執行Jar包中某個Class的方法 * * @param loader * @throws IOException * @throws ClassNotFoundException */ public static void execJarClass(URLClassLoader loader, String jar_Path) throws IOException, ClassNotFoundException { // String jar_Path = "/home/levin/workspcae/java/plugin_test/target/plugin_test-1.0.jar"; JarFile jarFile = new JarFile(new File(jar_Path)); Enumeration<JarEntry> es = jarFile.entries(); while (es.hasMoreElements()) { JarEntry jarEntry = (JarEntry) es.nextElement(); String name = jarEntry.getName(); // 只加載.class檔案 if (name != null && name.endsWith(".class")) { Class<?> c = loader.loadClass(name.replace("/", ".").substring(0, name.length() - 6)); classes.add(c); Annotation[] classAnnos = c.getDeclaredAnnotations(); classAnnotationMap.put(c, classAnnos); Method[] classMethods = c.getDeclaredMethods(); Map<Method, Annotation[]> methodAnnoMap = new HashMap<Method, Annotation[]>(); for (int i = 0; i < classMethods.length; i++) { Annotation[] a = classMethods[i].getDeclaredAnnotations(); methodAnnoMap.put(classMethods[i], a); } classMethodAnnoMap.put(c, methodAnnoMap); } } System.out.println("新增 " + classes.size() + " 個Class檔案"); } public static void main(String[] args) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { String jar_Path = "/home/levin/workspcae/java/plugin_test/target/plugin_test-1.0.jar"; URLClassLoader loader = libLoad("/home/levin/install-soft/bigdata/presto-server-0.187/lib"); execJarClass(loader, jar_Path); Iterator class_iterator = classes.iterator(); while (class_iterator.hasNext()) { Class c = (Class) class_iterator.next(); if (Plugin.class.isAssignableFrom(c)) { System.out.println(c.getName().concat(" 類實現了 com.facebook.presto.spi.Plugin 介面")); } else { System.out.println(c.getName()); } } } }