在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

ClassLoader案例

2016-12-13 12:56| 发布者: zhangjf| 查看: 579| 评论: 0

摘要: 接自定义类加载器的理论,讲一个实践。 我们都有使用jsp的经验,为什么jsp可以修改后直接生效?就是ClassLoader在起作用,一个jsp对应一个ClassLoader,一旦jsp修改,就需要卸载原来加载此jsp(先是被转换为java文 ...
接自定义类加载器的理论,讲一个实践。

我们都有使用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,然后生成实例,处理请求。

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

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


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


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


  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. private ClassLoaderFactory classLoaderFactory = TClassLoaderFactory.getFactory();
  12. private static final String path = "com"+File.separator+"gym"+File.separator+"backadmin"+File.separator+"service"+File.separator+"user";
  13. @SuppressWarnings("unchecked")
  14. @RequestMapping("getData")
  15. @ResponseBody
  16. public Map<String, Object> getData() throws Exception {
  17. logger.info("enter getData");
  18. CachedClassLoader loader = (CachedClassLoader)classLoaderFactory.getClassLoader(path, "BasicUserService");
  19. Class<UserService> userServiceClass = (Class<UserService>)loader.getClass("BasicUserService");
  20. UserService userService = userServiceClass.newInstance();
  21. System.out.println(userService.getClass().getClassLoader());
  22. Map<String, Object> model = userService.getUser();
  23. logger.info("exit getData");
  24. return model;
  25. }
  26. @RequestMapping("reload")
  27. @ResponseBody
  28. public Map<String, Object> reload(String classname) throws Exception {
  29. Map<String, Object> model = new HashMap<String,Object>();
  30. try{
  31. classLoaderFactory.reload(path);
  32. CachedClassLoader loader = (CachedClassLoader)classLoaderFactory.getClassLoader(path, "BasicUserService");
  33. loader.loadClass(classname);
  34. model.put("ret", "success");
  35. }catch(Exception e){
  36. logger.error("",e);
  37. model.put("ret", e);
  38. }
  39. return model;
  40. }
  41. }
  42. /**
  43. *
  44. * @author xinchun.wang
  45. @email: 532002108@qq.com
  46. */
  47. public class BasicUserService implements UserService {
  48. public Map<String, Object> getUser() {
  49. Map<String, Object> model = new HashMap<String, Object>();
  50. model.put("username", "ooooooooooooo");
  51. return model;
  52. }
  53. }
复制代码


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

来自:http://wangxinchun.iteye.com/blog/2199498

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-7-7 13:39

Copyright 2015-2025 djqfx

返回顶部