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,然后生成实例,处理请求。
下面我附上相关核心代码:
- public abstract class CachedClassLoader extends ClassLoader {
- private final static Log logger = LogFactory.getLog(CachedClassLoader.class);
- protected HashMap<String,Class<?>> cache = null;
- protected String classname;
- protected String path;
- public String getPath() {
- return path;
- }
- public CachedClassLoader(String path, String classname) {
- super();
- this.classname = classname;
- this.path = path;
- this.cache = new HashMap<String,Class<?>>();
- }
- /**
- * Loads the class with the specified name.
- * @param name: classname.
- */
- public synchronized Class<?> loadClass(String classname, boolean resolve) {
- if (this.cache.containsKey(classname)) {
- logger.debug("load Class:" + classname + " from cache.");
- Class<?> c = this.cache.get(classname);
- if (resolve)
- resolveClass(c);
- return c;
- } else {
- try {
- Class<?> c = Class.forName(classname);
- return c;
- }
- catch (ClassNotFoundException e) {
- Class<?> c = this.newClass(classname);
- if (c == null)
- return null;
- this.cache.put(classname, c);
- if (resolve)
- resolveClass(c);
- return c;
- }
- catch (NoClassDefFoundError e) {
- Class<?> c = this.newClass(classname);
- if (c == null)
- return null;
- this.cache.put(classname, c);
- if (resolve)
- resolveClass(c);
- return c;
- }
- }
- }
- public synchronized Class<?> getClass(String classname){
- return this.cache.get(classname);
- }
- /**
- * @return java.lang.Class
- * @param name
- * @param resolve
- */
- public synchronized Class<?> loadClass(boolean resolve) {
- return this.loadClass(this.classname, resolve);
- }
- /**
- * Abstract method for create new class object.
- * @param classname
- * @return
- */
- abstract Class<?> newClass(String classname);
- public String getClassname() {
- return classname;
- }
- public void setClassname(String classname) {
- this.classname = classname;
- }
- }
- public class FileClassLoader extends CachedClassLoader{
- private static Log logger =LogFactory.getLog(FileClassLoader.class);
- public String CLASSPATH_ROOT=TClassLoaderFactory.getFactory().getPropertyValue(TClassLoaderFactory.FILEROOT_PATH);
- public FileClassLoader (String path,String classname) {
- super(path, classname);
- }
- /**
- * Implements CachedClassLoader.newClass method.
- * @param classname
- */
- protected Class<?> newClass(String classname) {
- String fullpath = CLASSPATH_ROOT+File.separator+this.path+File.separator+classname + ".class";
- logger.debug("loading remote class " + classname + " from "+ fullpath);
- byte data[] = loadClassData(fullpath);
- if (data == null) {
- logger.debug("Class data is null");
- return null;
- }
- logger.debug("defining class " + classname);
- try {
- return super.defineClass(this.path.replaceAll("\\\\", ".")+"."+classname, data, 0, data.length);
- } catch (Exception e) {
- logger.error("Init class exception",e);
- }
- return null;
- }
- /**
- * Read class as a byts array.
- * @return byte[]
- * @param name
- */
- private byte[] loadClassData(String urlString) {
- logger.debug("loadClassData by:"+urlString);
- try {
- //return byteOutput.toByteArray();
- FileInputStream in =new FileInputStream(urlString);
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- FileChannel channel =in.getChannel();
- WritableByteChannel outchannel = Channels.newChannel(out);
- ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
- while (true) {
- int i = channel.read(buffer);
- if (i == 0 || i == -1) {
- break;
- }
- buffer.flip();
- outchannel.write(buffer);
- buffer.clear();
- }
- byte[] bytes =out.toByteArray();
- out.close();
- in.close();
- return bytes;
- } catch (IOException ie) {
- logger.error("read local file exception "+urlString, ie);
- }
- return null;
- }
- /**
- * Load spec file from FileClassLoader's rootpath.
- * @param name resource's name.
- */
- public InputStream getResourceAsStream(String name) {
- String fullpath = CLASSPATH_ROOT+File.separator+this.path+File.separator+name;
- logger.debug("load resource from:"+fullpath);
- try {
- return new FileInputStream(fullpath);
- }
- catch(FileNotFoundException fe) {
- logger.error("spec:"+fullpath,fe);
- return null;
- }
- }
- }
- public class ClassLoaderFactory {
- private static Log logger =LogFactory.getLog(ClassLoaderFactory.class);
- public static final String LOADER_NAME = "classloader.name";
- public static final String NETROOT_URL = "classloader.NetworkClassLoader";
- public static final String FILEROOT_PATH = "classloader.FileClassLoader";
- public static final String PREVIOUS_ON_FILE="classloader.FileClassLoader.PRELOAD";
- private Map<String,ClassLoader> loaderMap = null;
- private static ClassLoaderFactory factory = new ClassLoaderFactory();
- public Properties conf = new Properties();
- private ClassLoaderFactory(){
- this.loaderMap = new ConcurrentHashMap<String,ClassLoader>();
- InputStream in = FileClassLoader.class.getResourceAsStream("/*****.properties");
- try {
- conf.load(in);
- logger.debug("ClassLoaderFactory init:"+LOADER_NAME +this.conf.getProperty(LOADER_NAME) );
- logger.debug("ClassLoaderFactory init:"+NETROOT_URL + this.conf.getProperty(NETROOT_URL) );
- logger.debug("ClassLoaderFactory init:"+FILEROOT_PATH + this.conf.getProperty(FILEROOT_PATH));
- }
- catch(IOException ie) {
- logger.error("Init classpath exception",ie);
- }
- }
- /**
- * Implements factory pattern.
- * @return
- */
- public static ClassLoaderFactory getFactory() {
- return factory;
- }
- protected String getPropertyValue(String propertyName) {
- return this.conf.getProperty(propertyName);
- }
- /**
- * Create new classloader object for this wrapper. default classloader is FileClassLoader.
- * @param key
- * @param classname
- * @return
- */
- public ClassLoader getClassLoader(String key,String classname) {
- long startTime = System.currentTimeMillis();
- String loaderKey = key;
- if(!this.loaderMap.containsKey(loaderKey)){
- synchronized(this.loaderMap) {
- if(this.loaderMap.containsKey(loaderKey)){
- return (ClassLoader)this.loaderMap.get(loaderKey);
- }
- try {
- Class<?> cl = Class.forName(this.conf.getProperty(LOADER_NAME));
- Class<?>[] params = {String.class,String.class};
- Constructor<?> constructor = cl.getConstructor(params);
- String[] args = {key,classname};
- logger.info("create new ClassLoader for:"+key+" classname:"+classname+" consume:"+(System.currentTimeMillis()-startTime)+" (ms)");
- this.loaderMap.put(loaderKey, (ClassLoader)constructor.newInstance(args));
- }
- catch(ClassNotFoundException cne) {
- logger.error("init classloader failed. system occure fetal error.!!!"+key+" codename:"+classname, cne);
- }
- catch(NoSuchMethodException nme) {
- logger.error("key:"+key+" classname:"+classname+ "get classloader failed.",nme);
- }
- catch(Exception e){
- logger.error("key:"+key+" classname:"+classname+ "get classloader failed.",e);
- }
- }
- }else {
- //(ClassLoader)this.loaderMap.get(loaderKey);
- CachedClassLoader loader =(CachedClassLoader)this.loaderMap.get(loaderKey);
- loader.setClassname(classname);
- logger.debug("retrieve classloader from cache map, key:"+key+" classname:"+classname);
- }
- return (ClassLoader)this.loaderMap.get(loaderKey);
- }
- public void reload(String key){
- if(loaderMap.containsKey(key)){
- synchronized(this.loaderMap) {
- loaderMap.remove(key);
- logger.info("Wrapper classes for key:"+key+ " were removed!");
- }
- }
- }
- public void reloadAll() {
- synchronized (this.loaderMap) {
- loaderMap.clear();
- logger.info("Wrapper classes for all key were removed!");
- }
- }
- }
- /**
- *
- * @author xinchun.wang
- @email: 532002108@qq.com
- * @createTime 2015-4-4 下午9:54:12
- */
- @Controller
- @RequestMapping("test")
- public class TestController {
- private final Logger logger = LoggerFactory.getLogger(getClass());
- private ClassLoaderFactory classLoaderFactory = TClassLoaderFactory.getFactory();
- private static final String path = "com"+File.separator+"gym"+File.separator+"backadmin"+File.separator+"service"+File.separator+"user";
- @SuppressWarnings("unchecked")
- @RequestMapping("getData")
- @ResponseBody
- public Map<String, Object> getData() throws Exception {
- logger.info("enter getData");
- CachedClassLoader loader = (CachedClassLoader)classLoaderFactory.getClassLoader(path, "BasicUserService");
- Class<UserService> userServiceClass = (Class<UserService>)loader.getClass("BasicUserService");
- UserService userService = userServiceClass.newInstance();
- System.out.println(userService.getClass().getClassLoader());
- Map<String, Object> model = userService.getUser();
- logger.info("exit getData");
- return model;
- }
- @RequestMapping("reload")
- @ResponseBody
- public Map<String, Object> reload(String classname) throws Exception {
- Map<String, Object> model = new HashMap<String,Object>();
- try{
- classLoaderFactory.reload(path);
- CachedClassLoader loader = (CachedClassLoader)classLoaderFactory.getClassLoader(path, "BasicUserService");
- loader.loadClass(classname);
- model.put("ret", "success");
- }catch(Exception e){
- logger.error("",e);
- model.put("ret", e);
- }
- return model;
- }
- }
- /**
- *
- * @author xinchun.wang
- @email: 532002108@qq.com
- */
- public class BasicUserService implements UserService {
- public Map<String, Object> getUser() {
- Map<String, Object> model = new HashMap<String, Object>();
- model.put("username", "ooooooooooooo");
- return model;
- }
- }
测试:随意更改BasicUserService 的实现,然后调用reload。
相关推荐
通过自定义类加载器,我们可以实现热替换,提高开发效率。 在“classloader-example-master”压缩包中,可能包含了以下内容: 1. 源代码文件:展示了如何编写自定义类加载器的Java代码。 2. 测试用例:用于演示如何...
Java类热替换技术通常涉及类加载器(ClassLoader)的自定义以及字节码处理。本资料将通过源码分析,讲解如何实现这一功能。 首先,我们需要理解Java的类加载机制。在Java中,类加载器负责查找并加载类的字节码文件...
1. **热更新实现**:通过自定义ClassLoader,可以在不重启服务器的情况下,加载新的类文件替换旧的类文件。 2. **多租户隔离**:淘宝网存在大量的商户和服务,每个服务都可能有自己的类加载器,这样可以有效地隔离...
热更新通常依赖于类加载器(Classloader)的工作原理来实现,本篇文章将深入探讨如何利用Android的类加载器实现热更新的机制。 首先,我们需要理解什么是类加载器。在Java和Android中,类加载器是负责查找、加载和...
eclipse工程格式 博文链接:https://aga.iteye.com/blog/200818
热部署的基本思想是当源代码发生变化时,能够即时替换已加载的类,而无需重启应用。Java提供了一些工具和框架,如JRebel、DCEVM等,来实现这一目标。然而,热部署并不是没有缺陷的。其中一个问题在于类加载器的双亲...
通过自定义的 ClassLoader,我们可以实现类的热替换,例如,在 Web 服务器中,我们可以使用自定义的 ClassLoader 来实现热替换,以提高 Web 服务器的性能。 在 ClassLoader 中,还有一个重要的方法是 ...
本示例项目"my_classloader_demo-master.7z"聚焦于通过Native层实现热修复,这涉及到Android系统的核心组件以及对Java虚拟机(JVM)的深入理解。 1. **类加载器(ClassLoader)**: 在Java编程中,类加载器是负责将类...
总结来说,安卓原生热更新是通过动态替换`ClassLoader`来加载新的`.dex`文件,进而实现应用的热更新。这一过程涉及到应用的编译、传输、存储以及运行时的类加载机制。理解并熟练掌握这些步骤,开发者能够在不打扰...
1. **安全性**: 如果没有双亲委托机制,那么自定义的类加载器就可以轻易地替换掉核心类库中的类,这将带来极大的安全隐患。 2. **避免重复加载**: 由于每个类加载器都有一个缓存机制,因此在同一个类加载器环境下...
3. **加载更新**:下载完成后,框架会负责将新.dex文件加载到内存中,并替换或追加到现有的应用 DexClassLoader 中。这里涉及到DexClassLoader的使用,它是ClassLoader的一个子类,用于加载.dex格式的字节码。 4. *...
3. 动态加载:创建自己的类加载器,尝试动态加载新的类,实现代码的热替换。 4. 类隔离:通过自定义ClassLoader实现不同模块之间的类隔离,避免类冲突。 总的来说,深入理解ClassLoader的工作原理对于优化程序性能...
开发者可以通过继承`java.lang.ClassLoader`创建自己的类加载器,实现特定的类加载逻辑,如动态加载、代码热替换(HotSwap)等功能。HotSwap允许在JVM运行时替换已加载的类,从而无需重启应用就能更新代码,提高了...
这篇博客探讨了如何结合Classloader实现热部署。 首先,理解类加载器的工作机制至关重要。Java中的类加载分为五个阶段:加载、验证、准备、解析和初始化。每个类加载器都有自己的命名空间,避免了类名冲突。Java的...
Android热修复的核心在于替换或修补运行时应用中的问题代码,而实现这一目标的关键是类加载器(ClassLoader)。在Java和Android系统中,类加载器负责查找、加载和初始化类。通过自定义类加载器,我们可以实现动态...
深入理解ClassLoader是深入理解Java动态加载机制、自定义类加载器以及对类的热部署等高级特性的重要基础。 在Java中,ClassLoader遵循的是委托模型(Delegation Model)。当一个类加载器接收到一个类加载请求时,它...
3. **动态加载的优势**:通过动态加载,开发者可以在程序运行时加载新的功能或替换现有功能,增强了软件的可扩展性和灵活性。例如,插件系统、热更新等场景广泛使用了动态加载技术。 4. **类加载策略**:根据实际...
- 自定义ClassLoader在插件系统、热部署、模块化系统等领域有广泛应用。 5. **类的生命周期** - 加载:通过ClassLoader找到对应的.class文件。 - 链接:验证、准备、解析三个步骤。 - 初始化:执行类的静态初始...
- 源码分析是理解热更新机制的关键,包括ClassLoader的继承结构、 DexFile和DexOpt接口的使用,以及如何在运行时安全地替换或加载新的Dex。 5. **安全性与兼容性** - 热更新需确保安全性,防止恶意代码注入,同时...