自定义类加载器实现反射调用

需求描述:
提供springboot项目使用maven打的jar包,然后通过程序反射调用启动jar包

重写一个类加载器:

import java.io.*;
import java.net.MalformedURLException;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class MyWebAppLoader extends ClassLoader{
    /**
     * lib:表示加载的文件在jar包中
     * 类似tomcat就是{PROJECT}/WEB-INF/lib/
     */
    private String lib;
    /**
     * classes:表示加载的文件是单纯的class文件
     * 类似tomcat就是{PROJECT}/WEB-INF/classes/
     */
    private String classes;
    /**
     * 采取将所有的jar包中的class读取到内存中
     * 然后如果需要读取的时候,再从map中查找
     */
    private Map map;

    /**
     * 只需要指定项目路径就好
     * 默认jar加载路径是目录下{PROJECT}/WEB-INF/lib/
     * 默认class加载路径是目录下{PROJECT}/WEB-INF/classes/
     * @param webPath
     * @throws MalformedURLException
     * @throws SecurityException
     * @throws NoSuchMethodException
     */
    public MyWebAppLoader(String webPath) throws NoSuchMethodException, SecurityException, MalformedURLException {
        lib = webPath + "BOOT-INF/lib/";
        classes = webPath + "BOOT-INF/classes/";
        map = new HashMap(64);
        preReadJarFile();
    }

    /**
     * 按照父类的机制,如果在父类中没有找到的类
     * 才会调用这个findClass来加载
     * 这样只会加载放在自己目录下的文件
     * 而系统自带需要的class并不是由这个加载
     */
    @Override
    protected Class findClass(String name){
        try {
            byte[] result = getClassFromFileOrMap(name);
            if(result == null){
                throw new FileNotFoundException();
            }else{
                return defineClass(name, result, 0, result.length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从指定的classes文件夹下找到文件
     * @param name
     * @return
     */
    private byte[] getClassFromFileOrMap(String name){
        String classPath = classes + name.replace('.', File.separatorChar) + ".class";
        File file = new File(classPath);
        if(file.exists()){
            InputStream input = null;
            try {
                input = new FileInputStream(file);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int bufferSize = 4096;
                byte[] buffer = new byte[bufferSize];
                int bytesNumRead = 0;
                while ((bytesNumRead = input.read(buffer)) != -1) {
                    baos.write(buffer, 0, bytesNumRead);
                }
                return baos.toByteArray();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally{
                if(input != null){
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

        }else{
            if(map.containsKey(name)) {
                //去除map中的引用,避免GC无法回收无用的class文件
                return map.remove(name);
            }
        }
        return null;
    }

    /**
     * 预读lib下面的包
     */
    private void preReadJarFile(){
        List list = scanDir();
        for(File f : list){
            JarFile jar;
            try {
                jar = new JarFile(f);
                readJAR(jar);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 读取一个jar包内的class文件,并存在当前加载器的map中
     * @param jar
     * @throws IOException
     */
    private void readJAR(JarFile jar) throws IOException{
        Enumeration en = jar.entries();
        while (en.hasMoreElements()){
            JarEntry je = en.nextElement();
            String name = je.getName();
            if (name.endsWith(".class")){
                String clss = name.replace(".class", "").replaceAll("/", ".");
                if(this.findLoadedClass(clss) != null) continue;

                InputStream input = jar.getInputStream(je);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int bufferSize = 4096;
                byte[] buffer = new byte[bufferSize];
                int bytesNumRead = 0;
                while ((bytesNumRead = input.read(buffer)) != -1) {
                    baos.write(buffer, 0, bytesNumRead);
                }
                byte[] cc = baos.toByteArray();
                input.close();
                map.put(clss, cc);//暂时保存下来
            }
        }
    }

    /**
     * 扫描lib下面的所有jar包
     * @return
     */
    private List scanDir() {
        List list = new ArrayList();
        File[] files = new File(lib).listFiles();
        for (File f : files) {
            if (f.isFile() && f.getName().endsWith(".jar"))
            list.add(f);
        }
        return list;
    }

    /**
     * 添加一个jar包到加载器中去。
     * @param jarPath
     * @throws IOException
     */
    public void addJar(String jarPath) throws IOException{
        File file = new File(jarPath);
        if(file.exists()){
            JarFile jar = new JarFile(file);
            readJAR(jar);
        }
    }
}

这里我们将jar文件进行了解压
程序调用

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class TestInvoke {
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, MalformedURLException {

//        Method mainMethod  =  Class.forName("com.example.demo.DemoApplication").getMethod("main", String[].class);
//        mainMethod.invoke(null,  (Object)new String[]{"a", "b", "c"});
//
//
//        Method mainMethod2  =  Class.forName("com.example.demo2.DemoApplication2").getMethod("main", String[].class);
//        mainMethod2.invoke(null,  (Object)new String[]{"a", "b"});

//        URL url = new URL("file:///D:/Project/demo2/target/demo2-0.0.1-SNAPSHOT.jar");
//        URLClassLoader myClassLoader = new URLClassLoader(new URL[] {url});
//        Method mainMethod2 = myClassLoader.loadClass("com.example.demo2.DemoApplication2").getMethod("main", String[].class);
//        mainMethod2.invoke(null,  (Object)new String[]{"a", "b"});

        MyWebAppLoader myClassLoader2 = new MyWebAppLoader("D:/Project/demo2/target/demo2-0.0.1-SNAPSHOT1111/");
        Method mainMethod2 = myClassLoader2.loadClass("com.example.demo2.DemoApplication2").getMethod("main", String[].class);
        mainMethod2.invoke(null,  (Object)new String[]{"a", "b"});

        MyWebAppLoader myClassLoader = new MyWebAppLoader("D:/Project/demo/target/demo-0.0.1-SNAPSHOT/");
        Method mainMethod = myClassLoader.loadClass("com.example.demo.DemoApplication").getMethod("main", String[].class);
        mainMethod.invoke(null,  (Object)new String[]{"a", "b"});

    }
}

你可能感兴趣的:(自定义类加载器实现反射调用)