黑马程序员_java高新技术—类加载器&&动态代理

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ---------------------

一、类加载器

一、概念

首先,类加载器也是一个java类。类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例。通过此实例的 newInstance()方法就可以创建出该类的一个对象。

基本上所有的类加载器都是 java.lang.ClassLoader类的一个实例


二、委托机制:

每个类加载器加载类的时候,先委托给上级类加载器,如果所有的祖宗类加载器找不到类的时候就返回到发起者类加载器。如果再加载不了的话就会抛出ClaaNotFoundException异常,不会再去找发起者类加载的子类加载器。这就是我们所说的委托机制。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。

类加载器的结构图:

Bootstrap (引导类加载器,并不是继继承ClassLoder类,并不是一个java类)

-------ExtClassLoder (加载ext目录下jar)

-------AppClassLoder(指定的Classpath目录)

除了Bootstrap 之外,所有的类加载器都有其父类加载器。我们自己编写的类加载器都是继承AppClassLoder加载器。我们可以通过getParent()方法得到其父类加载器。

下面就是一个获取类加载器结构的事例:

  public class getParent { 

    public static void main(String[] args) { 
        ClassLoader loader = getParent.class.getClassLoader(); 
        while (loader != null) { 
            System.out.println(loader.getClass().getName()); 
        
            loader = loader.getParent(); 
        } 
    } 
   }

sun.misc.Launcher$AppClassLoader

sun.misc.Launcher$ExtClassLoader

null

没有返回Bootstrap 是因为当父类加载器是Bootstrap 的时候getParent得到的是NULL

三、类的加载过程

真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。真正完成类的加载工作是通过调用 defineClass来实现的;而启动类的加载过程是通过调用 loadClass来实现的。前者称为一个类的定义加载器,后者称为初始加载器。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。

类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass方法不会被重复调用。

自己编写类加载器的步骤

1、继承loadClass

2、复写findClass

 

需求:自编一个类加载器,加载加密的文件

步骤:编写一个对文件内容加密的程序

编写一个自己的类类加载器,可实现对加密过的类进行加载和解密

把解密后的Class文件替换ClassPath下的Class文件。因为由类的委托机制可以知道如果不替换的话,我们编写的类加载器会委托其父类加载ClassPath下的文件,那么输出的还是原来没有加密的Class文件。

 

程序如下:

加密和自编类加载器类

  import java.io.ByteArrayOutputStream;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  
  public class MyClassLoader extends ClassLoader
  {
  	public static void main(String[] args) throws IOException
  	{
  	
  		FileInputStream fi=new FileInputStream("C:\\Users\\tzh\\Workspaces\\MyEclipse 8.5\\ReStuday\\bin\\MyClassLoaderAcc.class");//加密前的class文件
  		FileOutputStream fo=new FileOutputStream("C:\\Users\\tzh\\Workspaces\\MyEclipse 8.5\\ReStuday\\file\\MyClassLoaderAcc.class");//解密后的Class文件
  		cypher(fi,fo);
  		fi.close();
  		fo.close();
  		
  	}
  	public static void cypher(InputStream ins,OutputStream out) throws IOException
  	{
  		int num=0;
  		while((num=ins.read())!=-1)
  		{
  			out.write(num^0xff);//加密解密过程
  		}
  		ins.close();
  		out.close();
  	}
  	private String ClassDir;
  	@Override
  	//复写findClass
  	protected Class<?> findClass(String name) throws ClassNotFoundException {
  		// TODO Auto-generated method stub
  		String ClassFileName=ClassDir+"\\"+name+".class";
  		try {
  				FileInputStream in=new FileInputStream(ClassFileName);
  				ByteArrayOutputStream ou=new ByteArrayOutputStream();
  				cypher(in,ou);//解密文件
  				in.close();
  				byte[] by=ou.toByteArray();
  				return defineClass(by, 0, by.length);
  				
  			
  		} catch (Exception e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  		return super.findClass(name);
  	}
  	
  	public MyClassLoader(String ClassDir)
  	{
  		this.ClassDir=ClassDir;
  	}
  }

  被加载的类:
import java.util.Date;

public class MyClassLoaderAcc extends Date {
	public String toString()
	{
		return "hello java";
	}
  }
  加载MyClassLoaderAcc的类
import java.util.Date;

public class MyClassTest 
{
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException
	{
		System.out.println(new MyClassLoaderAcc().toString());//正常
		
		Class clazz=new MyClassLoader("file").loadClass("Studay.MyClassLoaderAcc");
		Date d=(Date)clazz.newInstance();
		System.out.println(d);
	}
  }

使用我们自编加载器的父类加载器调用加密文件后的结果

Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 889275713 in class file Studay/MyClassLoaderAcc

 

使用我们自编的加载器加载后的结果是:

hello java


二、代理

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 

动态代理类

JVM可以在运行时期动态生成类的字节码,这种方法生成的类就被用做代理。

JVM生成的动态代理类必须实现了一个或者多个接口,所以JVM生成的动态代理类只能代理实现了相同接口的目标类代理。

为没有实现接口的类生成动态代理可以用CGLIB库。Cglib库可以动态生成一个类的子类,一个类的子类也可以用作代理类的代理。

 

代理类各个方法中通常除了要调用目标的相应方法和对外返回的结果外,还可以在方法中的如下四个位置加上系统功能代码。

1、在调用目标方法之前

2、在调用目标方法之后

3、在调用目标方法前后

4、在处理目标方法异常的catch快中

创建实现了Collection接口的动态 类和查看器方法,分析Proxy.getProxyClass的各个参数。

 

怎么创建动态类的实例对象?

1、用反射的方法获得构造方法

2、调用构造方法创建动态类的实例对象,将InvocationHandler类的实例对象传进去。

 

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

public class ProxyDemo 
{
	public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
	{
		Class clazz=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		System.out.println(clazz.getName());
		//获取所有的构造函数
		Constructor[] cons=clazz.getConstructors();
		for(Constructor constructor:cons)
		{
			String name=constructor.getName();
			StringBuffer sb=new StringBuffer(name);
			
			sb.append('(');
			Class[] classPrame=constructor.getParameterTypes();//获取参数类型
			for(Class prame:classPrame)
			{
				
				sb.append(prame.getName()).append(',');
			}
			if(classPrame.length!=0&&classPrame!=null)
				sb.deleteCharAt(sb.length()-1);
				sb.append(')');
				System.out.println(sb.toString());
			
			
		}
//获得所有的方法
		Method[] methods=clazz.getMethods();
		for(Method method:methods)
		{
			String name=method.getName();
			StringBuffer sb=new StringBuffer(name);
			
			sb.append('(');
			Class[] classPrame=method.getParameterTypes();//获取参数类型
			for(Class prame:classPrame)
			{
				
				
				sb.append(prame.getName()).append(',');
			}
			if(classPrame.length!=0&&classPrame!=null)
				sb.deleteCharAt(sb.length()-1);
			sb.append(')');
			System.out.println(sb.toString());
		}
			
			//利用动态类生成对象
			Constructor constr=clazz.getConstructor(InvocationHandler.class);
			Collection proxy=(Collection)constr.newInstance(new InvocationHandler() {
				
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					// TODO Auto-generated method stub
					return null;
				}
			});
			//System.out.println(proxy.size());//调用代理对象的方法时返回的是null可是,size返回的是int型,所以会报错
		
	
  }

 

怎么把要执行的系统功能代码一当成参数的形式提供?

把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接受者只要调用这个对象的方法,即等于执行了外界提供的代码!

final ArrayList target=new ArrayList();
			Collection proxy3 = (Collection)getProxy(target,new MyAdvice() );
			
			proxy3.add("xx");
			proxy3.add("dd");
			proxy3.add("mm");
			System.out.println(proxy3.size());
			
			
		}
//生成代理的第三种方式

	private static Object getProxy(final Object target,final Advice advice) {
		Object proxy3=Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
				
					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;
	}
建立一个接口:
import java.lang.reflect.Method;

public interface Advice
{
	public void beforeMethod(Method method);
	public void afterMethod(Method method);
}
实现这个接口
import java.lang.reflect.Method;

public class MyAdvice implements Advice
{
	long beginTime;
	
	public void afterMethod(Method method) {
		
		long endTime=System.currentTimeMillis();
		System.out.println("haha ");
		System.out.println(method.getName()+"..........."+(endTime-beginTime));
	}

	
	public void beforeMethod(Method method) {
		System.out.println("heihei");
		 beginTime=System.currentTimeMillis();
		 
	}
	
}

参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数 




----------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值