深度解析JDK动态代理

动态代理模式,就是在内存中动态生成$Proxy0对象,该对象实现了要被代理对象的接口。

如下图,$Proxy0就是在内存中生成的。


1. 首先需要写一个class,实现InvocationHandler接口。重写invoke方法。

invoke方法有3个参数:

    1.第一个参数是Proxy的一个动态实例。只有Proxy实例在InvocationHandler实现类里加载才可以产生第二个参数method,所以$Proxy实例需要把自己传给invoke方法。在InvocationHandler源码中对proxy的描述:proxy the proxy instance that the method was invoked on。

    2.第二个参数method是真实对象要实现的业务方法,由$Proxy0实例的静态代码段得到。

    3.第三参数args是method的参数。

主要起作用是method.invoke,将要被代理对象的接口传进去。

public class MyHandler implements InvocationHandler{
	private People people;
	public MyHandler(People people){
		this.people=people;
		
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		before();
		//obj是要被代理的类
		method.invoke(people, args);
		after();
		return null;
	}
	private void before(){
		System.out.println("吃之前做一些准备工作");
		
	}
	private void after(){
		System.out.println("吃之后做一些整理工作");
	}

}


在使用的过程中,普通调用一般是

People people=new Damon();

动态代理则不同,

	People proxyPeople=(People) Proxy.newProxyInstance(proxyTest.class.getClassLoader(),
				new Class<?>[] {People.class}, 
				new MyHandler(new Damon()));
     proxyPeople.eat();

首先需要使用Proxy类的newProxyInstance方法。需要3个参数,其中前两个参数用来生成$Proxy的构造器,在以involcationHandler为参数生成$Proxy实例。

    1.第一个参数是类加载器。不一定要指定具体class,用this.getClass().getClassLoader()也可以。

    2. 第二个参数接口数组。需要实现的接口。这个会体现在$Proxy中。

    3.第三个参数调用处理器,指派involcationHandler去实际执行增强业务。

    当执行的时候,会自动在内存中生成$Proxy0对象。该对象实现了People接口。所以这是为什么JDK动态代理只能代理有接口的类。

    其重写了接口的eat方法,调用了h.invoke();


查看$Proxy,可以使用ProxyGenerator生成byte数组,然后在用outputStream生成.class文件,进行反编译,查看java代码。

ProxyGenerator.generateProxyClass


自己重写Proxy类。主要实现其newProxyInstance静态方法。

public static Object newProxyInstance(ClassLoader loader,
			Class<?>[] interfaces, Handler myHandler) {
		
String fileName = "D:/Java_space_sum/0413/Proxy/src/invoke/myproxy/$Proxy0.java";		
//创建一个$Proxy0的类结构,就是创建一个java文件, 字符串拼凑加上流String javaClassStr = getJavaStr(interfaces);//通过流的方式将javaStr输出到文件createJavaFile(javaClassStr,fileName);//编译生成文件compilerJava(fileName);//自定义一个类加载器,把对应的.class加载到内存中,并且生成代理实例Object h=LoadClass(myHandler);return h;}

    1.指定java文件存放地址。

String fileName = "D:/Java_space_sum/0413/Proxy/src/invoke/myproxy/$Proxy0.java";

    其实JDK支持读取当前文件所在路径,不需要hadcode.

InputStream resourceStream=MyProxy.class.getResourceAsStream("/");

    2.创建一个类结构

private static String getJavaStr(Class<?>[] interfaces) {
		Method[] methods = interfaces[0].getMethods();
		String proxyClass = "package baoming" + rt + "import " + rt
				+ "public class  $Proxy0 implements" + interfaces[0].getName()
				+ "{" + rt + "MyInvocaltionHandler h;" + rt + "public ";
		return proxyClass;
	}

    3.生成java文件

private static void createJavaFile(String javaClassStr,String fileName) {
		File f = new File(fileName);
		FileWriter fw;
		try {
			fw = new FileWriter(f);
			fw.write(javaClassStr);
			fw.flush();
			fw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

    4.编译java文件,生成.class文件

private static void compilerJava(String fileName){
		JavaCompiler systemJavaComplier=ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager standardJavaFileManager=systemJavaComplier.getStandardFileManager
				(null, null, null);
		Iterable<? extends  JavaFileObject>  javaFileObject=standardJavaFileManager.getJavaFileObjects(fileName);
		systemJavaComplier.getTask(null, standardJavaFileManager, 
				null, null, null, javaFileObject);
	}

    5.自定义一个类加载器,把对应的.class文件加载到内存中。生成代理对象

private static Object LoadClass(Handler h){
		MyClassLoader myClassLoader=new MyClassLoader("D:/Java_space_sum/0413/Spring0414/src/invoke/MyProxy");
		Object newInstance = null;
		try {
			Class<?> findClass = myClassLoader.findClass("$Proxy0");
			//内存里面的代理对象的反射对象
			Constructor<?> constructor=findClass.getConstructor(Handler.class);
			//拿到反射对象的构造器
			newInstance=constructor.newInstance(h);
			//这个newInstance就是我们需要的代理实例
			return newInstance;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		return newInstance;
		
	}


cglib实现原理跟JDK动态代理不一样。cglib支持无接口实现的类。但是不支持使用final修饰的类。其底层使用到了ASM字节码包,所以又叫字节码代理。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值