• 创建并使用自定义的类加载器


               JVM 中除根加载器之外的所有类加载器都是ClassLoader子类的实例,开发者可以通过扩展ClassLoader的子类,并重写该ClassLoader所包含的方法来实现自定义的类加载器。

              ClassLoader中包含了大量的proteted方法--- 这些方法都可被子类重写, ClassLoader有如下三个关键方法:

    •     loadClass(String name,boolean resolve ) :该方法为ClassLoader的入口点,根据指定的二进制名称加载类,系统就是调用ClassLoader的方法来获取指定类对应的Class对象。
    •     findClass(String name)   :根据二进制名称来查找类。   

            如果需要实现自定义的ClassLoader,可以通过重写以上两个方法来实现,当然我们推荐重写findClass()方法,而不是重写loadClass()方法。因为loadClass()方法的执行步骤如下:

    1.     用findLoadedClass(String ) 来检查是否已经加载类,如果已经加载则直接返回。
    2.     在父类加载器上调用loadClass方法。如果父类加载器为null,则使用根类加载器来加载。
    3.     调用findClass(String ) 方法查找类。 

                  从上面步骤中可以看出,重写findClass()方法可以避免覆盖默认类加载器的父类委托、缓冲机制两种策略。如果重写loadClass()方法,则实现逻辑更为复杂。

       在ClassLoader里还有一个核心方法:Class defineClass (String name, byte[] b,int off,int len )  .该方法负责将指定类的字节码文件转化为class对象,该字节码文件可以来源于文件网络.


      下面是自定义的类加载器:
      
    package com.reflect;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.lang.reflect.Method;
    
    public class CompileClassLoader extends ClassLoader {
    	
    	String path="D:/mavenwork/mianshi/src/";
    	
    	//读取一个文件的内容
    	private byte[] getBytes(String fileName) throws IOException{
    		File file=new File(fileName);
    		long len=file.length();
    		byte[] raw=new byte[(int) len];
    		
    		FileInputStream fin=new FileInputStream(file);
    		//一次读取class文件的全部二进制数据
    		int r=fin.read(raw);
    		if(r!=len){
    			throw new IOException("无法读取全部文件:"+r+"!="+len);
    		}
    		fin.close();
    		
    		return raw;
    	}
    	
    	//定义编译指定JAVA文件的方法
    	private boolean compile(String javaFile) throws IOException{
    		System.out.println("CompileClassLoader:正在编译"+javaFile+"...");
    		//调用系统的javac命令
    		Process p=Runtime.getRuntime().exec("javac "+javaFile);
    	    try {
    				//其他线程都等待这个线程完成
    	            p.waitFor();
    		} catch (Exception e) {
    			System.out.println(e);
    		}
    		//获取javac线程的退出值
    		int ret=p.exitValue();//此 Process 对象表示的子进程的出口值。根据惯例,值 0 表示正常终止。
    		//返回编译是否成功
    		return ret==0;
    	}
    	
    	//重写ClassLoader的findClass方法
    	protected Class<?> findClass(String name) {
    		 Class clazz=null;
    		//将包路径中的点(.)替换成斜线(/)
    		String fileSub=path+name.replace(".", "/");
    		
    		String javaFilename=fileSub+".java";
    		String classFilename=fileSub+".class";
    		
    		File javaFile=new File(javaFilename);
    		File classFile=new File(classFilename);
    		
    		//当指定的Java源文件存在,且class文件不存在;或Java 源文件的修改时间比class文件的修改时间晚时,重新编译
    		if(javaFile.exists()&&(!classFile.exists()||javaFile.lastModified()>classFile.lastModified())){
    			//如果编译失败,或者该class文件不存在
    			try {
    				if (!compile(javaFilename) || !classFile.exists()) {
    					  throw new ClassNotFoundException("ClassNotFoundException:"+javaFilename);
    				}
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    		//如果class文件存在,系统负责将该文件转换成Class对象
    		if(classFile.exists()){
    		try {
    			//将class文件的二进制数据读入数组
    			byte[] raw=getBytes(classFilename);	
    			//调用classLoader的defineClass方法将二进制数据转换成Class对象
    		   	clazz=defineClass(name, raw, 0,raw.length);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}		
    		}
    		
    		//如果clazz为null,表明加载失败,则抛出异常
    		if(clazz==null){
    			try {
    				throw new ClassNotFoundException(name);
    			} catch (ClassNotFoundException e) {
    				e.printStackTrace();
    			}
    		}
    		return clazz;
    	}
    	
    	
    	//定义一个主方法
    	public static void main(String[] args) throws Exception {
    		//如果运行该程序时没有参数,即没有目标类
    		if(args.length<1){
    			System.out.println("缺少运行的目标类,请按如下格式运行Java源文件:");
    			System.out.println("类的全路径名   参数,如: com.reflect.Hello  参数1");
    		}
    			//第一个参数是需要运行的类
    			String progClass=args[0];
    			//剩下的参数作为运行目标类时的参数,所以将这些参数复制到一个新数组中
    			String progArgs[]=new String[args.length-1];
    			System.arraycopy(args, 1, progArgs, 0, progArgs.length);
    			CompileClassLoader ccl=new CompileClassLoader();
    			//加载需要运行的类
    			 Class<?> clazz=ccl.loadClass(progClass);
    			//获取需要运行类的主方法
    			Method main=clazz.getMethod("main", (new String[0]).getClass());
    			
    			Object argsArray[]={progArgs};
    			
    			main.invoke(null, argsArray);
    	}
    
    }
    

    随意提供一个简单的主类,如Hello.java (包含main方法.)

    package com.reflect;
    
    public class Hello {
    
    	public static void main(String[] args) {
    		
    		for(String arg:args){
    			System.out.println("运行Hello的参数:"+arg);
    		}
    
    	}
    
    }
    







  • 相关阅读:
    基于yum的方式安装Cloudera Manager Agent(使用Mysql 8.0版本)
    数据库基础SQL知识面试题二
    Zabbix Server 自带模板监控更加灵活MySQL数据库
    Zabbix Server 自带模板监控有密码MySQL数据库
    Zabbix Server 自带模板监控无密码MySQL数据库
    Zabbix Server 监控Web页面
    HDFS集群常见报错汇总
    Hadoop ha CDH5.15.1-hadoop集群启动后,集群容量不正确,莫慌,这是正常的表现!
    FastDFS + Nginx代理方式访问
    FastDFS 分布式文件系统部署实战及基本使用
  • 原文地址:https://www.cnblogs.com/wuyida/p/6300342.html
Copyright © 2020-2023  润新知