`

ClassLoader-热替换

阅读更多

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。

2
2
分享到:
评论

相关推荐

    classloader-example:博客帖子

    通过自定义类加载器,我们可以实现热替换,提高开发效率。 在“classloader-example-master”压缩包中,可能包含了以下内容: 1. 源代码文件:展示了如何编写自定义类加载器的Java代码。 2. 测试用例:用于演示如何...

    Java类热替换(热部署)源码

    Java类热替换技术通常涉及类加载器(ClassLoader)的自定义以及字节码处理。本资料将通过源码分析,讲解如何实现这一功能。 首先,我们需要理解Java的类加载机制。在Java中,类加载器负责查找并加载类的字节码文件...

    java classloader讲义-淘宝网

    1. **热更新实现**:通过自定义ClassLoader,可以在不重启服务器的情况下,加载新的类文件替换旧的类文件。 2. **多租户隔离**:淘宝网存在大量的商户和服务,每个服务都可能有自己的类加载器,这样可以有效地隔离...

    Android 使用classloader原理进行热更新

    热更新通常依赖于类加载器(Classloader)的工作原理来实现,本篇文章将深入探讨如何利用Android的类加载器实现热更新的机制。 首先,我们需要理解什么是类加载器。在Java和Android中,类加载器是负责查找、加载和...

    classloader做的一个热部署

    eclipse工程格式 博文链接:https://aga.iteye.com/blog/200818

    JAVA类加载器分析--热部署的缺陷(有代码示例,及分析)

    热部署的基本思想是当源代码发生变化时,能够即时替换已加载的类,而无需重启应用。Java提供了一些工具和框架,如JRebel、DCEVM等,来实现这一目标。然而,热部署并不是没有缺陷的。其中一个问题在于类加载器的双亲...

    Java ClassLoader学习总结

    通过自定义的 ClassLoader,我们可以实现类的热替换,例如,在 Web 服务器中,我们可以使用自定义的 ClassLoader 来实现热替换,以提高 Web 服务器的性能。 在 ClassLoader 中,还有一个重要的方法是 ...

    my_classloader_demo-master.7z Native层实现热修复

    本示例项目"my_classloader_demo-master.7z"聚焦于通过Native层实现热修复,这涉及到Android系统的核心组件以及对Java虚拟机(JVM)的深入理解。 1. **类加载器(ClassLoader)**: 在Java编程中,类加载器是负责将类...

    安卓原生热更新 classloader

    总结来说,安卓原生热更新是通过动态替换`ClassLoader`来加载新的`.dex`文件,进而实现应用的热更新。这一过程涉及到应用的编译、传输、存储以及运行时的类加载机制。理解并熟练掌握这些步骤,开发者能够在不打扰...

    ClassLoader

    1. **安全性**: 如果没有双亲委托机制,那么自定义的类加载器就可以轻易地替换掉核心类库中的类,这将带来极大的安全隐患。 2. **避免重复加载**: 由于每个类加载器都有一个缓存机制,因此在同一个类加载器环境下...

    Node.js-热更新开发框架App端

    3. **加载更新**:下载完成后,框架会负责将新.dex文件加载到内存中,并替换或追加到现有的应用 DexClassLoader 中。这里涉及到DexClassLoader的使用,它是ClassLoader的一个子类,用于加载.dex格式的字节码。 4. *...

    ClassLoader的 一些测试

    3. 动态加载:创建自己的类加载器,尝试动态加载新的类,实现代码的热替换。 4. 类隔离:通过自定义ClassLoader实现不同模块之间的类隔离,避免类冲突。 总的来说,深入理解ClassLoader的工作原理对于优化程序性能...

    classloader体系结构(含hotswap)

    开发者可以通过继承`java.lang.ClassLoader`创建自己的类加载器,实现特定的类加载逻辑,如动态加载、代码热替换(HotSwap)等功能。HotSwap允许在JVM运行时替换已加载的类,从而无需重启应用就能更新代码,提高了...

    classloader 热部署

    这篇博客探讨了如何结合Classloader实现热部署。 首先,理解类加载器的工作机制至关重要。Java中的类加载分为五个阶段:加载、验证、准备、解析和初始化。每个类加载器都有自己的命名空间,避免了类名冲突。Java的...

    Android-Android热修复框架基于类加载机制的代码修复

    Android热修复的核心在于替换或修补运行时应用中的问题代码,而实现这一目标的关键是类加载器(ClassLoader)。在Java和Android系统中,类加载器负责查找、加载和初始化类。通过自定义类加载器,我们可以实现动态...

    Java SE: ClassLoader in depth

    深入理解ClassLoader是深入理解Java动态加载机制、自定义类加载器以及对类的热部署等高级特性的重要基础。 在Java中,ClassLoader遵循的是委托模型(Delegation Model)。当一个类加载器接收到一个类加载请求时,它...

    使用classloader动态加载Class

    3. **动态加载的优势**:通过动态加载,开发者可以在程序运行时加载新的功能或替换现有功能,增强了软件的可扩展性和灵活性。例如,插件系统、热更新等场景广泛使用了动态加载技术。 4. **类加载策略**:根据实际...

    了解Java ClassLoader

    - 自定义ClassLoader在插件系统、热部署、模块化系统等领域有广泛应用。 5. **类的生命周期** - 加载:通过ClassLoader找到对应的.class文件。 - 链接:验证、准备、解析三个步骤。 - 初始化:执行类的静态初始...

    热更新(添加补丁—dex文件).xmind.zip

    - 源码分析是理解热更新机制的关键,包括ClassLoader的继承结构、 DexFile和DexOpt接口的使用,以及如何在运行时安全地替换或加载新的Dex。 5. **安全性与兼容性** - 热更新需确保安全性,防止恶意代码注入,同时...

Global site tag (gtag.js) - Google Analytics