黑马程序员技术交流社区
标题:
java中代理是如何动态创建实例的?举例说明
[打印本页]
作者:
铿锵科技
时间:
2013-1-29 19:23
标题:
java中代理是如何动态创建实例的?举例说明
本帖最后由 张向辉 于 2013-1-30 11:43 编辑
java中代理是如何动态创建实例的?举例说明
作者:
张庚
时间:
2013-1-29 19:47
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由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。
作者:
苏克
时间:
2013-1-29 20:03
代理的演示类: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();
}
}
这是张老师视频中的一个动态代理,我看了后写的加了详细注释,希望对你有帮助。
作者:
铿锵科技
时间:
2013-1-30 12:59
谢了......
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2