A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 张向辉 于 2013-1-30 11:43 编辑

java中代理是如何动态创建实例的?举例说明

点评

张孝祥老师在高新技术里对动态代理这块讲的很细致,可以仔细学习下  发表于 2013-1-30 11:40

3 个回复

倒序浏览
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

Proxy类提供了创建动态代理类及其实例的静态方法。
(1)getProxyClass()静态方法负责创建动态代理类,它的完整定义如下:

public static Class<?> getProxyClass(ClassLoader loader, Class<?>[] interfaces) throws IllegalArgumentException

  参数loader 指定动态代理类的类加载器,参数interfaces 指定动态代理类需要实现的所有接口。

(2)newProxyInstance()静态方法负责创建动态代理类的实例,它的完整定义如下:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws
     IllegalArgumentException

   参数loader 指定动态代理类的类加载器,参数interfaces 指定动态代理类需要实现的所有接口,参数handler 指定与动态代理类关联的 InvocationHandler 对象。


以下两种方式都创建了实现Foo接口的动态代理类的实例:
/**** 方式一 ****/
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);

//创建动态代理类
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });

//创建动态代理类的实例
Foo foo = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).
   newInstance(new Object[] { handler });


/**** 方式二 ****/
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);

//直接创建动态代理类的实例
Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class }, handler);

由Proxy类的静态方法创建的动态代理类具有以下特点:
  动态代理类是public、final和非抽象类型的;
  动态代理类继承了java.lang.reflect.Proxy类;
  动态代理类的名字以“$Proxy”开头;
  动态代理类实现getProxyClass()和newProxyInstance()方法中参数interfaces指定的所有接口;

Proxy 类的isProxyClass(Class<?> cl)静态方法可用来判断参数指定的类是否为动态代理类。只有通过Proxy类创建的类才是动态代理类;

动态代理类都具有一个public 类型的构造方法,该构造方法有一个InvocationHandler 类型的参数。

由Proxy类的静态方法创建的动态代理类的实例具有以下特点:
1. 假定变量foo 是一个动态代理类的实例,并且这个动态代理类实现了Foo 接口,那么“foo instanceof Foo”的值为true。把变量foo强制转换为Foo类型是合法的:
(Foo) foo //合法

2.每个动态代理类实例都和一个InvocationHandler 实例关联。Proxy 类的getInvocationHandler(Object proxy)静态方法返回与参数proxy指定的代理类实例所关联的InvocationHandler 对象。

3.假定Foo接口有一个amethod()方法,那么当程序调用动态代理类实例foo的amethod()方法时,该方法会调用与它关联的InvocationHandler 对象的invoke()方法。

InvocationHandler 接口为方法调用接口,它声明了负责调用任意一个方法的invoke()方法:
Object invoke(Object proxy,Method method,Object[] args) throws Throwable

参数proxy指定动态代理类实例,参数method指定被调用的方法,参数args 指定向被调用方法传递的参数,invoke()方法的返回值表示被调用方法的返回值。

四、最后看一个实例:
    HelloServiceProxyFactory 类的getHelloServiceProxy()静态方法负责创建实现了HelloService接口的动态代理类的实例。

例程5 HelloServiceProxyFactory.java
package proxy;
import java.lang.reflect.*;
public class HelloServiceProxyFactory {
  /** 创建一个实现了HelloService 接口的动态代理类的实例
   * 参数helloService 引用被代理的HelloService 实例
   */
  public static HelloService getHelloServiceProxy(final HelloService helloService){
  //创建一个实现了InvocationHandler接口的匿名类的实例
  InvocationHandler handler=new InvocationHandler(){
     public Object invoke(Object proxy,Method method,Object args[])throws Exception{
       System.out.println("before calling "+method); //预处理
       Object result=method.invoke(helloService,args);
      //调用被代理的HelloService 实例的方法
       System.out.println("after calling "+method); //事后处理
       return result;
      }
    };
   Class classType=HelloService.class;
   return (HelloService)Proxy.newProxyInstance(classType.getClassLoader(),
       new Class[]{classType},
       handler);
   }
}
如下所示的Client2 类先创建了一个HelloServiceImpl 实例,然后创建了一个动态代理类实例helloServiceProxy,最后调用动态代理类实例的echo()方法。
例程6 Client2.java
package proxy;
public class Client2{
  public static void main(String args[]){
   HelloService helloService=new HelloServiceImpl();
   HelloService helloServiceProxy=HelloServiceProxyFactory.getHelloServiceProxy(helloService);
   System.out.println("动态代理类的名字为"+helloServiceProxy.getClass().getName());
   System.out.println(helloServiceProxy.echo("Hello"));
  }
}
运行Client2,打印结果如下:
动态代理类的名字为$Proxy0
before calling public abstract java.lang.String proxy.HelloService.echo(java.lang.String)
after calling public abstract java.lang.String proxy.HelloService.echo(java.lang.String)
echo:Hello
从结果看出,动态代理类的名字为$Proxy0。  

评分

参与人数 1技术分 +1 收起 理由
Rancho_Gump + 1

查看全部评分

回复 使用道具 举报
代理的演示类:public class ProxyTest {
public static void main(String[] args) throws Exception {
  
   Object target=new ArrayList<String>();//代理要操作的目标。
   MyAdvice myA=new MyAdvice();//作为一个程序员实际要开发的程序类。 打个比方,比如你想买一个联想笔记本,这时你找到一个代理商,
//你除了要告诉代理商你要买的东西是联想笔记本即目标target,还要告诉他,你要什么型号的,外表是什么颜色的等一些事情。这些事情代理
//不可能知道,所以是我们程序员要做的事情。
  Collection Proxy2 =(Collection) getObject(target,myA);//创建代理类,这个代理给的目标是一个集合,建议是打印出每操作一次集合的时间。
  Proxy2.add("sss");//通过代理调用集合。
  Proxy2.add("kkk");
  Proxy2.add("ppp");
  Proxy2.size();
  System.out.println(Proxy2.size());
  System.out.println(Proxy2.toString());
}
private static Object getObject(final Object target,final Advice log) {//封装好的代理类。这段代码一但写好,是不用改变的。这就是代理的好
//处。
  Object Proxy2=Proxy.newProxyInstance(target.getClass().getClassLoader(),//目标类的加载器
                                    target.getClass().getInterfaces(),//目标类实现的接口的类字节码集合
            new InvocationHandler(){       //这里是一个内部类,实际上代理即使通过这个类的invoke方法,来对目标(ArrayList集合)
             public Object invoke(Object proxy, Method method, Object[] args)//和Advice建议(打印方法运行时间)来进行操作的。
               throws Throwable {              //
              log.beforeMethed();
             Object reget=method.invoke(target, args);
                 log.afterMethed();
              return reget;//将操作后的结果返回给调用代理的方法。
             }
          });
  return Proxy2;
}
}

interface Advice{//代理需要的建议的格式。也就是说你必须实现这个接口才能提建议。为什么要实现这个接口呢,也很好理解。
void beforeMethed(); //继续拿买笔记本举例,你告诉代理商买个笔记本,建议是要有4个轱辘。代理商不久傻逼了吗。
void afterMethed();//这时代理商就要弄一个表格,里面只有。机子型号,机身颜色等几个内容。不按规格写的直接忽略。
}//这个表格就相当于建议接口。
class MyAdvice implements Advice{//这个更好理解了吧,就是你填写完的表格。代理建议的具体实现。
long beginTime;                             //实际开发中,当把代理做成AOP框架后,我们需要做的就是写这部分的代码。
@Override                                    //然后把目标和Advice配置到配置文件中就好了。
public void afterMethed() {
  
  // TODO Auto-generated method stub
  long afterTime= System.currentTimeMillis();
   System.out.println("执行时间为...."+(beginTime-afterTime));
}
@Override
public void beforeMethed() {
  // TODO Auto-generated method stub
  beginTime=System.currentTimeMillis();
}
}

这是张老师视频中的一个动态代理,我看了后写的加了详细注释,希望对你有帮助。

回复 使用道具 举报
谢了......
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马