java自定义类加载器

前言

  java反射,最常用的Class.forName()方法。做毕设的时候,接收到代码字符串,通过 JavaCompiler将代码字符串生成A.class文件(存放在classpath下,也就是eclipse项目中的bin目录里),然后通过java反射机制,获取main方法并执行。.class文件名称固定。当 A.class文件更新的时候,问题出现了,main方法的执行结果总和第一次的执行结果相同。

程序流程

  代码提交->接收代码->编译成A.class文件->java反射->main方法执行

  具体代码参考:http://www.cnblogs.com/hujunzheng/p/5203067.html

问题原因

  类加载器的委托机制!说到这里,不得不介绍一下java的类加载器。

java虚拟机中的类加载器

  java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类: BootStrap,ExtClassLoader,AppClassLoader

  类加载器也是Java类,因为Java类的类加载器本身也是要被类加载器加载的,显然必须有第一个类加载器不是Java类,这个正是BootStrap,使用C/C++代码写的,已经封装到JVM内核中了,而ExtClassLoader和AppClassLoader是Java类。

类加载器的属性结构图

  盗图一张:

由此得到结论

  首先我的A.class文件更新了,接着调用Class.forName()[我想的是重新加载一下字节码文件对象],然后最终由AppClassLoader去加载,其中有一个函数很重要,就是loadClass(), 看一下这个函数的源码,如下:

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{  //加上锁,同步处理,因为可能是多线程在加载类  synchronized (getClassLoadingLock(name)) {  //检查,是否该类已经加载过了,如果加载过了,就不加载了  Class c = findLoadedClass(name);  if (c == null) {  long t0 = System.nanoTime();  try {  //如果自定义的类加载器的parent不为null,就调用parent的loadClass进行加载类  if (parent != null) {  c = parent.loadClass(name, false);  } else {  //如果自定义的类加载器的parent为null,就调用findBootstrapClass方法查找类,就是Bootstrap类加载器  c = findBootstrapClassOrNull(name);  }  } catch (ClassNotFoundException e) {  // ClassNotFoundException thrown if class not found  // from the non-null parent class loader  
         }  if (c == null) {  // If still not found, then invoke findClass in order  // to find the class.  long t1 = System.nanoTime();  //如果parent加载类失败,就调用自己的findClass方法进行类加载  c = findClass(name);  // this is the defining class loader; record the stats  sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);  sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);  sun.misc.PerfCounter.getFindClasses().increment();  }  }  if (resolve) {  resolveClass(c);  }  return c;  }  
}  

  如果同名的.class文件之前加载了就不会在加载了。。。

解决办法  用户自定义类加载器

  想法1: 重写loadClass()这个函数,无论是否加载过.class问价,都重新加载。

   @Overridepublic java.lang.Class<?> loadClass(String name) throws ClassNotFoundException {System.out.println(name);byte[] data = loaderClassData(name);return this.defineClass(name, data, 0, data.length);};

  但是竟然出错了,至今还没有搞明白... Main是我要加载的类,loadClass()函数执行了两次,第二次不知道怎么调用的。。。?有谁知到,告诉我一下,谢了!

Main
java.lang.Object
java.io.FileNotFoundException: java\lang\Object.class (系统找不到指定的路径。)at java.io.FileInputStream.open(Native Method)at java.io.FileInputStream.<init>(Unknown Source)at com.ds.tools.MyClassLoader.loaderClassData(MyClassLoader.java:53)at com.ds.tools.MyClassLoader.loadClass(MyClassLoader.java:78)at java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClassCond(Unknown Source)at java.lang.ClassLoader.defineClass(Unknown Source)at java.lang.ClassLoader.defineClass(Unknown Source)at com.ds.tools.MyClassLoader.loadClass(MyClassLoader.java:79)at com.ds.tools.MyClassLoader.main(MyClassLoader.java:96)

  想法2: 只能默默的重写findClass()方法了, loadClass()方法中会调用这个函数,为了避过AppClassLoader检查类是否已经加载过了,我把A.class的生成位置放到了项目根目录下的myClass目录中,这样MyClassLoader委托AppClassLoader对A.class进行加载时,在当前的classpath下找不到对应的类,无法完成类的加载(同样BootStrapLoader和ExtClassLoader都不会找到),最终是我们自定的类加载器完成类的加载,代码如下:

public class MyClassLoader extends ClassLoader {//类加载器名称private String loaderName;//加载类的路径private String path = "";private final String fileType = ".class";public MyClassLoader(String loaderName){//让系统类加载器成为该 类加载器的父加载器super();this.loaderName = loaderName;}public MyClassLoader(ClassLoader parent, String loaderName){//显示指定该类加载器的父加载器super(parent);this.loaderName = loaderName;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}@Overridepublic String toString() {return this.loaderName;}/*** 获取.class文件的字节数组* @param name* @return*/private byte[] loaderClassData(String name){InputStream is = null;byte[] data = null;ByteArrayOutputStream baos = new ByteArrayOutputStream();name = name.replace(".", "/");try {is = new FileInputStream(new File(path + name + fileType));int c = 0;while(-1 != (c = is.read())){baos.write(c);}data = baos.toByteArray();} catch (Exception e) {e.printStackTrace();} finally{try {if(is != null)is.close();if(baos != null)baos.close();} catch (IOException e) {e.printStackTrace();}}return data;}/*** 获取Class对象*/@Overridepublic Class<?> findClass(String name) throws ClassNotFoundException{byte[] data = loaderClassData(name);return this.defineClass(name, data, 0, data.length);}public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {for(int i=0; i<5; i++){MyClassLoader loader1 = new MyClassLoader("MyClassLoader");//String path = new File(MyClassLoader.getSystemClassLoader().getResource("").getPath()).getParent();loader1.setPath("myClass/");Class<?> clazz = loader1.loadClass("Main");System.out.println(clazz.getName());}}
}

 

转载于:https://www.cnblogs.com/hujunzheng/p/5357008.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531328.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

repo介绍(一)

repo简介 Repo 是我们以 Git 为基础构建的代码库管理工具,可以组织多个仓库的上传和下载。它是由一系列的Python脚本组成&#xff0c;封装了一系列的Git命令&#xff0c;用来统一管理多个Git仓库 一个大型的项目可能由很多小的仓库组合而成的&#xff0c;为了方便统一管理各个…

hash长度扩展攻击

作为一个信息安全的人&#xff0c;打各个学校的CTF比赛是比较重要的&#xff01; 最近一个朋友发了道题目过来&#xff0c;发现有道题目比较有意思&#xff0c;这里跟大家分享下 这串代码的大致意思是&#xff1a; 这段代码首先引入了一个名为"flag.php"的文件&am…

repo介绍(二)

这篇文章来实例操作 安装repo&#xff0c;参考repo介绍这一节创建repo存放default.xml 的git仓库 初始化repo&#xff0c;repo init -u https://gitee.com/angerial/repo-test.git 这个时候会在当前目录生成如下文件 参考repo组成&#xff0c;修改.repo/manifest.xml,这里我的…

springmvc环境搭建以及常见问题解决

1.新建maven工程 a) 打开eclipse&#xff0c;file->new->project->Maven->Maven Project b) 下一步 c) 选择创建的工程为webapp&#xff0c;下一步 d) 填写项目的group id和artifact id。一般情况下&#xff0c;group id写域名的倒序&#xff0c;artifact id…

eclipse build workspace太慢或者 js出错问题解决

1.js文件错误解决办法 右键项目->properties->Builders(注&#xff1a;JavaScript Validator也会引起 build workspace太慢) 2.Eclipse 一直不停 building workspace完美解决总结&#xff08;来自: http://blog.163.com/shadow_wolf/blog/static/18346909720145279519222…

springmvc+jpa实现分页的两种方式

1.工具类 public final class QueryTool {public static PageRequest buildPageRequest(int pageNumber, int pageSize, String sortType){Sort sort null;if("auto".equals(sortType)) {sort new Sort(Direction.DESC, "ctime");} else {sort new Sort…

不使用session,借助redis实现验证码

1.首先看一下基本的流程 2.看一下代码 注&#xff1a;其中用到的一些工具类&#xff0c;可以到我的github上去下载 https://github.com/hjzgg/usually_util/tree/master/utils windows 下的 redis下载 https://github.com/hjzgg/redis 获取验证码的tooken RequestMapping(value…

PS批处理的使用

一、 前言 做开发的时候&#xff0c;最多的时候就是图片的使用了。有时候图片的处理都按照同样的步骤&#xff0c;比如说统一将图片的大小调整为固定大小&#xff0c;或者统一在所有的图片的的某个位置上加入文字或者小图片等等&#xff0c;这时候PS的批处理可以帮你完成这些重…

exe4j的使用

下载&#xff1a;http://download.cnet.com/exe4j/3000-2070_4-144405.html 参考&#xff1a;http://blog.chinaunix.net/uid-25749806-id-4380850.html 注&#xff1a;打包成jar包的一般就是src目录&#xff0c;其他目录放到目标文件夹中&#xff0c;目录结构如下 如何解决exe…

android表白app

一、前言 马上就要520和521了&#xff0c;是不是还有像我一样的单身狗啊。我就知道有&#xff0c;所以这两天简单写了这个小程序&#xff08;其实是替别人写的&#xff09;&#xff0c;虽然我并不会用去骗女孩子&#xff08;因为最近太忙了&#xff0c;实习完之后要搞毕设&…

webpack+react+es6开发模式

一、前言 实习了两个月&#xff0c;把在公司用到的前端开发模式做个简单的整理。公司里前端开发模式webpackreactreduxes6&#xff0c;这里去掉了redux。 webpack, react, redux等学习网址&#xff1a;http://www.cnblogs.com/hujunzheng/p/5405780.html 二、简单的步骤条组件 …

git命令分类图

转载于:https://www.cnblogs.com/hujunzheng/p/5560826.html

数据结构算法模拟系统

一、前言 学习数据结构已经有很长时间了&#xff0c;加上之前搞过一段时间的ACM&#xff0c;虽然搞得并不怎么样吧&#xff0c;但是喜欢的东西不能放弃&#xff0c;一直打算写一个算法模拟系统&#xff0c;对常用的一些算法进行简单的模拟&#xff0c;于是我的毕业设计就这样诞…

推荐几款jquery图片切换插件

一、前言 毕业季到了&#xff0c;大家都在匆匆忙忙的记录大学里最美好的时光&#xff0c;照片中各种花式、各种姿势都涌现出来了。这么多的照片怎么展示出来给自己的好友看呢&#xff1f;有人选择做成视频&#xff0c;有人选择ps之后做成图片集&#xff0c;而我选择利用静态网页…

c语言表白

马上就要520了&#xff0c;不少小伙伴们一定开始想尽各种办法进行表白了…那么身为奔跑在程序员道路上的我们也一定要有独特的表白方法&#xff01; 下面是一段表白代码&#xff0c;请直接搬去用吧&#xff01;&#xff01;&#xff01; #include <stdio.h> void change…

python表白

马上就要520了&#xff0c;不少小伙伴们一定开始想尽各种办法进行表白了…那么身为奔跑在程序员道路上的我们也一定要有独特的表白方法&#xff01; 下面是一段表白代码&#xff0c;请直接搬去用吧&#xff01;&#xff01;&#xff01; import turtle import timedef hart_ar…

git revert和reset区别

1.在github上建立测试项目并克隆到本地 2.本地中新建两个文本文件 3.将a.txt commit并push到远程仓库 执行 git add a.txt, git commit -m "a.txt", git push 4.将b.txt提交到本地仓库&#xff0c;不执行push 通过gitk命令查看提交历史如下&#xff1a; 情景&#xf…

sorl6.0+jetty+mysql搭建solr服务

1.下载solr 官网&#xff1a;http://lucene.apache.org/solr/ 2.目录结构如下 3.启动solr&#xff08;默认使用jetty部署&#xff09; 在path路径下将 bin文件夹对应的目录加入&#xff0c;然后输入 solr start&#xff08;或者 solr start -p port&#xff0c;指定端口启动&am…

Maven中安装本地Jar包到仓库中或将本地jar包上传

摘要 maven install 本地jar命令格式 mvn install:install-file -DgroupId<group_name> -DartifactId<artifact_name> -Dversion<version_no> -Dfile<path_of_the_local_jar> -Dpackagingjar -DgeneratePomtrue 示例 mvn install:install-file -Dgroup…

二维码登录原理及生成与解析

一、前言 这几天在研究二维码的扫码登录。初来乍到&#xff0c;还有好多东西不懂。在网上看到有人写了一些通过QRCode或者Zxing实现二维码的生成和解码。一时兴起&#xff0c;决定自己亲手试一试。本人是通过QRCode实现的&#xff0c;下面具体的说一下。 二、二维码原理 基础知…