黑马程序员15 java基础加强2(类加载器和代理)

时间:2023-02-12 09:08:22

------- android培训、java培训、期待与您交流! ----------

类加载器

在java中每个类都是由某个类加载器的实体来载入的,因此在Class类的实体中,都会有
字段记录载入它的类加载器的实体(当为null时,其实是指Bootstrap ClassLoader)。 在java类加载器中除了引导类加载器(既Bootstrap ClassLoader),所有的类加载器都有一个父类加载器(因为他们本身自己就是java类)。而类的加载机制是遵循一种委托模式:当类加载器有加载类的需求时,会先请求其Parent加载(依次递归),如果在其父加载器树中都没有成功加载该类,则由当前类加载器加载。

java的类加载器分为以下几种:
1,Bootstrap ClassLoader,用C++实现,一切的开始,是所有类加载器的最终父加载器。负责将一些关键的Java类,如java.lang.Object和其他一些运行时代码先加载进内存中。
2,ExtClassLoader,用java实现,是Launcher.java的内部类,编译后的名字为:Launcher$ExtClassLoader.class 。此类由Bootstrap ClassLoader加载,但由
Bootstrap ClassLoader已经脱离了java体系(c++),所以Launcher$ExtClassLoader.class的Parent(父加载器)被设置为null;它用于装载Java运行环境扩展包(jre/lib/ext)中的类,而且一旦建立其加载的路径将不再改变。
3,AppClassLoader,用java实现,也是是Launcher.java的内部类,编译后的名字为:Launcher$AppClassLoader.class 。AppClassLoader是当Bootstrap ClassLoader
加载完ExtClassLoader后,再被Bootstrap ClassLoader加载。所以ExtClassLoader和AppClassLoader都是被Bootstrap ClassLoader加载,但AppClassLoader的Parent被设置
为ExtClassLoader。可见Parent和由哪个类加载器来加载不一定是对应的。个类装载器是我们经常使用的,可以调用ClassLoader.getSystemClassLoader() 来获得,如果程序中没有使用类装载器相关操作设定或者自定义新的类装载器,那么我们编写的所有java类都会由它来装载。而它的查找区域就是我们常常说到的Classpath,一旦建立其加载路径也不再改变。
4,ClassLoader:一般我们自定义的ClassLoader从ClassLoader类继承而来。比如:URLClassloader是ClassLoader的一个子类,而URLClassloader也是ExtClassLoader和AppClassLoader的父类(注意不是父加载器)。

对class文件进行加密

public static void cypher(InputStream istream,OutputStream ostream) throws 

Exception  
{  
    //下面这段代码可能遇到255的字节,当成byte就成了-1  
    /*byte b = 0; 
    while((b = (byte)istream.read()) != -1) 
    { 
        ostream.write(b ^ 0xff);//异或 
    }*/  
  
    int b = 0;  
    while((b = istream.read()) != -1)  
    {  
        ostream.write(((byte)b) ^ 0xff);  
    }  
}

完整示例

import java.util.Date;  
  
public class ClassLoaderTest {  
  
    /** 
     * @param args 
     * @throws Exception 
     */  
    public static void main(String[] args) throws Exception {  
  
        System.out.println(ClassLoaderTest.class.getClassLoader().getClass()  
                .getName());// sun.misc.Launcher$AppClassLoader  
        System.out.println(System.class.getClassLoader());// null,特殊的类加

载器,不是java对象,没有字节码  
  
        ClassLoader loader = ClassLoaderTest.class.getClassLoader();  
        while (loader != null) {  
            System.out.println(loader.getClass().getName

());//sun.misc.Launcher$AppClassLoader  
            loader = loader.getParent();//sun.misc.Launcher$ExtClassLoader  
        }  
        System.out.println(loader);//null  
  
        // System.out.println(new  
        // ClassLoaderAttachment().toString());//无法使用已加密的字节码  
        // 创建MyClassLoader对象,传入目录"itcastlib",并加载字节码

ClassLoaderAttachment.class  
        Class clazz = new MyClassLoader("itcastlib")  
                .loadClass("ClassLoaderAttachment");  
        // ClassLoaderAttachment d1=  
        // (ClassLoaderAttachment)clazz.newInstance();//此时不能使用

ClassLoaderAttachment,因为此时未解密  
        Date d1 = (Date) clazz.newInstance();// 使用加载的字节码实例化  
        System.out.println(d1);//Hello,itcast!  
    }  
  
}  
import java.io.ByteArrayOutputStream;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.InputStream;  
import java.io.OutputStream;  
  
public class MyClassLoader extends ClassLoader {  
  
    /** 
     * @param args 
     * @throws Exception 
     */  
    public static void main(String[] args) throws Exception {  
        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();  
    }  
  
    private 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);//解密  
            fis.close();  
            byte[] bytes=bos.toByteArray();//将解密内容转为字节数组  
            return defineClass(bytes,0,bytes.length);//将字节数组生成字节码并

返回  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return super.findClass(name);  
    }  
  
    public MyClassLoader() {  
  
    }  
  
    public MyClassLoader(String classDir) {  
        this.classDir = classDir;  
    }  
  
}  

  

import java.util.Date;  
  
public class ClassLoaderAttachment extends Date {  
    public String toString(){  
        return "Hello,itcast!";  
    }  
}  

===========================================

代理


动态代理技术
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即
动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有
相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,
如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外
,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中

 

import java.lang.reflect.*;  
import java.util.ArrayList;  
import java.util.Collection;  
  
/* 
 * 创建动态类及查看其方法列表信息 
 */  
public class ProxyTest {  
  
    /** 
     * @param args 
     * @throws Exception 
     */  
    public static void main(String[] args) throws Exception {  
        // 返回代理类的 java.lang.Class 对象(获取字节码),并向其提供类加载器

和接口数组  
        Class clazzProxy1 = Proxy.getProxyClass(  
                Collection.class.getClassLoader(), Collection.class);// 通常

使用与接口相同的类加载器  
        System.out.println(clazzProxy1.getName());  
        System.out.println("");
        // 查看获得的字节码中有什么方法  
        System.out.println("---------------constructors 

list------------------");
          
        Constructor[] constructors = clazzProxy1.getConstructors();  
        for (Constructor constructor : constructors) {  
            String name = constructor.getName();  
            StringBuilder sBuilder = new StringBuilder(name);  
            // 单线程StringBuilder效率高,多线程StringBuffer效率较高  
            sBuilder.append('(');  
            Class[] clazzParams = constructor.getParameterTypes();  
            for (Class clazzParam : clazzParams) {  
                sBuilder.append(clazzParam.getName()).append(',');  
            }  
            if (clazzParams != null && clazzParams.length != 0) {  
                sBuilder.deleteCharAt(sBuilder.length() - 1);// 去掉最后一个

逗号','  
                sBuilder.append(')');  
                System.out.println(sBuilder.toString());  
            }   
        }  
        System.out.println("");  
        System.out.println("---------------methods list------------------");  

     
        Method[] methods = clazzProxy1.getMethods();  
        for (Method method : methods) {  
            String name = method.getName();  
            StringBuilder sBuilder = new StringBuilder(name);  
            // 单线程StringBuilder效率高,多线程StringBuffer效率较高  
            sBuilder.append('(');  
            Class[] clazzParams = method.getParameterTypes();  
            for (Class clazzParam : clazzParams) {  
                sBuilder.append(clazzParam.getName()).append(',');  
            }  
            if (clazzParams != null && clazzParams.length != 0) {  
                sBuilder.deleteCharAt(sBuilder.length() - 1);// 去掉最后一个

逗号','  
                sBuilder.append(')');  
                System.out.println(sBuilder.toString());  
            }  
        }  
        System.out.println("");  
        System.out.println("---------------create 

instance------------------");      
        // clazzProxy1.newInstance();//没有不带参数的构造方法  
        Constructor constructor = clazzProxy1  
                .getConstructor(InvocationHandler.class);  
        
        
        class MyInvocationHander1 implements InvocationHandler {  
            @Override  
            public Object invoke(Object proxy, Method method, Object[] args)  
                    throws Throwable {  
                return null;  
            }  
  
        }  
        // InvocationHandler是interface,只能实现它的实现类,这里是

MyInvocationHandler  
        Collection proxy1 = (Collection) constructor  
                .newInstance(new MyInvocationHander1());  
        System.out.println(proxy1);// null,这里是toString方法返回了null,否则

如果对象没有创建成功的话会出现异常  
  
        proxy1.clear();// 清除集合  
        // proxy1.size();  
        Collection proxy2 = (Collection) constructor  
                .newInstance(new InvocationHandler() {  
                    @Override  
                    public Object invoke(Object proxy, Method method,  
                            Object[] args) throws Throwable {  
                        return null;  
                    }  
                });  
  
        final ArrayList target = new ArrayList();  
        Collection proxy3 = (Collection)getProxy(target, new MyAdvice());  
        proxy3.add("abc");// 每次调用add,都会调用invoke方法  
        proxy3.add("cba");  
        proxy3.add("ccc");  
  
        System.out.println(proxy3.size());  
        System.out.println(proxy3.getClass().getName());// $Proxy0,返回的不

是目标  
  
    }  
  
    private static Object getProxy(final Object target,final Advice advice) { 

 
        Object proxy3 = (Collection) Proxy.newProxyInstance(  
                target.getClass().getClassLoader(),  
                /*new Class[] { Collection.class }*/  
                target.getClass().getInterfaces()  
                , new InvocationHandler() {  
                    @Override  
                    public Object invoke(Object proxy, Method method,  
                            Object[] args) throws Throwable {  
                        // 指定目标  
                        /*long beginTime = System.currentTimeMillis(); 
                        Object retVal = method.invoke(target, args); 
                        long endTime = System.currentTimeMillis(); 
                        System.out.println(method.getName() 
                                + " running time of " + (endTime - 

beginTime)); 
                        return retVal;*/  
                          
                          
                        advice.beforeMethod(method);  
                        Object retVal = method.invoke(target, args);  
                        advice.afterMethod(method);  
                        return retVal;  
                    }  
                });  
        return proxy3;  
    }  
}  

  
  
interface Advice {  
    void beforeMethod(Method method);  
    void afterMethod(Method method);  
}  


class MyAdvice implements Advice {  
    long beginTime=0;  
    @Override  
    public void beforeMethod(Method method) {  
        // TODO Auto-generated method stub  
        System.out.println("开始");  
        beginTime = System.currentTimeMillis();  
          
    }  
  
    @Override  
    public void afterMethod(Method method) {  
        // TODO Auto-generated method stub  
        System.out.println("结束");  
        long endTime = System.currentTimeMillis();  
        System.out.println(method.getName()  
                + " running time of " + (endTime - beginTime));  
  
    }  
  
}