欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ClassLoader-热替换 博客分类: java-jvm&性能&原理 CassLoader热部署 

程序员文章站 2024-02-23 19:06:40
...

https://www.ibm.com/developerworks/cn/java/l-multithreading/

接自定义类加载器的理论,讲一个实践。

我们都有使用jsp的经验,为什么jsp可以修改后直接生效?就是ClassLoader在起作用,一个jsp对应一个ClassLoader,一旦jsp修改,就需要卸载原来加载此jsp(先是被转换为java文件,然后被编译为class文件)的ClassLoader ,然后重新生成一个ClassLoader来加载jsp对应的class文件。

最近参与到了一个抓取垂直网站报价数据的设计,由于抓取的网站有上千之多,并且每天都有大量的网站更新,导致原来的java解析代码必须修改以适应原来的功能。

数据抓取有两步:
1、抓取数据页面(html,或者json串)
2、解析数据

这样我们的系统,对每一要抓取的个网站建一个类,根据条件调用不同的类对象,问题来了:如果修改其中一个网站的类,如何生效? 重新启动tomcat当然是可以的,不过代价过高,也不可取。 
想必大家想到了jsp和tomcat交互的方式:通过对每一个类建立一个ClassLoader对象,如果某个类更新了,上传class文件到特定目录下,重新加载一个ClassLoader对象,由新的ClassLoader来加载class,然后生成实例,处理请求。

下面我附上相关核心代码:

Java代码 
  1. public abstract class CachedClassLoader extends ClassLoader {  
  2.   
  3.     private final static Log logger = LogFactory.getLog(CachedClassLoader.class);  
  4.     protected HashMap<String,Class<?>> cache = null;  
  5.     protected String classname;  
  6.     protected String path;  
  7.   
  8.     public String getPath() {  
  9.         return path;  
  10.     }  
  11.   
  12.     public CachedClassLoader(String path, String classname) {  
  13.         super();  
  14.         this.classname = classname;  
  15.         this.path = path;  
  16.         this.cache = new HashMap<String,Class<?>>();  
  17.     }  
  18.   
  19.     /** 
  20.      * Loads the class with the specified name. 
  21.      * @param name: classname. 
  22.      */  
  23.     public synchronized Class<?> loadClass(String classname, boolean resolve) {  
  24.         if (this.cache.containsKey(classname)) {  
  25.             logger.debug("load Class:" + classname + " from cache.");  
  26.             Class<?> c =  this.cache.get(classname);  
  27.             if (resolve)  
  28.                 resolveClass(c);  
  29.             return c;  
  30.         } else {  
  31.             try {  
  32.                 Class<?> c = Class.forName(classname);  
  33.                 return c;  
  34.             }  
  35.             catch (ClassNotFoundException e) {  
  36.                 Class<?> c = this.newClass(classname);  
  37.                 if (c == null)  
  38.                     return null;  
  39.                 this.cache.put(classname, c);  
  40.                 if (resolve)  
  41.                     resolveClass(c);  
  42.                 return c;  
  43.             }  
  44.             catch (NoClassDefFoundError e) {  
  45.                 Class<?> c = this.newClass(classname);  
  46.                 if (c == null)  
  47.                     return null;  
  48.                 this.cache.put(classname, c);  
  49.                 if (resolve)  
  50.                     resolveClass(c);  
  51.                 return c;  
  52.             }  
  53.         }  
  54.     }  
  55.       
  56.     public synchronized Class<?> getClass(String classname){  
  57.         return this.cache.get(classname);  
  58.     }  
  59.       
  60.     /** 
  61.      * @return java.lang.Class 
  62.      * @param name 
  63.      * @param resolve 
  64.      */  
  65.     public synchronized Class<?> loadClass(boolean resolve) {  
  66.         return this.loadClass(this.classname, resolve);  
  67.     }  
  68.   
  69.     /** 
  70.      * Abstract method for create new class object. 
  71.      * @param classname 
  72.      * @return 
  73.      */  
  74.     abstract Class<?> newClass(String classname);  
  75.   
  76.     public String getClassname() {  
  77.         return classname;  
  78.     }  
  79.   
  80.     public void setClassname(String classname) {  
  81.         this.classname = classname;  
  82.     }  
  83. }  



Java代码 
  1. public class FileClassLoader extends CachedClassLoader{   
  2.     private static Log logger =LogFactory.getLog(FileClassLoader.class);  
  3.     public String CLASSPATH_ROOT=TClassLoaderFactory.getFactory().getPropertyValue(TClassLoaderFactory.FILEROOT_PATH);  
  4.       
  5.     public FileClassLoader (String path,String classname) {  
  6.         super(path, classname);  
  7.     }  
  8.           
  9.     /** 
  10.      * Implements CachedClassLoader.newClass method. 
  11.      * @param classname 
  12.      */  
  13.    protected  Class<?> newClass(String classname) {  
  14.         String fullpath = CLASSPATH_ROOT+File.separator+this.path+File.separator+classname + ".class";  
  15.         logger.debug("loading remote class " + classname + " from "+ fullpath);  
  16.         byte data[] = loadClassData(fullpath);  
  17.         if (data == null) {  
  18.             logger.debug("Class data is null");  
  19.             return null;  
  20.         }  
  21.         logger.debug("defining class " + classname);  
  22.         try {  
  23.             return super.defineClass(this.path.replaceAll("\\\\", ".")+"."+classname, data, 0, data.length);  
  24.         } catch (Exception e) {  
  25.             logger.error("Init class exception",e);  
  26.         }  
  27.         return null;  
  28.     }  
  29.   
  30.     /** 
  31.      * Read class as a byts array. 
  32.      * @return byte[] 
  33.      * @param name 
  34.      */  
  35.     private byte[] loadClassData(String urlString) {  
  36.         logger.debug("loadClassData by:"+urlString);  
  37.         try {             
  38.             //return byteOutput.toByteArray();  
  39.             FileInputStream in =new FileInputStream(urlString);           
  40.             ByteArrayOutputStream out = new ByteArrayOutputStream();  
  41.             FileChannel channel =in.getChannel();              
  42.             WritableByteChannel outchannel = Channels.newChannel(out);   
  43.             ByteBuffer buffer = ByteBuffer.allocateDirect(1024);   
  44.             while (true) {   
  45.                 int i = channel.read(buffer);   
  46.                 if (i == 0 || i == -1) {   
  47.                     break;   
  48.                 }   
  49.                 buffer.flip();   
  50.                 outchannel.write(buffer);   
  51.                 buffer.clear();   
  52.             }               
  53.             byte[] bytes =out.toByteArray();  
  54.             out.close();  
  55.             in.close();  
  56.             return bytes;  
  57.         } catch (IOException ie) {  
  58.             logger.error("read local file exception "+urlString, ie);  
  59.         }  
  60.         return null;  
  61.     }  
  62.       
  63.     /** 
  64.      * Load spec file from FileClassLoader's  rootpath. 
  65.      * @param name resource's name. 
  66.      */  
  67.     public InputStream getResourceAsStream(String name) {  
  68.         String fullpath = CLASSPATH_ROOT+File.separator+this.path+File.separator+name;  
  69.         logger.debug("load resource from:"+fullpath);  
  70.         try {  
  71.             return new FileInputStream(fullpath);  
  72.         }  
  73.         catch(FileNotFoundException fe) {  
  74.             logger.error("spec:"+fullpath,fe);  
  75.             return null;  
  76.         }  
  77.     }  
  78. }  



Java代码 
  1. public class ClassLoaderFactory {  
  2.       
  3.     private static Log logger =LogFactory.getLog(ClassLoaderFactory.class);  
  4.       
  5.     public static final String  LOADER_NAME = "classloader.name";  
  6.     public static final String  NETROOT_URL = "classloader.NetworkClassLoader";  
  7.     public static final String  FILEROOT_PATH = "classloader.FileClassLoader";  
  8.     public static final String PREVIOUS_ON_FILE="classloader.FileClassLoader.PRELOAD";  
  9.       
  10.     private Map<String,ClassLoader> loaderMap = null;  
  11.       
  12.     private static ClassLoaderFactory factory = new ClassLoaderFactory();  
  13.       
  14.     public Properties conf = new Properties();  
  15.       
  16.     private ClassLoaderFactory(){         
  17.         this.loaderMap = new ConcurrentHashMap<String,ClassLoader>();  
  18.         InputStream in = FileClassLoader.class.getResourceAsStream("/*****.properties");  
  19.         try {  
  20.             conf.load(in);  
  21.             logger.debug("ClassLoaderFactory init:"+LOADER_NAME +this.conf.getProperty(LOADER_NAME) );  
  22.             logger.debug("ClassLoaderFactory init:"+NETROOT_URL + this.conf.getProperty(NETROOT_URL) );  
  23.             logger.debug("ClassLoaderFactory init:"+FILEROOT_PATH  + this.conf.getProperty(FILEROOT_PATH));  
  24.         }  
  25.         catch(IOException ie) {  
  26.             logger.error("Init classpath exception",ie);  
  27.         }  
  28.     }  
  29.       
  30.     /**  
  31.      * Implements factory pattern.  
  32.      * @return  
  33.      */  
  34.     public static ClassLoaderFactory getFactory() {  
  35.         return factory;  
  36.     }  
  37.       
  38.     protected String getPropertyValue(String propertyName) {  
  39.         return this.conf.getProperty(propertyName);  
  40.     }  
  41.     /** 
  42.      * Create new classloader object for this wrapper. default classloader is FileClassLoader. 
  43.      * @param key 
  44.      * @param classname 
  45.      * @return 
  46.      */  
  47.     public ClassLoader getClassLoader(String key,String classname) {  
  48.         long startTime = System.currentTimeMillis();  
  49.         String loaderKey = key;  
  50.         if(!this.loaderMap.containsKey(loaderKey)){  
  51.             synchronized(this.loaderMap) {  
  52.                 if(this.loaderMap.containsKey(loaderKey)){  
  53.                     return (ClassLoader)this.loaderMap.get(loaderKey);  
  54.                 }  
  55.                 try {  
  56.                     Class<?> cl = Class.forName(this.conf.getProperty(LOADER_NAME));  
  57.                     Class<?>[] params = {String.class,String.class};  
  58.                     Constructor<?> constructor = cl.getConstructor(params);  
  59.                     String[] args = {key,classname};  
  60.                     logger.info("create new ClassLoader for:"+key+" classname:"+classname+" consume:"+(System.currentTimeMillis()-startTime)+" (ms)");  
  61.                     this.loaderMap.put(loaderKey, (ClassLoader)constructor.newInstance(args));  
  62.                 }  
  63.                 catch(ClassNotFoundException cne) {  
  64.                     logger.error("init classloader failed. system occure fetal error.!!!"+key+" codename:"+classname, cne);  
  65.                 }  
  66.                 catch(NoSuchMethodException nme) {  
  67.                     logger.error("key:"+key+" classname:"+classname+ "get classloader failed.",nme);  
  68.                 }  
  69.                 catch(Exception e){  
  70.                     logger.error("key:"+key+" classname:"+classname+ "get classloader failed.",e);  
  71.                 }  
  72.             }  
  73.         }else {  
  74.             //(ClassLoader)this.loaderMap.get(loaderKey);  
  75.             CachedClassLoader loader =(CachedClassLoader)this.loaderMap.get(loaderKey);  
  76.             loader.setClassname(classname);           
  77.             logger.debug("retrieve classloader from cache map, key:"+key+" classname:"+classname);  
  78.         }  
  79.         return (ClassLoader)this.loaderMap.get(loaderKey);  
  80.     }  
  81.       
  82.     public void reload(String key){  
  83.         if(loaderMap.containsKey(key)){  
  84.             synchronized(this.loaderMap) {  
  85.                 loaderMap.remove(key);  
  86.                 logger.info("Wrapper classes for key:"+key+ " were removed!");  
  87.             }  
  88.         }  
  89.     }  
  90.     public void reloadAll() {  
  91.         synchronized (this.loaderMap) {  
  92.             loaderMap.clear();  
  93.             logger.info("Wrapper classes for all key were removed!");  
  94.         }  
  95.     }  
  96.       
  97. }  



Java代码 
  1. /** 
  2.  *  
  3.  * @author xinchun.wang  
  4.    @email: 532002108@qq.com 
  5.  * @createTime 2015-4-4 下午9:54:12 
  6.  */  
  7. @Controller  
  8. @RequestMapping("test")  
  9. public class TestController {  
  10.     private final Logger logger = LoggerFactory.getLogger(getClass());  
  11.   
  12.     private ClassLoaderFactory classLoaderFactory = TClassLoaderFactory.getFactory();  
  13.     private static final String path = "com"+File.separator+"gym"+File.separator+"backadmin"+File.separator+"service"+File.separator+"user";  
  14.       
  15.     @SuppressWarnings("unchecked")  
  16.     @RequestMapping("getData")  
  17.     @ResponseBody  
  18.     public Map<String, Object> getData() throws Exception {  
  19.         logger.info("enter getData");  
  20.         CachedClassLoader loader = (CachedClassLoader)classLoaderFactory.getClassLoader(path, "BasicUserService");  
  21.         Class<UserService> userServiceClass = (Class<UserService>)loader.getClass("BasicUserService");  
  22.         UserService userService = userServiceClass.newInstance();  
  23.         System.out.println(userService.getClass().getClassLoader());  
  24.         Map<String, Object> model = userService.getUser();  
  25.         logger.info("exit getData");  
  26.         return model;  
  27.     }  
  28.       
  29.       
  30.       
  31.     @RequestMapping("reload")  
  32.     @ResponseBody  
  33.     public Map<String, Object> reload(String classname) throws Exception {  
  34.         Map<String, Object> model = new HashMap<String,Object>();  
  35.         try{  
  36.             classLoaderFactory.reload(path);  
  37.             CachedClassLoader loader = (CachedClassLoader)classLoaderFactory.getClassLoader(path, "BasicUserService");  
  38.             loader.loadClass(classname);  
  39.             model.put("ret""success");  
  40.         }catch(Exception e){  
  41.             logger.error("",e);  
  42.             model.put("ret", e);  
  43.         }  
  44.         return model;  
  45.     }     
  46. }  
  47.   
  48. /** 
  49.  *  
  50.  * @author xinchun.wang  
  51.    @email: 532002108@qq.com 
  52.  */  
  53. public class BasicUserService implements UserService {  
  54.     public Map<String, Object> getUser() {  
  55.         Map<String, Object> model = new HashMap<String, Object>();  
  56.         model.put("username""ooooooooooooo");  
  57.         return model;  
  58.     }  
  59. }  



测试:随意更改BasicUserService 的实现,然后调用reload。