classloader 热部署

这两天一直在学习一些classloader的相关知识,看了一些文章,了解到classloader的作用之一就是实现热部署功能。于是就看了一个网络上的一个例子,然后自己实现了一个应用。虽然作出来了,但是说实话:不满意。因为在这个例子当中,只要热部署一次,就要重新new一个classloader,这样会引发什么问题我也不清楚,并且,classloader究竟实现了什么,以及一些底层的东西我还不是很了解,还要继续研究,目前的版本就是一个中间版本。以后还要优化,或者在我读完tomcat的classloader之后我在去仿照着写一个。

1,使用CLass.forName()可以动态的得到一个.class的类。

2,Thread.currentThread().getContextClassLoader())得到当前的classloader。然后就可以得到classpath下面任何包中的类


好了,下面介绍这个工程的构思、以及实现方式,设计思想:首先来说:这个工程至少需要是需要2个线程,一个是类似tomcat的服务线程,另外一个就是检测线程,检测变化,重新加载Class对象。我猜tomcat是采取了检测类,检测加载了的类文件变化。我没有那么实现,因为这种实现方式相对复杂,并且我的想集中解决热部署问题,而不是如何实现监控文件,所以我就采取了相对简单的方式:socket通知方式。也就是,在我重新编译一个class之后,利用socket通知检测线程,监测监测在监测到socket命令之后会自动的加载。

Java代码 复制代码
  1. Java代码    
  2. package com.cxz.classloader;      
  3.      
  4. import com.cxz.jiangyou.Say;      
  5.      
  6. import java.io.ByteArrayOutputStream;      
  7. import java.io.File;      
  8. import java.io.FileInputStream;      
  9. import java.io.IOException;      
  10.      
  11. /**    
  12.  * This classloader is like a template    
  13.  * which includes pre-loadClass and after-loadClass    
  14.  * @author Bernard    
  15.  *    
  16.  */     
  17. public class ComplexClassLoader extends ClassLoader {      
  18.      
  19.     public ComplexClassLoader() {      
  20.     }      
  21.      
  22.     public ComplexClassLoader(String defaultTargetDir) {      
  23.         this.defaultTargetDir = defaultTargetDir;      
  24.     }      
  25.      
  26.     private String defaultTargetDir = "D://hotdeploys//";      
  27.      
  28.     public Class<?> findClass(String className) throws ClassNotFoundException {      
  29.         byte[] classBytes = null;      
  30.         try {      
  31.             classBytes = loadByteCode(className);      
  32.         } catch (IOException e) {      
  33.             // TODO Auto-generated catch block      
  34.             e.printStackTrace();      
  35.         }      
  36.         return super.defineClass(className, classBytes, 0, classBytes.length);      
  37.     }      
  38.      
  39.     private byte[] loadByteCode(String className) throws IOException {      
  40.         int ch = 0;      
  41.         className = className.replaceAll("//.""////") + ".class";      
  42.         //The two slashes represent for meaning changing      
  43.         File file = new File(defaultTargetDir + className);      
  44.         FileInputStream in = null;      
  45.         in = new FileInputStream(file);      
  46.         ByteArrayOutputStream buffer = new ByteArrayOutputStream();      
  47.         while ((ch = in.read()) != -1) {      
  48.             buffer.write(ch);      
  49.         }      
  50.         in.close();      
  51.         return buffer.toByteArray();      
  52.     }      
  53.      
  54.     public String getDefaultTargetDir() {      
  55.         return defaultTargetDir;      
  56.     }      
  57.      
  58.     public void setDefaultTargetDir(String defaultTargetDir) {      
  59.         this.defaultTargetDir = defaultTargetDir;      
  60.     }         
  61. }     
  62.   
  63. package com.cxz.classloader;   
  64.   
  65. import com.cxz.jiangyou.Say;   
  66.   
  67. import java.io.ByteArrayOutputStream;   
  68. import java.io.File;   
  69. import java.io.FileInputStream;   
  70. import java.io.IOException;   
  71.   
  72. /**  
  73.  * This classloader is like a template  
  74.  * which includes pre-loadClass and after-loadClass  
  75.  * @author Bernard  
  76.  *  
  77.  */  
  78. public class ComplexClassLoader extends ClassLoader {   
  79.   
  80.     public ComplexClassLoader() {   
  81.     }   
  82.   
  83.     public ComplexClassLoader(String defaultTargetDir) {   
  84.         this.defaultTargetDir = defaultTargetDir;   
  85.     }   
  86.   
  87.     private String defaultTargetDir = "D://hotdeploys//";   
  88.   
  89.     public Class<?> findClass(String className) throws ClassNotFoundException {   
  90.         byte[] classBytes = null;   
  91.         try {   
  92.             classBytes = loadByteCode(className);   
  93.         } catch (IOException e) {   
  94.             // TODO Auto-generated catch block   
  95.             e.printStackTrace();   
  96.         }   
  97.         return super.defineClass(className, classBytes, 0, classBytes.length);   
  98.     }   
  99.   
  100.     private byte[] loadByteCode(String className) throws IOException {   
  101.         int ch = 0;   
  102.         className = className.replaceAll("//.""////") + ".class";   
  103.         //The two slashes represent for meaning changing   
  104.         File file = new File(defaultTargetDir + className);   
  105.         FileInputStream in = null;   
  106.         in = new FileInputStream(file);   
  107.         ByteArrayOutputStream buffer = new ByteArrayOutputStream();   
  108.         while ((ch = in.read()) != -1) {   
  109.             buffer.write(ch);   
  110.         }   
  111.         in.close();   
  112.         return buffer.toByteArray();   
  113.     }   
  114.   
  115.     public String getDefaultTargetDir() {   
  116.         return defaultTargetDir;   
  117.     }   
  118.   
  119.     public void setDefaultTargetDir(String defaultTargetDir) {   
  120.         this.defaultTargetDir = defaultTargetDir;   
  121.     }      
  122. }  
Java代码 
package com.cxz.classloader;   
  
import com.cxz.jiangyou.Say;   
  
import java.io.ByteArrayOutputStream;   
import java.io.File;   
import java.io.FileInputStream;   
import java.io.IOException;   
  
/**  
 * This classloader is like a template  
 * which includes pre-loadClass and after-loadClass  
 * @author Bernard  
 *  
 */  
public class ComplexClassLoader extends ClassLoader {   
  
    public ComplexClassLoader() {   
    }   
  
    public ComplexClassLoader(String defaultTargetDir) {   
        this.defaultTargetDir = defaultTargetDir;   
    }   
  
    private String defaultTargetDir = "D://hotdeploys//";   
  
    public Class<?> findClass(String className) throws ClassNotFoundException {   
        byte[] classBytes = null;   
        try {   
            classBytes = loadByteCode(className);   
        } catch (IOException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
        return super.defineClass(className, classBytes, 0, classBytes.length);   
    }   
  
    private byte[] loadByteCode(String className) throws IOException {   
        int ch = 0;   
        className = className.replaceAll("//.", "////") + ".class";   
        //The two slashes represent for meaning changing   
        File file = new File(defaultTargetDir + className);   
        FileInputStream in = null;   
        in = new FileInputStream(file);   
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();   
        while ((ch = in.read()) != -1) {   
            buffer.write(ch);   
        }   
        in.close();   
        return buffer.toByteArray();   
    }   
  
    public String getDefaultTargetDir() {   
        return defaultTargetDir;   
    }   
  
    public void setDefaultTargetDir(String defaultTargetDir) {   
        this.defaultTargetDir = defaultTargetDir;   
    }      
}  

package com.cxz.classloader;

import com.cxz.jiangyou.Say;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * This classloader is like a template
 * which includes pre-loadClass and after-loadClass
 * @author Bernard
 *
 */
public class ComplexClassLoader extends ClassLoader {

	public ComplexClassLoader() {
	}

	public ComplexClassLoader(String defaultTargetDir) {
		this.defaultTargetDir = defaultTargetDir;
	}

	private String defaultTargetDir = "D://hotdeploys//";

	public Class<?> findClass(String className) throws ClassNotFoundException {
		byte[] classBytes = null;
		try {
			classBytes = loadByteCode(className);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return super.defineClass(className, classBytes, 0, classBytes.length);
	}

	private byte[] loadByteCode(String className) throws IOException {
		int ch = 0;
		className = className.replaceAll("//.", "////") + ".class";
		//The two slashes represent for meaning changing
		File file = new File(defaultTargetDir + className);
		FileInputStream in = null;
		in = new FileInputStream(file);
		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
		while ((ch = in.read()) != -1) {
			buffer.write(ch);
		}
		in.close();
		return buffer.toByteArray();
	}

	public String getDefaultTargetDir() {
		return defaultTargetDir;
	}

	public void setDefaultTargetDir(String defaultTargetDir) {
		this.defaultTargetDir = defaultTargetDir;
	}	
}



该类会自动加载d:/hotdeploys下的类文件.

下面这个就是测试类
Java代码

Java代码 复制代码
  1. package com.cxz.classloader;      
  2.      
  3. import java.io.IOException;      
  4. import java.net.ServerSocket;      
  5. import java.net.Socket;      
  6.      
  7. import com.cxz.jiangyou.Say;      
  8.      
  9. public class MultiThreadTest implements Runnable {      
  10.      
  11.     private static final int portNum = 9090;      
  12.      
  13.     private static final int sleepCycle = 3000;      
  14.      
  15.     private Say sayer = null;      
  16.      
  17.     private ComplexClassLoader loader = new ComplexClassLoader();      
  18.      
  19.     private String delpoyee = "com.cxz.jiangyou.Sample";      
  20.      
  21.     public MultiThreadTest() {      
  22.         hotDeploy(delpoyee);      
  23.     }      
  24.      
  25.     public void startService() {      
  26.         while (true) {      
  27.             synchronized (sayer) {      
  28.                 sayer.say();      
  29.             }      
  30.             try {      
  31.                 Thread.sleep(sleepCycle);      
  32.             } catch (InterruptedException e) {      
  33.                 // TODO Auto-generated catch block      
  34.                 e.printStackTrace();      
  35.             }      
  36.         }      
  37.     }      
  38.      
  39.     public void hotDeploy(String name) {      
  40.         try {      
  41.             if (sayer != null) {      
  42.                 synchronized (sayer) {      
  43.                     loader = new ComplexClassLoader();      
  44.                     sayer = (Say) loader.loadClass(name).newInstance();      
  45.                 }      
  46.                 System.out.println("-------------->Hot deployment finished!");      
  47.             } else {      
  48.                 sayer = (Say) loader.loadClass(name).newInstance();      
  49.                 System.out.println("-------------->Initialization finished!");      
  50.             }      
  51.      
  52.         } catch (InstantiationException e) {      
  53.             // TODO Auto-generated catch block      
  54.             e.printStackTrace();      
  55.         } catch (IllegalAccessException e) {      
  56.             // TODO Auto-generated catch block      
  57.             e.printStackTrace();      
  58.         } catch (ClassNotFoundException e) {      
  59.             // TODO Auto-generated catch block      
  60.             e.printStackTrace();      
  61.         }      
  62.     }      
  63.      
  64.     @Override     
  65.     public void run() {      
  66.         ServerSocket server = null;      
  67.         Socket socket = null;      
  68.         try {      
  69.             server = new ServerSocket(portNum);      
  70.             while (true) {      
  71.                 socket = server.accept();      
  72.                 socket.close();      
  73.                 hotDeploy(delpoyee);      
  74.             }      
  75.         } catch (IOException e) {      
  76.             // TODO Auto-generated catch block      
  77.             e.printStackTrace();      
  78.         }      
  79.     }      
  80.      
  81.     public static void main(String[] args) {      
  82.         // new MultiThreadTest().startService();      
  83.         // new MultiThreadTest().run();      
  84.         MultiThreadTest test = new MultiThreadTest();      
  85.         Thread thread = new Thread(test);      
  86.         thread.start();      
  87.         try {      
  88.             thread.sleep(sleepCycle);      
  89.             //Waiting for the deployment Thread deploy the say obj.      
  90.         } catch (InterruptedException e) {      
  91.             // TODO Auto-generated catch block      
  92.             e.printStackTrace();      
  93.         }      
  94.         test.startService();      
  95.     }      
  96.      
  97. }     
  98.   
  99. package com.cxz.classloader;   
  100.   
  101. import java.io.IOException;   
  102. import java.net.ServerSocket;   
  103. import java.net.Socket;   
  104.   
  105. import com.cxz.jiangyou.Say;   
  106.   
  107. public class MultiThreadTest implements Runnable {   
  108.   
  109.     private static final int portNum = 9090;   
  110.   
  111.     private static final int sleepCycle = 3000;   
  112.   
  113.     private Say sayer = null;   
  114.   
  115.     private ComplexClassLoader loader = new ComplexClassLoader();   
  116.   
  117.     private String delpoyee = "com.cxz.jiangyou.Sample";   
  118.   
  119.     public MultiThreadTest() {   
  120.         hotDeploy(delpoyee);   
  121.     }   
  122.   
  123.     public void startService() {   
  124.         while (true) {   
  125.             synchronized (sayer) {   
  126.                 sayer.say();   
  127.             }   
  128.             try {   
  129.                 Thread.sleep(sleepCycle);   
  130.             } catch (InterruptedException e) {   
  131.                 // TODO Auto-generated catch block   
  132.                 e.printStackTrace();   
  133.             }   
  134.         }   
  135.     }   
  136.   
  137.     public void hotDeploy(String name) {   
  138.         try {   
  139.             if (sayer != null) {   
  140.                 synchronized (sayer) {   
  141.                     loader = new ComplexClassLoader();   
  142.                     sayer = (Say) loader.loadClass(name).newInstance();   
  143.                 }   
  144.                 System.out.println("-------------->Hot deployment finished!");   
  145.             } else {   
  146.                 sayer = (Say) loader.loadClass(name).newInstance();   
  147.                 System.out.println("-------------->Initialization finished!");   
  148.             }   
  149.   
  150.         } catch (InstantiationException e) {   
  151.             // TODO Auto-generated catch block   
  152.             e.printStackTrace();   
  153.         } catch (IllegalAccessException e) {   
  154.             // TODO Auto-generated catch block   
  155.             e.printStackTrace();   
  156.         } catch (ClassNotFoundException e) {   
  157.             // TODO Auto-generated catch block   
  158.             e.printStackTrace();   
  159.         }   
  160.     }   
  161.   
  162.     @Override  
  163.     public void run() {   
  164.         ServerSocket server = null;   
  165.         Socket socket = null;   
  166.         try {   
  167.             server = new ServerSocket(portNum);   
  168.             while (true) {   
  169.                 socket = server.accept();   
  170.                 socket.close();   
  171.                 hotDeploy(delpoyee);   
  172.             }   
  173.         } catch (IOException e) {   
  174.             // TODO Auto-generated catch block   
  175.             e.printStackTrace();   
  176.         }   
  177.     }   
  178.   
  179.     public static void main(String[] args) {   
  180.         // new MultiThreadTest().startService();   
  181.         // new MultiThreadTest().run();   
  182.         MultiThreadTest test = new MultiThreadTest();   
  183.         Thread thread = new Thread(test);   
  184.         thread.start();   
  185.         try {   
  186.             thread.sleep(sleepCycle);   
  187.             //Waiting for the deployment Thread deploy the say obj.   
  188.         } catch (InterruptedException e) {   
  189.             // TODO Auto-generated catch block   
  190.             e.printStackTrace();   
  191.         }   
  192.         test.startService();   
  193.     }   
  194.   
  195. }  
package com.cxz.classloader;   
  
import java.io.IOException;   
import java.net.ServerSocket;   
import java.net.Socket;   
  
import com.cxz.jiangyou.Say;   
  
public class MultiThreadTest implements Runnable {   
  
    private static final int portNum = 9090;   
  
    private static final int sleepCycle = 3000;   
  
    private Say sayer = null;   
  
    private ComplexClassLoader loader = new ComplexClassLoader();   
  
    private String delpoyee = "com.cxz.jiangyou.Sample";   
  
    public MultiThreadTest() {   
        hotDeploy(delpoyee);   
    }   
  
    public void startService() {   
        while (true) {   
            synchronized (sayer) {   
                sayer.say();   
            }   
            try {   
                Thread.sleep(sleepCycle);   
            } catch (InterruptedException e) {   
                // TODO Auto-generated catch block   
                e.printStackTrace();   
            }   
        }   
    }   
  
    public void hotDeploy(String name) {   
        try {   
            if (sayer != null) {   
                synchronized (sayer) {   
                    loader = new ComplexClassLoader();   
                    sayer = (Say) loader.loadClass(name).newInstance();   
                }   
                System.out.println("-------------->Hot deployment finished!");   
            } else {   
                sayer = (Say) loader.loadClass(name).newInstance();   
                System.out.println("-------------->Initialization finished!");   
            }   
  
        } catch (InstantiationException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        } catch (IllegalAccessException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        } catch (ClassNotFoundException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
    }   
  
    @Override  
    public void run() {   
        ServerSocket server = null;   
        Socket socket = null;   
        try {   
            server = new ServerSocket(portNum);   
            while (true) {   
                socket = server.accept();   
                socket.close();   
                hotDeploy(delpoyee);   
            }   
        } catch (IOException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
    }   
  
    public static void main(String[] args) {   
        // new MultiThreadTest().startService();   
        // new MultiThreadTest().run();   
        MultiThreadTest test = new MultiThreadTest();   
        Thread thread = new Thread(test);   
        thread.start();   
        try {   
            thread.sleep(sleepCycle);   
            //Waiting for the deployment Thread deploy the say obj.   
        } catch (InterruptedException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
        test.startService();   
    }   
  
}  

package com.cxz.classloader;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import com.cxz.jiangyou.Say;

public class MultiThreadTest implements Runnable {

	private static final int portNum = 9090;

	private static final int sleepCycle = 3000;

	private Say sayer = null;

	private ComplexClassLoader loader = new ComplexClassLoader();

	private String delpoyee = "com.cxz.jiangyou.Sample";

	public MultiThreadTest() {
		hotDeploy(delpoyee);
	}

	public void startService() {
		while (true) {
			synchronized (sayer) {
				sayer.say();
			}
			try {
				Thread.sleep(sleepCycle);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void hotDeploy(String name) {
		try {
			if (sayer != null) {
				synchronized (sayer) {
					loader = new ComplexClassLoader();
					sayer = (Say) loader.loadClass(name).newInstance();
				}
				System.out.println("-------------->Hot deployment finished!");
			} else {
				sayer = (Say) loader.loadClass(name).newInstance();
				System.out.println("-------------->Initialization finished!");
			}

		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		ServerSocket server = null;
		Socket socket = null;
		try {
			server = new ServerSocket(portNum);
			while (true) {
				socket = server.accept();
				socket.close();
				hotDeploy(delpoyee);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		// new MultiThreadTest().startService();
		// new MultiThreadTest().run();
		MultiThreadTest test = new MultiThreadTest();
		Thread thread = new Thread(test);
		thread.start();
		try {
			thread.sleep(sleepCycle);
			//Waiting for the deployment Thread deploy the say obj.
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		test.startService();
	}

}


main线程主要是服务线程,通过调用startService()不停的通过system.out来打印。支线程负责监听端口(9090),当有连接信号后就重新加载类。
服务接口很简单,如下

Java代码 复制代码
  1. Java代码    
  2. package com.cxz.jiangyou;      
  3. public interface Say{      
  4.     public void say();      
  5. }     
  6.   
  7. package com.cxz.jiangyou;   
  8. public interface Say{   
  9.     public void say();   
  10. }  
Java代码 
package com.cxz.jiangyou;   
public interface Say{   
    public void say();   
}  

package com.cxz.jiangyou;
public interface Say{
	public void say();
}




总结:所有的customerClassLoader都要加载与之相关的类(比如:父类、包含的类)。如果你需要override loadclass(string, boolean)绕过findLoadedClass()检测,只能引发java.lang.LinkageError:duplicate class definition for name: "com/cxz/jiangyou/Sample"因此,比较通用的重新加载方式应该就是new一个用户定义的classloader

原文地址:https://www.cnblogs.com/liaomin416100569/p/9331833.html