您的位置:首页 > 编程语言 > Java开发

深入理解java动态代理

2014-08-26 10:21 731 查看
昨天看spring aop(面向切面编程)时,遇到了java的动态代理问题。折腾了太多时间,大体弄明白了什么意思。

首先来看一下动态代理类的定义:动态代理实际上是一种设计模式。之所以称为动态,是因为 proxy类是在运行中才创建出来的,它是根据你创建的接口来实现(创建)的。


如上图所示,代理(HelloProxy)相当于一个经纪人,别人需要明星(HelloImpl)做一些事情的时候,首先要找的是代理。HelloProxy采用组合方式,实现了对HelloImpl的引用。明星说的一些可能不太合适,但是经过代理润色后可以更为恰当。这种方式还可以用来控制访问helloImpl的权限上。

上面的这种方式被称为静态代理。但是静态代理存在一个缺陷就是每当IHello增加一个接口方法,我们的HelloProxy就需要再写一次,实现对应的接口,有没有一种方式能够一劳永逸呢?我们可以用java动态代理来实现。

而动态代理则是java技术的核心。

首先来看一下动态代理的类图:



我们的明星在此类图中相当于user。他所能做的事情都在接口interfaceB当中。从上图手中我们可以看到,$prxoyN和user都实现了接口interfaceB。$prxoyN类继承自类Proxy。在静态代理当中,proxy类直接含有对实际要工作的对象的引用(明星),但在这里,$prxoyN实际引用的对象是AInocationHandler。AInocationHandler包含对user(明星)对象的引用。AInocationHandler同时实现了接口InvocationHandler,这其实是java在java.lang.reflect.InvocationHandler定义的接口。由于$proxyN 实现了所有的interfaceB接口,因此我们可以直接使用interfaceB userPrxoy=

(interfaceB)Proxy.newProxyInstance(User.getClass().getClassLoader(), User.getClass().getInterfaces(), iHandler);的方式来进行访问。在调用userPrxoy方法时,实际上userProxy通过AInocationHandler调用了User的方法。

我们只需要在AInocationHandler里重写接口方法

@Override
public Object invoke(Object proxied, Method method, Object[] args)
throws Throwable {//在整个方法当中 proxied一直没有用 
           //这里proxied传递过来的为 $proxyN对象实例。即在类<span style="font-family: Arial, Helvetica, sans-serif;">$proxyN中,其会这样调用invoke(this,method,args);</span>
//可以写一些日志,如:System.out.println(method.getname+"方法被调用.....");以及通过method.getName判断调用的是哪个方法,来控制哪些方法不能被调用。
method.invoke(target, args);//实际user的方法,这里的target=user,注意这里 target不能用proxied,因为如果用proxied,就会再次进调用invoke方法,导致死循环的出现。 
System.out.println("method.getname+"方法被调用完成....."");
 return null;
}


这样的好处是实现了松耦合,我们并不需要对实际的User类做出任何修改,就可以实现对其控制(自己理解)。在一些访问控制,日志管理中十分有效。

实际例子程序。

程序源码如下:



接口(=interfaceB)

package com.myproxy;
public interface person {
 public void sayHello();
}


被代理的类(=User)

package com.myproxy;
public class userImpl implements person{
@Override
public void sayHello() {
System.out.println("hello,i am a star");
}
}


s AInvocationHandler



package com.myproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class AInvocationHandler implements InvocationHandler {

private person p;//含有对实际被代理对象的引用
    public AInvocationHandler(person p)
    {
    this.p=p;
    }
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("hello boys,and girls");//这其实是代理说出来的话,类似可以写一些日志类
System.out.println("即将被调用的方法是"+method.getName());
return method.invoke(this.p, args);//这里调用的是实际的被代理对象的引用
}
}


package com.myproxy;
import java.lang.reflect.Proxy;
import com.proxy.UserManager;
import com.proxy.invokeHandler;
import com.proxy.managerInterface;
import com.proxy.user;
import java.lang.reflect.Proxy;
public class test {
public static void main(String []ars)
{

   person p=new userImpl();
   AInvocationHandler iHandler=new AInvocationHandler(p);
   person userProxy=(person)Proxy.newProxyInstance(p.getClass().getClassLoader(),p.getClass().getInterfaces(), iHandler);
   userProxy.sayHello();
}
}


classLoader作用是加载class文件的。我们可以利用Class clz=classLoader.loadClass("className");产生Class对象,然后利用

Object obj=clz.newInstance();来产生className对应的实例。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: