------- android培训、java培训、期待与您交流! ----------
44 类加载器及其委托机制的深入分析
1.类加载器
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器(BootStrap,ExtClassLoader,AppClassLoader),每个类加载器负责加载特定位置的类.除了BootStrap以外的类加载器都是Java类,要被其他类加载器加载,最终的类加载器就是bootStrap.Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载器.
类加载器之间的父子关系和管辖范围图
- public class ClassLoaderTest {
- public static void main(String[] args) {
- //获取当前类的类加载器
- ClassLoader loader=ClassLoaderTest.class.getClassLoader();
- //最终的类加载器是BootStrap,不是Java类,不能获取,为null
- while(loader!=null){
- System.out.println(loader.getClass().getName());//获取类加载器名
- loader=loader.getParent();//获取父类
- }
- System.out.println(loader);
- }
- }
类加载器的委托机制
1.首先当前线程的类加载器去加载线程的第一个类
2.如果类A中引用了类B,Java虚拟机将使用加载A的类加载器去加载B
3.还可以直接调用ClassLoader.loaderClass()方法来指定某个类加载器去加载某个类
4.每个类加载器加载类时,先委托给其上级加载器,当最终的类加载器没有加载到类时,向发起者类加载器逐级加载,如果直到发起者类加载器都没有加载到类,则抛ClassNotFoundException
编写自己的类加载器
编程步骤:
1.编写一个对文件内容进行简单加密的程序
2.编写一个自己的类加载器,可实现对加密过的类进行装载和解密
3.编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文加载器或者系统类加载器,然后再使用Class.forName
练习:编写对class加密的工具类
要加密的类
- public class ClassLoaderAttachments extends Date {
- @Override
- public String toString() {
- return "hello,java";
- }
- }
自定义加载器
- public class MyClassLoader extends ClassLoader {
- public static void main(String[] args)throws Exception {
- // TODO Auto-generated method stub
- String srcPath = args[0];
- String destDir = args[1];
- FileInputStream fis = new FileInputStream(srcPath);
- //获取目标目录路径
- String destFileName =srcPath.substring(srcPath.lastIndexOf('\\')+1);
- String destPath = destDir+"\\"+destFileName;
- FileOutputStream fos = new FileOutputStream(destPath);
- //加密操作
- cypher(fis, fos);
- fis.close();
- fos.close();
- }
- public static void cypher(InputStream ips,OutputStream ops) throws Exception{
- int b = -1;
- while((b=ips.read())!=-1){
- ops.write(b^0xff); //文件加密
- }
- }
- private String classDir;
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- // 获取文件路径并解密
- String classFileName = classDir+"\\"+name+".class";
- try {
- FileInputStream fis = new FileInputStream(classFileName);
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- cypher(fis,bos);
- byte[] b = bos.toByteArray();
- return defineClass(b, 0, b.length);
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return super.findClass(name);
- }
- public MyClassLoader(){
- }
- public MyClassLoader(String classDir){
- this.classDir = classDir;
- }
- }
调用自己定义的加载器
- Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachments");
- Date d = (Date)clazz.newInstance();
- System.out.println(d.toString());
2.代理
动态代理技术
1.JVM可以在运行期动态生成该类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
2.JVM生成的动态代理类必须实现一个或多个接口,所以JVM生成的动态类只能用作具有相同接口的目标类型的代理
3.CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库
4.代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置上加上系统功能代码:
a.在调用目标方法之前
b.在调用目标方法之后
c.在调用目标方法前后
d.在处理目标方法异常的catch块中
利用InvocationHandler接口创建代理类的三种形式
- //1.实现InvocationHandler接口
- class MyInvocationHandler1 implements InvocationHandler{
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- return null;
- }
- }
- Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());
- System.out.println(proxy1.toString());
- //2.通过匿名内部的形式实现
- Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- return null;
- }
- });
- //3.用Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- Collection proxy3=(Collection)Proxy.newProxyInstance(
- Collection.class.getClassLoader(),
- new Class[]{Collection.class},
- new InvocationHandler(){
- ArrayList target= new ArrayList();
- public Object invoke(Object proxy, Method method,
- Object[] args) throws Throwable {
- long startTime = System.currentTimeMillis();
- Object retVal = method.invoke(target, args);
- long endTime = System.currentTimeMillis();
- System.out.println(method.getName()+" running time:"+(endTime - startTime));
- return retVal;
- }
- });
- proxy3.add("123");
- proxy3.add("453");
- proxy3.add("789");
- System.out.println(proxy3.size());
练习:编写一个代理
创建接口
- public interface Advice {
- void beforeMethod(Method method);
- void afterMethod(Method method);
- }
实现接口
- public class MyAdvice implements Advice {
- long startTime=0;
- @Override
- public void beforeMethod(Method method) {
- startTime = System.currentTimeMillis();
- }
- @Override
- public void afterMethod(Method method) {
- long endTime = System.currentTimeMillis();
- System.out.println(method.getName()+" running time:"+(endTime - startTime));
- }
- }
创建代理
- public static Object getProxy(final Object target,final Advice advice) {
- Object proxy3 =Proxy.newProxyInstance(
- target.getClass().getClassLoader(), //类加载器
- target.getClass().getInterfaces(),//实现的接口
- new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- advice.beforeMethod(method);
- Object retVal = method.invoke(target, args);
- advice.afterMethod(method);
- return retVal;
- }
- });
- return proxy3;
- }
------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------