类加载实践:读取jar包

类加载实践:读取jar包(在B站上视频学习记录)

视频链接,没怎么接触过的可以看下视频。https://www.bilibili.com/video/BV16T4y1P79h?p=9&t=628

类加载机制实际上调用了 loadClass() -> findClass()

1、从特定路径jar包中读取class(没有打破双亲委派机制,只是读取其他位置的jar包):

public class test {
    public static void main(String[] args) throws MalformedURLException {
        Double salary = 2000.0;
        Double money;
        URL jarPath = new URL("file:F:\\temp\\com.hzy\\salary.jar");
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jarPath});
        while (true) {
            try {
                Thread.sleep(1000);
                money = calSalary(salary, urlClassLoader);
                System.out.println("实际到手工资 :" + money);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    private static Double calSalary(Double salary, ClassLoader urlClassLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> clazz = urlClassLoader.loadClass("com.hzy.Salary");
        Object obj = clazz.newInstance();
        return (Double) clazz.getMethod("cal", Double.class).invoke(obj, salary);

    }
}

jar包生成方法,使用eclipse可以直接生成,idea建议直接用maven生成

在这里插入图片描述

2、读取jar包中class,并打破双亲委派机制(重写loadClass方法)

public class OADemo {
    public static void main(String[] args) throws MalformedURLException {
        Double salary = 2000.0;
        Double money;
        
        // 自定义jar包路径,jar包中包含了要调用的salary.class
        String path = "F:\\temp\\hzy\\salary.jar";
        
        // 自定义ClassLoader,打破双亲委派机制,重写loadClass方法
        MyClassLoader myClassLoader = new MyClassLoader(path);
        while (true) {
            try {
                Thread.sleep(1000);
                money = calSalary(salary, myClassLoader);
                System.out.println("实际到手工资 :" + money);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    // 通过类加载器,加载要调用的类中的方法()
    private static Double calSalary(Double salary, ClassLoader urlClassLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> clazz = urlClassLoader.loadClass("com.hzy.Salary");
        Object obj = clazz.newInstance();
        return (Double) clazz.getMethod("cal", Double.class).invoke(obj, salary);

    }

    // 静态内部类,自定的classLoder
    private static class MyClassLoader extends SecureClassLoader {
        private String jarFile;

        public MyClassLoader(String jarFile) {
            this.jarFile = jarFile;
        }

        // 打破双亲委派机制,优先加载本地类
        @Override
        protected Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException {
            // 读取缓存中的类
            Class<?> c = findLoadedClass(name);
            if (c != null) {
                return c;
            }
            if (name.indexOf("Salary") > 0) {
                return this.findClass(name);
            }
            return super.loadClass(name, resolve);
        }

        // 重写的原因是为了清晰class文件的读取流程 
        @Override
        public Class findClass(String name) {
            InputStream inputStream;
            URL fileUrl;
            byte[] b;
            int code;
            // 需要先对class路径进行转换,获取jar包中的class文件路径
            String classPath = name.replace(".", "/").concat(".class");
            StringBuilder sb = new StringBuilder();
            ByteArrayBuffer buffer = new ByteArrayBuffer();
            try {
                // 读取jar包中的class
                fileUrl = new URL("jar:file:\\" + this.jarFile + "!/" + classPath);
                inputStream = fileUrl.openStream();
                while ((code = inputStream.read()) != -1) {
                    buffer.write(code);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            b = buffer.toByteArray();
            return super.defineClass(name, b, 0, b.length);
        }
    }
}

在这里插入图片描述

3、java的SPI(service Provider interface)

方法:

  • 在resources/META-INF/ 下新建services文件

  • 新建接口方法的文件 com.hzy.SalaryCalService.txt

在这里插入图片描述

  • 在接口方法 文件中写入具体的实现类,可以有多个

在这里插入图片描述

在前面的例子中都是使用反射的方式调用方法,java的SPI实际上体现了多态的思想,一个接口,多个实现

public class OADemo {
    public static void main(String[] args) throws MalformedURLException {
        Double salary = 2000.0;
        Double money;
        
        // 同样是读取自定义的jar包
        String path = "F:\\temp\\hzy\\salary.jar";
        MyClassLoader myClassLoader = new MyClassLoader(path);
        
        // 分析源码可以看出实际上调用的线程上下文的 classLoader
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        try {
            // 设置自定义类加载器为当前线程上下文类加载器
            Thread.currentThread().setContextClassLoader(myClassLoader);
            
            // 加载实现类
            ServiceLoader<SalaryCalService> services = ServiceLoader.load(SalaryCalService.class);
            
            // 配置文件中只写了一个,所以迭代器中只有一个
            Iterator<SalaryCalService> iterator = services.iterator();
            if (iterator.hasNext()) {
                
                // 调用实现类中方法,(而不是通过反射的方式)
                SalaryCalService service = iterator.next();
                System.out.println(service.cal(salary));
            }
        } finally {
            // 调用完之后重置线程上下文类加载器(不重置不知道会不会有啥问题)
            Thread.currentThread().setContextClassLoader(cl);
        }
    }

    private static Double calSalary(Double salary, ClassLoader urlClassLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> clazz = urlClassLoader.loadClass("com.hzy.Salary");
        Object obj = clazz.newInstance();
        return (Double) clazz.getMethod("cal", Double.class).invoke(obj, salary);

    }

    // 自定义类加载器
  private static class MyClassLoader extends SecureClassLoader {
        private String jarFile;

        public MyClassLoader(String jarFile) {
            this.jarFile = jarFile;
        }

        @Override
        public Class findClass(String name) {
            InputStream inputStream;
            URL fileUrl;
            byte[] b;
            int code;
            String classPath = name.replace(".", "/").concat(".class");
            StringBuilder sb = new StringBuilder();
            ByteArrayBuffer buffer = new ByteArrayBuffer();
            try {
                fileUrl = new URL("jar:file:\\" + this.jarFile + "!/" + classPath);
                inputStream = fileUrl.openStream();
                while ((code = inputStream.read()) != -1) {
                    buffer.write(code);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            b = buffer.toByteArray();
            return super.defineClass(name, b, 0, b.length);
        }
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值