java jdk动态代理原理,java中有哪些常用的动态代理技术
如何解决写爬虫IP受阻的问题?立即使用。
随着Java动态代理机制的出现,Java开发人员无需手动编写代理类,只需指定一组接口和委托类对象,就可以动态获取代理类。(推荐:java视频教程)
代理负责将所有方法调用分派给委托对象以执行反射。在调度执行的过程中,开发人员还可以根据需要调整委托对象及其功能。这是一个非常灵活的代理框架。让我们开始学习动态代理。
动态代理的简要说明
在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler(接口),一个是proxy(类)。
一、 InvocationHandler(interface)的描述:
InvocationHandler是由代理实例的调用处理程序实现的接口。
每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用被编码并被分派给其InvocationHandler的invoke方法。每个动态代理类都必须实现调用处理程序的接口。并且代理类的每个实例都与一个处理程序相关联。当我们通过代理对象调用一个方法时,这个方法的调用会被转发给InvocationHandler接口的invoke方法调用。让我们看一下invoke方法,InvocationHandler接口的唯一方法:
Object Invoke (Object Proxy,Method Method,Object[]args)Throwable该方法接收三个参数并返回一个对象类型,分别代表以下含义:
代理:指我们所代表的真实对象。
方法:指我们要调用真实对象的方法的方法对象。
Args:指调用真实对象的方法时接受的参数。
返回的对象指的是实对象方法的返回类型,下面的例子会进一步理解。
从代理实例上的方法调用返回的值。二。代理描述(类别):
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
这个代理类的功能是动态创建一个代理对象。我们经常使用newProxyInstance方法:
公共静态对象new proxy instance(Class loader loader,Class?[]接口,调用处理程序h)对Throws IllegalArgumentException参数的理解:
//一个ClassLoader对象,它定义使用哪个ClassLoader对象来加载生成的代理对象。
loader——定义代理类的类加载器
//一个接口对象数组,指示我将向需要代理的对象提供什么接口。
interfaces -代理类要实现的接口列表
//一个InvokeHandler对象,指示当我的动态代理对象调用方法时,将关联哪个InvokeHandler对象。
h-调用处理程序,调度方法调用以理解返回的结果:代理对象的一个例子
具有代理类的指定调用处理程序的代理实例,该代理类由指定的类加载器定义并实现指定的接口
简单的Java代理
我们创建了一个Java项目来测试和理解动态代理。项目结构如下:
一、 先定义一个接口Interface,添加两个方法。
包com . huhx . proxy;
公共接口界面{
void get myname();
String getNameById(字符串id);
}二、 定义一个真实的实现上述接口的类,RealObject:
包com . huhx . proxy;
公共类RealObject实现接口{
@覆盖
public void getMyName() {
System.out.println(我叫huhx );
}
@覆盖
公共字符串getNameById(字符串id) {
System.out.println(参数id: id );
返回‘huhx’;
}
}三、 定义一个代理对象,也实现了上述的Interface接口:
包com . huhx . proxy;
公共类SimpleProxy实现接口{
代理的专用接口;
公共简单代理(代理的接口){
this.proxied=代理
}
@覆盖
public void getMyName() {
系统。出去。println(“proxy get my name”);
代理的。获取my name();
}
@覆盖
公共字符串getNameById(字符串id) {
系统。出去。println( proxy getname byid );
返回代理的。getname byid(id);
}
}四、 SimpleMain在Main方法中,测试上述的结果:
包com。huhx。代理人;
公共类简单主体{
私有静态空的使用者(接口接口){
iface。获取my name();
字符串名称=I面。getname byid( 1 );
系统。出去。println( name: name );
}
公共静态void main(String[] args) {
consume(新真实对象());
系统。出去。println(====================================);
consume(新简单代理(新真实对象()));
}
}五、 运行的结果如下:
我的名字是huhx
参数id: 1
名称:huhx
========================================================
代理获取我的名字
我的名字是huhx
代理getnamebyid
参数id: 1
名称:huhxJava的动态代理
完成了上述简单的爪哇代理,现在我们开始学习爪哇的动态代理,它比代理的思想更向前一步,因为它可以动态地创建代理并动态的处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。下面我们通过案例来加深爪哇动态代理的理解:
一、 创建一个继承了InvocationHandler的处理器:DynamicProxyHandler
包com。huhx。动态代理;
导入Java。郎。反思。调用处理程序;
导入Java。郎。反思。方法;
导入Java。util。数组;
公共类DynamicProxyHandler实现InvocationHandler {
代理的私有对象;
公共DynamicProxyHandler(代理的对象){
System.out.println(动态代理处理程序构造器:已代理。getclass());
this.proxied=代理
}
@覆盖
公共对象调用(对象代理、方法方法、对象[]参数)抛出可投掷的
System.out.println(动态代理名:代理。getclass());
系统。出去。println( method: method。getname());
系统。出去。println( args:数组。tostring(args));
对象调用对象=方法。invoke(proxied,args);
if (invokeObject!=null) {
系统。出去。println( invoke对象: invoke对象。getclass());
}否则{
System.out.println(调用对象为null’);
}
返回invoke对象
}
} 二、 我们写一个测试的Main方法,DynamicProxyMain:
包com。huhx。动态代理;
导入Java。郎。反思。调用处理程序;
导入Java。郎。反思。代理人;
导入com。huhx。代理。界面;
导入com。huhx。代理。实物;
公共类DynamicProxyMain {
公共静态空的使用者(接口接口){
iface。获取my name();
字符串名称=I面。getname byid( 1 );
系统。出去。println( name: name );
}
公共静态void main(String[] args)引发异常,SecurityException,Throwable {
真实对象真实对象=新真实对象();
消费者(实物);
系统。出去。println(======================);
//动态代理
类加载器类加载器=接口。班级。获取类加载器();
班级?[]接口=新类[]{接口。class };
invocation handler=new DynamicProxyHandler(真实对象);
接口代理=(接口)代理。newproxyinstance(类加载器、接口、处理程序);
系统。出去。println( in dynamicproxyMain proxy: proxy。getclass());
消费者(代理);
}
}三、 运行结果如下:
我的名字是huhx
参数id: 1
名称:huhx
==============================
动态代理处理程序构造器:com.huhx.proxy.RealObject类
在动态代理主代理中:class com.sun.proxy.$Proxy0
动态代理名称:class com.sun.proxy.$Proxy0
方法:getMyName
参数:空
我的名字是huhx
调用对象为空
动态代理名称:class com.sun.proxy.$Proxy0
方法:getNameById
参数:[1]
参数id: 1
调用对象:java.lang.String类
名称:huhx从以上输出结果,我们可以得出以下结论:
与代理对象相关联的InvocationHandler,只有在代理对象调用方法时,才会执行它的引起方法
引起的三个参数的理解:对象代理是代理的对象,方法方法是真实对象中调用方法的方法类,Object[] args是真实对象中调用方法的参数
Java动态代理的原理
一、 动态代理的关键代码就是Proxy.newProxyInstance(classLoader, interfaces, handler),我们跟进源代码看看:
公共静态对象新代理实例(类加载器加载器,类?[]接口InvocationHandler h)抛出IllegalArgumentException {
//处理程序不能为空
if (h==null) {
抛出新的NullPointerException();
}
期末班?[]intfs=接口。clone();
最终安全管理器sm=系统。getsecuritymanager();
如果(sm!=null) {
checkProxyAccess(反射。getcallerclass()、loader、intfs);
}
/*
*查找或生成指定的代理类。
*/
//通过装货设备和接口,得到代理的班级对象
班级?cl=getProxyClass0(loader,intfs);
/*
*用指定的调用处理程序调用其构造函数。
*/
尝试{
最终构造者?cons=cl。获取构造函数(构造函数参数);
最终调用处理程序ih=h;
如果(sm!=null proxyaccesshelper。needsnewinstancecheck(cl)){
//使用特权创建代理实例,因为代理类可以
//实现需要特殊权限的非公共接口
返回门禁控制器。doprivileged(new PrivilegedActionObject(){
公共对象运行(){
返回newInstance(cons,ih);
}
});
}否则{
//创建代理对象的实例
返回newInstance(cons,ih);
}
} catch(NoSuchMethodException e){
抛出新的内部错误(例如tostring());
}
}二、 我们看一下newInstance方法的源代码:
私有静态对象新实例(构造函数?cons,InvocationHandler h) {
尝试{
返回cons.newInstance(新对象[]{ h });
} catch(IllegalAccessException 实例化异常e){
抛出新的内部错误(例如tostring());
} catch(InvocationTargetException e){
throwable t=e . get cause();
if(运行时异常的测试实例){
throw(运行时异常)t;
}否则{
抛出新的内部错误(t . tostring());
}
}
}三、 当我们通过代理对象调用 一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
体现这句话的代码,我在源码中没有找到,于是我在测试类的主要的方法中加入以下代码:
如果(代理的代理实例){
调用处理器调用处理器=代理。getinvocationhandler(代理);
invocationHandler.invoke(proxy,realObject.getClass().getMethod(getMyName ),null);
系统。出去。println(-);
}这段代码的输出结果如下,与上述中调用代理对象中的获取我的名字方法输出是一样的,不知道虚拟机(Java虚拟机的缩写)底层是否是这样判断的:
动态代理处理程序构造器:com.huhx.proxy.RealObject类
动态代理名称:class com.sun.proxy.$Proxy0
方法:getMyName
参数:空
我的名字是huhx
调用对象为空
-更多爪哇岛知识请关注爪哇岛基础教程栏目。以上就是爪哇动态代理的原理的详细内容,更多请关注我们其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。