Java反射学习总结四(动态代理使用实例和内部原理解析)

原创 2014年05月21日 19:46:08

通过上一篇文章介绍的静态代理Java反射学习总结三(静态代理)中,大家可以发现在静态代理中每一个代理类只能为一个接口服务,这样一来必然会产生过多的代理,而且对于每个实例,如果需要添加不同代理就要去添加相应的代理类。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能或者说去动态的生成这个代理类,那么此时就必须使用动态代理完成。

动态代理知识点:

Java动态代理类位于java.lang.reflect包下,主要有以下一个接口和一个类:

1.InvocationHandler接口:    该接口中仅有一个方法

public object invoke(Object obj, Method method, Object[] args)

在实际使用时,obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象的invoke方法在代理类中动态实现。


2.Proxy类:  该类即为动态代理类,这里只介绍一下newProxyInstance()这个方法

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

这个方法是最主要的方法,它会返回代理类的一个实例,返回后的代理类可以当做被代理类使用

实现动态代理需4步骤:

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法。

2.通过Proxy的静态方法newProxyInstance创建一个代理

3.创建被代理的类以及接口

4.通过代理调用方法

下面看这个例子具体说明如何通过上面的4个步骤来建立一个动态代理:


步骤1和步骤2合并写在一个类中,命名为DynamicProxy

public class DynamicProxy implements InvocationHandler {

	// 需要被代理类的引用
	private Object object;

	// 通过构造方法传入引用
	public DynamicProxy(Object object) {
		this.object = object;
	}

	// 定义一个工厂类,去生成动态代理
	public Object getProxy() {
		// 通过Proxy类的newProxyInstance方法动态的生成一个动态代理,并返回它
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object
				.getClass().getInterfaces(), this);
	}

	// 重写的invoke方法,这里处理真正的方法调用
	@Override
	public Object invoke(Object obj, Method method, Object[] args)
			throws Throwable {
		
		beforeDoing();
		
		Object invoke = method.invoke(object, args);
		
		afterDoing();
		
		return invoke;
	}
	
	public void beforeDoing() {
		System.out.println("before ............");
	}
	
	public void afterDoing() {
		System.out.println("after ............."+"\n");
	}

}
该类实现了InvocationHandler接口,并且自定义了一个getProxy()方法去调用Proxy类的newProxyInstance()去生成一个动态代理。


步骤3:创建被代理的类以及接口

//真实角色对象,继承自抽象角色,重写定义的方法。
public class RealSubject implements Subject1,Subject2{

	//Subject1接口中的方法
	@Override
	public void request() {
		System.out.println("this is real subject");
	}

	//Subject1接口中的方法
	@Override
	public void ask() {
		System.out.println("this is real ask");
		
	}
	
	//Subject2接口中的方法
	@Override
	public void request2() {
		System.out.println("this is real subject2");
		
	}
}
这个类就是我们需要被代理的类,他继承了两个接口分别是Subject1,Subject2

interface Subject1 {
	
	public  void request();
	
	public void ask();
}
interface Subject2 {
	
	public  void request2();
}


4.通过代理调用方法

接下来在main方法中通过动态生成的代理来调用方法

public static void main(String[] args) {
		
		//需要被代理的类
		RealSubject realSubject = new RealSubject();
		//用于创建动态代理的类,将被代理类的引用传递进去
		DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
		
		//通过getProxy方法动态的获取代理类,转换成需要调用的接口类型后调用方法
		Subject1 s1 = (Subject1) dynamicProxy.getProxy();
		s1.request();
		s1.ask();
		
		//通过getProxy方法动态的获取代理类,转换成需要调用的接口类型后调用方法
		Subject2 s2 = (Subject2) dynamicProxy.getProxy();
		s2.request2();
	}

最后打印:

before ............
this is real subject
after .............

before ............
this is real ask
after .............

before ............
this is real subject2
after .............

简单介绍动态代理内部实现原理:

例子看完了,肯定有如下疑问:

动态代理在哪里应用了反射机制?仅仅通过一个InvocationHandler接口和一个Proxy类的newProxyInstance方法是如何动态的生成代理?

下面就来简单的分析一下InvocationHandler,和Proxy的newProxyInstance方法是如何在运行时动态的生成代理的:


以下代码都是伪代码而且内容大部分参考马士兵动态代理的视频。如果感兴趣,建议找视频去学。


先看newProxyInstance是如何定义的

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

这里需要传入3个参数。先看第二个参数,传入一个接口类型的Class数组。

上面例子中传入的参数是object.getClass().getInterfaces()

object是被代理对象,这个参数就是通过反射拿到被代理对象的所有接口

在上面例子中就是我们定义的Subject1,Subject2接口了

有了接口数组,就可以通过类似下面的代码使用反射拿到接口中的所有方法

for (interface infce : interfaces[]) {
	Method[] methods = infce.getMethods();
	for (Method m : method) {
		m.getName();
	}
}
在正常情况下,知道了被代理的接口和接口里面的方法就可以去生成代理类了。

大概就是下面这种的一个简单的实现:一个很固定的套路,只要知道实现接口和方法就仿照写出。

public class ProxySubject implements Subject{
	
        private RealSubject realSubject;	
    
	@Override
	public void request() {
		
		realSubject.request();  
	}
}


动态代理还会在代理的方法中做一些其他的操作,如添加日志,时间,权限等操作。这时候就要靠InvocationHandler接口中的invoke方法。看看例子中如何实现的。

@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
		
	beforeDoing();
		
	Object invoke = method.invoke(object, args);
		
	afterDoing();
		
	return invoke;
}

这些代码是我们自定义的,需要实现什么操作就写在里面。

这段代码存在于Invocationhandler对象中,这个对象会在调用Proxy的newProxyInstance的方法中传递进去。

这时候可以通过反射知道被调用方法的名字等信息,之后还是通过字符串的形式拼接处类似下面的动态代理类

public class ProxySubject implements Subject{
	
        private RealSubject realSubject;	
	@Override
	public void request() {
		Methond md = Subject.getMethod("methodName");
		handler.invoke(this, md);  
	}
}

这个大概就是根据传递的接口对象和InvocationHandler结合后应该生成的代理类。但现在的问题是如何去动态的生成上面这样的代理类。

答案是使用字符串拼接的方式。

从看上面的代码可以看出,除了接口和调用方法不同其他都相同。而且我们已经通过反射获得了方法和接口名字,这样就可以按着这个“套路”去用字符串拼接成这样的一类。

大概就是下面这种代码:

  String source = "package com.gxy.proxy;" + rt 
  
			    + "public class "+ClassName+"implements "+InterfaceName+ rt  
			    + "{" + rt  
			    + 		"private "+ ClassName + ClassName.toLowerCase()+" ; " + rt
			         
			    +       "@Override"
			    +		"public Void "+InterfaceName+ "()" + rt  + " {"
			    + 			"Method md = "+InterfaceName+".getMethod("+ methodName+");" +rt 
			    +           "hander.invoke(this, md);" + rt
			    +		"}" + rt 
			    + "}";

用反射生成的出来类名,接口名,方法名去动态的创建这样一个类的字符串。

之后就特定的方法去将这个字符串生成成类。在用反射把这个类取出来。这样就有了这个“动态”生成的代理类了。

就简单介绍到这吧。。。

最后大家可以发现例子中的动态代理里都是通过接口来实现的,如果对于不能实现接口的类就不能用JDK的动态代理了。如果想用就需要使用cglib了,因为cglib是针对类来实现的。

关于动态代理我研究了一个多礼拜,觉得理解起来还是比较困难的,勉勉强强的知道了个大概。

以后有时间我会继续深入的学习动态代理,但暂时还是以反射为主。下一篇准备写一下反射与注解的内容,希望大家多多支持。





反射和动态代理的区别

好吧,我承认,这个博客的名称都是有点问题的。因为笔者这个时候也有点混沌,但是我坚信这个应该是在将要质变的时候才会有的状态。 步入正文:反射和动态代理的区别。 动态代理,代理,前面已经有一篇文章说了...
  • wltsysterm
  • wltsysterm
  • 2017年03月31日 09:10
  • 1438

java反射机制与动态代理

在学习HadoopRPC时,用到了函数调用,函数调用都是采用的java的反射机制和动态代理来实现的,所以现在回顾下java的反射和动态代理的相关知识。 一、反射 JAVA反射机制定义: JAVA反...
  • u011067360
  • u011067360
  • 2014年05月05日 13:03
  • 3416

设计模式——代理模式

由于代码是直接在word中写的,所以,当做伪代码看吧,肯定直接运行起来有问题。 为其他对象提供一个代理以控制对这个对象的访问。 两个类大雄和哆啦a梦(Nobita daxiong和Doraemon...
  • wxwinnie0903
  • wxwinnie0903
  • 2013年09月22日 10:26
  • 426

JAVA 反射 总结 之 动态代理

test
  • cuigaochong
  • cuigaochong
  • 2015年05月20日 22:54
  • 293

Java反射学习总结四(动态代理使用实例和内部原理解析)

动态代理就是应用两个类来实现的
  • a396901990
  • a396901990
  • 2014年05月21日 19:46
  • 2598

Java动态代理机制原理详解(JDK 和CGLIB,Javassist,ASM)

原文地址:http://blog.csdn.net/luanlouis/article/details/24589193 class文件简介及加载      Java编...
  • lazyer_dog
  • lazyer_dog
  • 2016年08月23日 16:39
  • 2398

Java反射和动态代理详解和实例

一、反射 JAVA反射机制定义: JAVA反射机制是java程序在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以...
  • he90227
  • he90227
  • 2014年09月09日 14:26
  • 2637

深入浅出MyBatis:反射和动态代理

前三篇详细总结了Mybatis的基本特性、常用配置、映射器,相对于Hibernate,映射器的配置相对复杂,但有很好的灵活性和扩展性,可以应对各种业务场景。熟练掌握这些内容,可以流畅的使用MyBati...
  • wanshanJay
  • wanshanJay
  • 2018年03月30日 19:43
  • 16

反射总结

反射,是Java中非常重要的一个功能,如果没有反射,可以说很多框架都难以实现。 什么是反射?说白了就是可以通过Java代码获取装载到方法区的类信息的手段。 当装载一个类时,会在方法区产生一个数据结...
  • aa11zhong
  • aa11zhong
  • 2016年10月17日 20:45
  • 263

RPC中用到的 *动态代理,反射机制,NIO

今天在学习RPC协议时,用到了动态代理和反射机制 所以,我们先来看看动态机制:在java的动态代理中,有两个重要的类或接口,一个是InvocationHandler(Interface),另一个则是P...
  • syq521125
  • syq521125
  • 2015年09月17日 19:27
  • 1042
收藏助手
不良信息举报
您举报文章:Java反射学习总结四(动态代理使用实例和内部原理解析)
举报原因:
原因补充:

(最多只允许输入30个字)