仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 924|回复: 10
打印 上一主题 下一主题

[学习教程] JAVA网页编程之深切探究 Java 热部署仓酷云

[复制链接]
因胸联盟 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-18 11:23:42 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
先谈谈我对java的一些认识。我选择java,是因为他语法简单,功能强大,从web,到桌面,到嵌入式,无所不能。但当我进一步了解了java后,感叹,java原来也有许多缺点。简介

在Java开辟范畴,热部署一向是一个难以办理的成绩,今朝的Java假造机只能完成办法体的修正热部署,关于全部类的布局修正,仍旧必要重启假造机,对类从头加载才干完成更新操纵。关于某些年夜型的使用来讲,每次的重启都必要消费大批的工夫本钱。固然osgi架构的呈现,让模块重启成为大概,可是假如模块之间有挪用干系的话,如许的操纵仍然会让使用呈现长久的功效性休克。本文将探究怎样在不损坏Java假造机现有举动的条件下,完成某个单一类的热部署,让体系无需重启就完成某个类的更新。
类加载的探究

起首谈一下作甚热部署(hotswap),热部署是在不重启Java假造机的条件下,能主动侦测到class文件的变更,更新运转时class的举动。Java类是经由过程Java假造机加载的,某个类的class文件在被classloader加载后,会天生对应的Class对象,以后就能够创立该类的实例。默许的假造机举动只会在启动时加载类,假如前期有一个类必要更新的话,纯真交换编译的class文件,Java假造机是不会更新正在运转的class。假如要完成热部署,最基本的体例是修正假造机的源代码,改动classloader的加载举动,使假造性能监听class文件的更新,从头加载class文件,如许的举动损坏性很年夜,为后续的JVM晋级埋下了一个年夜坑。
另外一种友爱的办法是创立本人的classloader来加载必要监听的class,如许就可以把持类加载的机会,从而完成热部署。本文将详细探究怎样完成这个计划。起首必要懂得一下Java假造机现有的加载机制。今朝的加载机制,称为双亲委派,体系在利用一个classloader来加载类时,会先扣问以后classloader的父类是不是有才能加载,假如父类没法完成加载操纵,才会将义务下放到该classloader来加载。这类自上而下的加载体例的优点是,让每一个classloader实行本人的加载义务,不会反复加载类。可是这类体例却使加载按次十分难改动,让自界说classloader争先加载必要监听改动的类成了一个困难。
不外我们能够换一个思绪,固然没法争先加载该类,可是仍旧能够用自界说classloader创立一个功效不异的类,让每次实例化的对象都指向这个新的类。当这个类的class文件产生改动的时分,再次创立一个更新的类,以后假如体系再次收回实例化哀求,创立的对象讲指向这个全新的类。
上面来复杂枚举一下必要做的事情。


  • 创立自界说的classloader,加载必要监听改动的类,在class文件产生改动的时分,从头加载该类。
  • 改动创立对象的举动,使他们在创立时利用自界说classloader加载的class。
自界说加载器的完成

自界说加载器仍旧必要实行类加载的功效。这里却存在一个成绩,统一个类加载器没法同时加载两个不异称号的类,因为不管类的布局怎样产生变更,天生的类名不会变,而classloader只能在假造机中断前烧毁已加载的类,如许classloader就没法加载更新后的类了。这里有一个小技能,让每次加载的类都保留成一个带有版本信息的class,好比加载Test.class时,保留在内存中的类是Test_v1.class,当类产生改动时,从头加载的类名是Test_v2.class。可是真正实行加载class文件创立class的defineClass办法是一个native的办法,修正起来又变得很坚苦。以是眼前还剩一条路,那就是间接修正编译天生的class文件。
使用ASM修正class文件

能够修正字节码的框架有良多,好比ASM,CGLIB。本文利用的是ASM。先来先容一下class文件的布局,class文件包括了以下几类信息,一个是类的基础信息,包括了会见权限信息,类名信息,父类信息,接口信息。第二个是类的变量信息。第三个是办法的信息。ASM会先加载一个class文件,然后严厉按次读取类的各项信息,用户能够依照本人的志愿界说加强组件修正这些信息,最初输入成一个新的class。
起首看一下怎样使用ASM修正类信息。
清单1.使用ASM修正字节码
  1. ClassWritercw=newClassWriter(ClassWriter.COMPUTE_MAXS);ClassReadercr=null;StringenhancedClassName=classSource.getEnhancedName();try{cr=newClassReader(newFileInputStream(classSource.getFile()));}catch(IOExceptione){e.printStackTrace();returnnull;}ClassVisitorcv=newEnhancedModifier(cw,className.replace(".","/"),enhancedClassName.replace(".","/"));cr.accept(cv,0);
复制代码
ASM修正字节码文件的流程是一个义务链形式,起首利用一个ClassReader读进字节码,然后使用ClassVisitor做本性化的修正,最初使用ClassWriter输入修正后的字节码。
之条件过,必要将读取的class文件的类名做一些修正,加载成一个全新名字的派生类。这里将之分为了2个步骤。
第一步,先将本来的类酿成接口。
清单2.重界说的原始类
  1. publicClass<?>redefineClass(StringclassName){ClassWritercw=newClassWriter(ClassWriter.COMPUTE_MAXS);ClassReadercr=null;ClassSourcecs=classFiles.get(className);if(cs==null){returnnull;}try{cr=newClassReader(newFileInputStream(cs.getFile()));}catch(IOExceptione){e.printStackTrace();returnnull;}ClassModifiercm=newClassModifier(cw);cr.accept(cm,0);byte[]code=cw.toByteArray();returndefineClass(className,code,0,code.length);}
复制代码
起首load原始类的class文件,此处界说了一个加强组件ClassModifier,感化是修正原始类的范例,将它转换成接口。原始类的一切办法逻辑城市被往失落。
第二步,天生的派生类都完成这个接口,即原始类,而且复制原始类中的一切办法逻辑。以后假如该类必要更新,会天生一个新的派生类,也会完成这个接口。如许做的目标是不管怎样修正,统一个class的派生类都有一个配合的接口,他们之间的转换变得对外不通明。
清单3.界说一个派生类
  1. //在class文件产生改动时从头界说这个类privateClass<?>redefineClass(StringclassName,ClassSourceclassSource){ClassWritercw=newClassWriter(ClassWriter.COMPUTE_MAXS);ClassReadercr=null;classSource.update();StringenhancedClassName=classSource.getEnhancedName();try{cr=newClassReader(newFileInputStream(classSource.getFile()));}catch(IOExceptione){e.printStackTrace();returnnull;}EnhancedModifierem=newEnhancedModifier(cw,className.replace(".","/"),enhancedClassName.replace(".","/"));ExtendModifierexm=newExtendModifier(em,className.replace(".","/"),enhancedClassName.replace(".","/"));cr.accept(exm,0);byte[]code=cw.toByteArray();classSource.setByteCopy(code);Class<?>clazz=defineClass(enhancedClassName,code,0,code.length);classSource.setClassCopy(clazz);returnclazz;}
复制代码
再次load原始类的class文件,此处界说了两个加强组件,一个是EnhancedModifier,这个加强组件的感化是改动原本的类名。第二个加强组件是ExtendModifier,这个加强组件的感化是改动原有类的父类,让这个修正后的派生类可以完成统一个原始类(此时原始类已转成接口了)。
自界说classloader另有一个感化是监听会产生改动的class文件,classloader会办理一个准时器,准时顺次扫描这些class文件是不是改动。
改动创立对象的举动

Java假造机罕见的创立对象的办法有两种,一种是静态创立,间接new一个对象,一种是静态创立,经由过程反射的办法,创立对象。
因为已在自界说加载器中变动了原有类的范例,把它从类改成了接口,以是这两种创立办法都没法建立。我们要做的是将实例化原始类的举动酿成实例化派生类。
关于第一种办法,必要做的是将静态创立,变成经由过程classloader猎取class,然后静态创立该对象。
清单4.交换后的指令集所对应的逻辑
  1. //原始逻辑Greeterp=newGreeter();//改动后的逻辑IGreeterp=(IGreeter)MyClassLoader.getInstance().findClass("com.example.Greeter").newInstance();
复制代码
这里又必要用到ASM来修正class文件了。查找到一切new对象的语句,交换成经由过程classloader的情势来猎取对象的情势。
清单5.使用ASM修正办法体
  1. @OverridepublicvoidvisitTypeInsn(intopcode,Stringtype){if(opcode==Opcodes.NEW&&type.equals(className)){List<LocalVariableNode>variables=node.localVariables;StringcompileType=null;for(inti=0;i<variables.size();i++){LocalVariableNodelocalVariable=variables.get(i);compileType=formType(localVariable.desc);if(matchType(compileType)&&!valiableIndexUsed[i]){valiableIndexUsed[i]=true;break;}}mv.visitMethodInsn(Opcodes.INVOKESTATIC,CLASSLOAD_TYPE,"getInstance","()L"+CLASSLOAD_TYPE+";");mv.visitLdcInsn(type.replace("/","."));mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,CLASSLOAD_TYPE,"findClass","(Ljava/lang/String;)Ljava/lang/Class;");mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,"java/lang/Class","newInstance","()Ljava/lang/Object;");mv.visitTypeInsn(Opcodes.CHECKCAST,compileType);flag=true;}else{mv.visitTypeInsn(opcode,type);}}
复制代码
关于第二种创立办法,必要经由过程修正Class.forName()和ClassLoader.findClass()的举动,使他们经由过程自界说加载器加载类。
利用JavaAgent拦阻默许加载器的举动

之前完成的类加载器已办理了热部署所必要的功效,但是JVM启动时,其实不会用自界说的加载器加载classpath下的一切class文件,取而代之的是经由过程使用加载器往加载。假如在其以后用自界说加载重视新加载已加载的class,有大概会呈现LinkageError的exception。以是必需在使用启动之前,从头交换已加载的class。假如在jdk1.4之前,能利用的办法只要一种,改动jdk中classloader的加载举动,使它指向自界说加载器的加载举动。幸亏jdk5.0以后,我们有了另外一种侵犯性更小的举措,这就是JavaAgent办法,JavaAgent能够在JVM启动以后,使用启动之前的长久间隙,供应空间给用户做一些特别举动。对照罕见的使用,是使用JavaAgent做面向方面的编程,在办法间到场监控日记等。
JavaAgent的完成很简单,只需在一个类内里,界说一个premain的办法。
清单6.一个复杂的JavaAgent
  1. publicclassReloadAgent{publicstaticvoidpremain(StringagentArgs,Instrumentationinst){GeneralTransformertrans=newGeneralTransformer();inst.addTransformer(trans);}}
复制代码
然后编写一个manifest文件,将Premain-Class属性设置成界说一个具有premain办法的类名便可。
天生一个包括这个manifest文件的jar包。
  1. manifest-Version:1.0Premain-Class:com.example.ReloadAgentCan-Redefine-Classes:true
复制代码
最初必要在实行使用的参数中增添-javaagent参数,到场这个jar。同时能够为Javaagent增添参数,下图中的参数是测试代码中testproject的相对路径。如许在实行使用的之前,会优先实行premain办法中的逻辑,而且预剖析必要加载的class。
.增添实行参数


<br>
这里使用JavaAgent交换原始字节码,制止原始字节码被Java假造机加载。只必要完成一个ClassFileTransformer的接口,使用这个完成类完成class交换的功效。
清单7.交换class
  1. @Overridepublicbyte[]transform(ClassLoaderparamClassLoader,StringparamString,Class<?>paramClass,ProtectionDomainparamProtectionDomain,byte[]paramArrayOfByte)throwsIllegalClassFormatException{StringclassName=paramString.replace("/",".");if(className.equals("com.example.Test")){MyClassLoadercl=MyClassLoader.getInstance();cl.defineReference(className,"com.example.Greeter");returncl.getByteCode(className);}elseif(className.equals("com.example.Greeter")){MyClassLoadercl=MyClassLoader.getInstance();cl.redefineClass(className);returncl.getByteCode(className);}returnnull;}
复制代码
至此,一切的事情半途而废,浏览一下hotswap的了局吧。

用java开发web只要两本书:一本是关于java基础的,一本是关于jsp、servlet的就可以了。开发周期长,我就来讲句题外话,现在有很多思想都是通过java来展现。
活着的死人 该用户已被删除
沙发
发表于 2015-1-19 23:51:09 | 只看该作者
J2SE开发桌面应用软件比起 VC,VB,DEPHI这些传统开发语言来说,优势好象并不明显。J2ME对于初学者来说,好象又有点深奥,而且一般开发者很难有开发环境。
冷月葬花魂 该用户已被删除
板凳
发表于 2015-1-25 09:07:18 | 只看该作者
如果要向java web方向发展也要吧看看《Java web从入门到精通》学完再到《Struts2.0入门到精通》这样你差不多就把代码给学完了。有兴趣可以看一些设计模块和框架的包等等。
蒙在股里 该用户已被删除
地板
发表于 2015-1-30 05:29:38 | 只看该作者
设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧
飘飘悠悠 该用户已被删除
5#
发表于 2015-2-6 07:35:46 | 只看该作者
Java是一个纯的面向对象的程序设计语言,它继承了 C++语言面向对象技术的核心。Java舍弃了C ++语言中容易引起错误的指针(以引用取代)、运算符重载(operator overloading)
柔情似水 该用户已被删除
6#
发表于 2015-2-6 18:11:57 | 只看该作者
设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧
若相依 该用户已被删除
7#
发表于 2015-2-15 23:13:30 | 只看该作者
设计模式是高级程序员真正掌握面向对象核心思想的必修课。设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧
透明 该用户已被删除
8#
发表于 2015-3-4 17:07:35 | 只看该作者
你可以去承接一些项目做了,一开始可能有些困难,可是你有技术积累,又考虑周全,接下项目来可以迅速作完,相信大家以后都会来找你的,所以Money就哗啦啦的。。。。。。
老尸 该用户已被删除
9#
发表于 2015-3-6 18:55:28 | 只看该作者
有时间再研究一下MVC结构(把Model-View-Control分离开的设计思想)
山那边是海 该用户已被删除
10#
发表于 2015-3-13 05:44:20 | 只看该作者
另外编写和运行Java程序需要JDK(包括JRE),在sun的官方网站上有下载,thinking in java第三版用的JDK版本是1.4,现在流行的版本1.5(sun称作J2SE 5.0,汗),不过听说Bruce的TIJ第四版国外已经出来了,是专门为J2SE 5.0而写的。
金色的骷髅 该用户已被删除
11#
发表于 2015-3-20 13:52:04 | 只看该作者
科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-12-23 23:36

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表