Tomcat Thermal deployment mechanism
For Java applications, a hot deployment is a Java class file that is updated at run time. Class loader plays an important role in implementing a hot deployment of Java-based application servers. Most Java-based application servers, including EJB servers and servlet containers, support hot deployment. The class loader cannot reload a class that has already been loaded, but you can load the class again into a running application as long as you are using a new class loader instance.
We know that most Web servers now support hot deployment, and for the implementation of the thermal deployment mechanism, the online speaking is not perfect, below we on the Tomcat implementation mechanism of the hot deployment, explain how it is implemented:
The Tomcat container implements a hot deployment using two mechanisms:
ClassLoader overrides the custom ClassLoader to load the corresponding JSP-compiled class into the JVM. The modified class is loaded into the JVM again by dynamically modifying the byte code in memory.
ClassLoader Implementing a JSP Reload
Tomcat has implemented a JSP load through Org.apache.jasper.servlet.JasperLoader, and here's a test: 1. Create a new Web project and write a JSP page that prints the page's Classloader,<%system.out.print (This.getclass (). getClassLoader ()) on the JSP page;%>.
2. Start the Web server, open the JSP page, we can see the background output, the JSP ClassLoader is an instance of Jasperloader.
3. Modify the JSP, save and refresh the JSP page, see the background output again, this ClassLoader instance is not just that, that is, Tomcat through a new ClassLoader loaded the JSP again.
4. In fact, for each JSP page Tomcat uses a separate classloader to load, each time after modifying the JSP, Tomcat will use a new classloader to load it.
about how to use a custom ClassLoader to load a class here do not say, I believe that the Internet can be found, JSP is a one-time consumption, each call to the container will create a new instance, which is used to throw the kind of, but for this way of implementation is difficult to use in other cases, As now many of our projects are using singleton, especially spring engineering, in which case it is unrealistic to use the new ClassLoader to load the modified class, and the Singleton class will produce multiple instances in memory, and this way cannot change the behavior of existing instances in the current memory, of course, Tomcat did not implement the reload of the class file in this way.
Modifying the byte code of a class in memory by proxy
The class file in Tomcat is loaded by Org.apache.catalina.loader. WebappClassLoader, and we can do a test that is similar to the JSP test, with the test steps not being said, just the results:
In the case of a hot deployment, for a class file loaded by the ClassLoader, its classloader is always the same webappclassloader, unless the container restarts, I believe that by doing this experiment you will no longer think that Tomcat is using a new ClassLoader to load the modified class, and for stateful instances, the properties and states previously owned by that instance will be saved and the next execution has the new class logic. This is the mystery of hot deployment (in fact, each instance only holds the state attribute of the instance, and we can see the state contained in the object by serializing the object, and the final logic exists in the class file).
The following class redefinition is implemented by: Java.lang.instrument, with reference to the relevant documentation.
Let's look at how to modify the class bytecode in memory by proxy:
The following is a simple hot deployment Proxy implementation Class (the code is coarser and there is no judgment):
Packageagent;ImportJava.lang.instrument.ClassFileTransformer;Importjava.lang.instrument.Instrumentation;ImportJava.util.Set;ImportJava.util.Timer;ImportJava.util.TreeSet; Public classhotagent {protected StaticSet<string> clsnames=NewTreeset<string>(); Public Static voidPremain (String Agentargs, Instrumentation Inst)throwsException {Classfiletransformer transformer=NewClasstransform (inst); Inst.addtransformer (transformer); System.out.println ("Whether to support the redefinition of a class:" +inst.isredefineclassessupported ()); Timer Timer=NewTimer (); Timer.schedule (NewReloadtask (inst), 2000,2000); }} Packageagent;Importjava.lang.instrument.classfiletransformer;importjava.lang.instrument.illegalclassformatexception;Importjava.lang.instrument.Instrumentation;ImportJava.security.ProtectionDomain; Public classClasstransform.ImplementsClassfiletransformer {PrivateInstrumentation Inst; protectedClasstransform (Instrumentation inst) { This. inst=Inst; } /*** This method is called at redefineclasses or initial load, meaning it is called when class is loaded again, * and we can dynamically modify the class bytecode to implement functions like proxies, using ASM or Java Sist, * If you are familiar with bytecode, you can modify the bytecode directly. */ Public byte[] Transform (ClassLoader loader, String className, Class<?>classbeingredefined, Protectiondomain Protectiondomain,byte[] classfilebuffer)throwsillegalclassformatexception {byte[] transformed =NULL; HotAgent.clsnames.add (ClassName); return NULL; }} Packageagent;ImportJava.io.InputStream;Importjava.lang.instrument.ClassDefinition;Importjava.lang.instrument.Instrumentation;ImportJava.util.TimerTask; Public classReloadtaskextendsTimerTask {PrivateInstrumentation Inst; protectedReloadtask (Instrumentation inst) { This. inst=Inst; } @Override Public voidrun () {Try{classdefinition[] CD=NewClassdefinition[1]; Class[] Classes=inst.getallloadedclasses (); for(Class cls:classes) {if(Cls.getclassloader () = =NULL||! Cls.getclassloader (). GetClass (). GetName (). Equals ("Sun.misc.launcher$appclassloader")) Continue; String name=cls.getname (). replaceall ("\ \", "/"); cd[0]=NewClassdefinition (Cls,loadclassbytes (cls,name+ ". Class"))); Inst.redefineclasses (CD); } }Catch(Exception ex) {ex.printstacktrace (); } } Private byte[] loadclassbytes (Class cls,string clsname)throwsexception{System.out.println (clsname+":"+CLS); InputStream is=Cls.getclassloader (). Getsystemclassloader (). getResourceAsStream (Clsname); if(is==NULL)return NULL; byte[] bt=New byte[Is.available ()]; Is.read (BT); Is.close (); returnBT; }}
The above is the basic implementation code, requires the component is: 1. Hotagent (pre-load) 2. Classtransform (The byte code of class can be modified when loading class), this example is useless to 3. Reloadtask (class Timer loader, the above code is for reference only) 4. Meta-inf/manifest. MF content is: (Parameter one: support class redefinition; parameter two: pre-loaded Class)
Can-redefine-classes:true premain-class:agent. Hotagent
5. Package the above components into a jar file (the component is complete and the test class file is written below). 6. To create a new Java project, write a Java logic class, and write a test class that invokes the method of the logical class in the testing class, let's look at the test class code:
PackageTest.redefine; Public classBean1 { Public voidtest1 () {System.out.println ("============================"); }} PackageTest.redefine; Public classTest { Public Static voidMain (string[] args)throwsinterruptedexception {Bean1 C1=NewBean1 (); while(true) {c1.test1 (); Thread.Sleep (5000); } }}
To run the test class:
Java–javaagent:agent.jar Test.redefine.Test
In the test class, we used a dead loop and timed the method of invoking the logical class. We can modify the method in the Bean1 implementation, will be at different times to see different output results, about the technical details also have nothing to say, I believe we can understand.
Tomcat Hot Deployment Configuration
<HostAppBase= "WebApps"Autodeploy= "true"name= "localhost"Unpackwars= "true"Xmlnamespaceaware= "false"xmlvalidation= "false"> <ContextDocBase= "Cpcweb"Path= "/cpcweb"reloadable= "true"Source= "Org.<span class="Wp_keywordlink "><ahref= "Http://res.importnew.com/eclipse"title= "Eclipse importnew Home"Target= "_blank">Eclipse</a></span>. Jst.j2ee.server:CPCWeb "/></Host>
Autodeploy= "true"-automatic deployment Reloadable= "true"-auto load
The implementation principle of Tomcat hot deployment