黑马软件工程师_<<基础加强-类加载器>>
---------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流! --------------------
1. 类加载器
1. 概述
当我们要运行某个程序的时候,那么就必须把其加载到内存中,那么就是通过类加载器进行加载。
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类,BootStrap,ExtClassLoader,AppClassLoader。
类加载器也是java类,因为其他是java类的加载器本身也要被类加载器加载,显然必须有一个类加载器不是java类,这正是BootStrap。是用C++编写的。
public class Demo { public static void main(String[] agrs) { ClassLoader cd = Demo.class.getClassLoader();// 获得加载器 System.out.println("Demo类的加载器:" + cd.getClass().getName()); while (cd != null) { System.out.println(cd.getClass().getName()); cd = cd.getParent();// 获取父类加载器 } } } 结果: Demo类的加载器:sun.misc.Launcher$AppClassLoader sun.misc.Launcher$AppClassLoader sun.misc.Launcher$ExtClassLoader
2. 委托机制
每个类加载器加载类时,又先委托给其上级加载器----当所有的祖宗类加载器没有加载到类,回到发起者类加载器,还加在不了的话,则抛出ClassNoFoundException异常,不在去找发起者加载器的儿子,因为没有getChild()方法获得子类的加载器,
也写加载自己类的加载器,如果你写JDK中现有类的加载器,那么虚拟机本身就有它的加载器,则不会执行到你写的加载JDK中类的加载器。
顺序:如果是在AppClassLoader中加载,他会先交给其父类ExtClassLoade加载器,然后在给其父类BootStrap加载器,然后找自己管理的加载去找,如果没有找到,则会交给子类ExtClassLoade加载器,如果还是没有找到要加载的那个文件,则会继续交给其子类加载器AppClassLoade,然后找自己管理的加载区域中查找,就是按照这样的顺序来查找和加载的。如果找到了,则在那一步结束加载。如如果最后都没有找到,那么就会抛出异常ClassNoFoundException。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类装载器为其父级类加载。
3. 自定义加载器
1.步骤
自定义的加载其要继承ClassLoader类
然后重新findClass方法,
使用 byte[]b=loadClassData(name);将其转换成字节码
然后使用的defineClass()方法将转换成Class。
会先执行loadClass()方法,查找其上一级加载器,没有找到后,就是返回执行自己覆盖的方法defineClass().就执行自己定义的加载器了。
注意一点:不要重写loadClass()方法,因为这个方法是默认的会调用现有的执行顺序,如果找不到了,会通过分findClass方法,找自定义的方法,,你要是 覆盖了loadClass方法,那么你就要写调用各个加载其,则会很麻烦的。所以你知道覆盖findClass()方法,这个方法才是自己天地。
2.加密加载器
1.首先定义一个类,继承一个类(例如:Date),为了就是使用加载器加载的时候,找不到时,需要父类。
2.定义自定义加载类,继承ClassLoader,然后定义加密方法,把上面定义的类文件加密,然后把源文件删除。
3.利用自定义的加载器,进行加载加密的文件。
把文件加密:部分代码,把加密后的文件放在了项目中的itcase文件夹中。
public class FileLoader extends Date { public String toString(){ return "hello,world"; } } public static void main(String[] args) throws Exception { String strPass = "E:\\heima\\heima\\src\\www\\fuxi\\jiaqiang1\\FileLoader.java"; String dir = "itcast"; String StrName = strPass.substring(strPass.lastIndexOf("\\") + 1); String despath = dir + "\\" + StrName; InputStream in = new FileInputStream(strPass); OutputStream out = new FileOutputStream(despath); addPass(in, out); } /** * 加密方法:把读取出来的字节都&0xff,这就是加密了,解密的话,就在&0xff。 * 也是解密方法:因为当同时&两次0xff,就和原来的一样了。 */ public static void addPass(InputStream in, OutputStream out) throws IOException { int b = -1; while ((b = in.read()) != -1) { out.write(b ^ 0xff); } }
把原来的FileLoader.java文件删除掉了
完整代码
public class FileLoader extends Date {//此类的源文件已经删除 public String toString(){ return "hello,world"; } } //自定义的加载器 import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class LoaderClass extends ClassLoader { privateString classDir; publicLoaderClass() { super(); } publicLoaderClass(String classDir) { super(); this.classDir= classDir; } publicstatic void main(String[] args) throws Exception { StringstrPass ="E:\\heima\\heima\\src\\www\\fuxi\\jiaqiang1\\FileLoader.java"; Stringdir = "itcast"; StringStrName = strPass.substring(strPass.lastIndexOf("\\") + 1); Stringdespath = dir + "\\" + StrName; InputStreamin = new FileInputStream(strPass); OutputStreamout = new FileOutputStream(despath); addPass(in,out); } /** * 加密方法:把读取出来的字节都&0xff,这就是加密了,解密的话,就在&0xff。 * 也是解密方法:因为当同时&两次0xff,就和原来的一样了。 */ public staticvoid addPass(InputStream in, OutputStream out) throwsIOException { int b= -1; while((b = in.read()) != -1) { out.write(b^ 0xff); } } /** * 重写的方法,里面调用的解密的方法。 */ @Override protectedClass<?> findClass(String name) throws ClassNotFoundException { StringclassFileName = classDir + "\\" +name.substring(name.lastIndexOf(".")+1) + ".java"; try { InputStreamin = new FileInputStream(classFileName); ByteArrayOutputStreamout = new ByteArrayOutputStream(); addPass(in,out);//这相当于解密的方法 byte[]bytes = out.toByteArray(); in.close(); out.close(); returndefineClass(bytes, 0, bytes.length); }catch (Exception e) { e.printStackTrace(); } returnsuper.findClass(name); } } //测试类 public class Text { /** * 利用自定义的加载器加载加密的文件 */ public static void main(String[] args) throws Exception { /* System.out.println(newFileLoader().toString()); */ Classclazz = new LoaderClass("itcast").loadClass("www.fuxi.jiaqiang1.FileLoader");//先用系统的加载器加载,找不到,就会找自定义的加载器 //FileLoader d1 =(FileLoader) clazz.newInstance(); Date d1 = (Date) clazz.newInstance(); System.out.println(d1); } }
2. 加载器的高级问题
1.获得加载Servlet的加载器
其实就是tomcat中提供的加载器
Servlet的部分代码:
public class MyServlet extends HttpServlet { public MyServlet() { super(); } public void destroy() { super.destroy(); } public void doGet(HttpServletRequest request, HttpServletResponseresponse) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); ClassLoader cl=this.getClass().getClassLoader();//获得加载器 while(cl!=null){ out.println(cl.getClass().getName()+"<br/>"); cl=cl.getParent();//找其上一级 } } 结果: org.apache.catalina.loader.WebappClassLoader org.apache.catalina.loader.StandardClassLoader sun.misc.Launcher$AppClassLoader sun.misc.Launcher$ExtClassLoader
2. 打包,移动包
将Servlet打成jar包后,放到了jdk中jre\lib\ext\itcase.jar中,其中itcase.jar是自己起的名字,这是时候重新启动tomcat,那么就会报异常,因为找不到含有HttpServlet加载的包(servlet-api.jar)。此jar包实在tomcat中,把其考到和刚才的itcase.jar放到一起。原因:为什么会报异常呢,因为加载器改变了,因为itcase.jar包是在jre\lib\ext\itcase.jar中,是由ExtClassLoader加载器加载(从上面的结果中可以看出,这是元老级的加载器了,所以如果此加载器找不到,也不会找其子类),但是itcase.jar包中的文件用到了HttpServlet类,但是此时找不到含有此类的包,所以报异常。
把tomcat中的lib\servlet-api.jar包复制到jdk中的jre\lib\ext\中,这样就可以找到了。
再次启动tomcat,运行那个servlet,
结果就是sun.misc.Launcher$ExtClassLoader。
3. 扩展
每次创建servlet,那么就会在项目中的WebRoot\WEB-INFO\web.xml中配置文件。
例如:我们在itheima.com包中创建了MyServletServlet,那么就会在web.xml中配置文件。
---------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流!
--------------------