jdk的动态代理是基于接口的,必须实现了某一个或多个任意接口才可以被代理,并且只有这些接口中的方法会被代理。看了一下jdk带的动态代理 api,发现没有例子实在是很容易走弯路,所以这里写一个加法器的简单示例。
// Adder.java
-
packagetest;
-
publicinterfaceAdder{
-
intadd(inta,intb);
-
}
// AdderImpl.java
-
packagetest;
-
publicclassAdderImplimplementsAdder{
-
@Override
-
publicintadd(inta,intb){
-
returna+b;
-
}
-
}
现在我们有一个接口Adder以及一个实现了这个接口的类AdderImpl,写一个Test测试一下。
// Test.java
-
packagetest;
-
-
publicclassTest{
-
publicstaticvoidmain(String[]args)throwsException{
-
Addercalc=newAdderImpl();
-
intresult=calc.add(1,2);
-
System.out.println("Theresultis"+result);
-
}
-
}
很显然,控制台会输出:
The result is 3
然而现在我们需要在加法器使用之后记录一些信息以便测试,但AdderImpl的源代码不能更改,就像这样:
Proxy: invoke add() at 2009-12-16 17:18:06
The result is 3
动态代理可以很轻易地解决这个问题。我们只需要写一个自定义的调用处理器(实现接口 java.lang.reflect.InvokationHandler),然后使用类java.lang.reflect.Proxy中的静态方法生成Adder的代理类,并把这个代理类当做原先的Adder使用就可以。
第一步:实现InvokationHandler,定义调用方法时应该执行的动作。
自定义一个类MyHandler实现接口 java.lang.reflect.InvokationHandler,需要重写的方法只有一个:
// AdderHandler.java
-
packagetest;
-
importjava.lang.reflect.InvocationHandler;
-
importjava.lang.reflect.Method;
-
classAdderHandlerimplementsInvocationHandler{
-
-
-
-
-
-
-
@Override
-
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
-
throwsThrowable{
-
-
}
-
}
使用代理后,这个方法将取代指定的所有接口中的所有方法的执行。在本例中,调用 adder.add()方法时,实际执行的将是invoke()。所以为了有正确的结果,我们需要在invoke()方法中手动调用add()方法。再看看invoke()方法的参数,正好符合反射需要的所有条件,所以这时我们马上会想到这样做:
Object returnValue = method.invoke(proxy, args);
如果你真的这么做了,那么恭喜你,你掉入了jdk为你精心准备的圈套。proxy是jdk为你生成的代理类的实例,实际上就是使用代理之后 adder引用所指向的对象。由于我们调用了adder.add(1, 2),才使得invoke()执行,如果在invoke()中使用method.invoke(proxy, args),那么又会使invoke()执行。没错,这是个死循环。然而,invoke()方法没有别的参数让我们使用了。最简单的解决方法就是,为 MyHandler加入一个属性指向实际被代理的对象。所以,因为jdk的冷幽默,我们需要在自定义的Handler中加入以下这么一段:
-
-
privateObjecttarget;
-
publicAdderHandler(Objecttarget){
-
this.target=target;
-
}
喜欢的话还可以加上getter/setter。接着,invoke()就可以这么用了:
// AdderHandler.java
-
packagetest;
-
importjava.lang.reflect.InvocationHandler;
-
importjava.lang.reflect.Method;
-
importjava.util.Date;
-
-
classAdderHandlerimplementsInvocationHandler{
-
-
privateObjecttarget;
-
-
publicAdderHandler(Objecttarget){
-
this.target=target;
-
}
-
-
@Override
-
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
-
throwsThrowable{
-
-
ObjectreturnValue=method.invoke(target,args);
-
-
System.out.println("Proxy:invoke"+method.getName()+"()at"+newDate().toLocaleString());
-
-
returnreturnValue;
-
}
-
}
第二步:使用jdk提供的java.lang.reflect.Proxy生成代理对象。
使用newProxyInstance()方法就可以生成一个代理对象。把这个方法的签名拿出来:
-
-
-
-
-
-
-
publicstaticObjectnewProxyInstance(ClassLoaderloader,
-
Class<?>[]interfaces,
-
InvocationHandlerh)
-
throwsIllegalArgumentException
这个方法会做这样一件事情,他将把你要代理的全部接口用一个由代码动态生成的类类实现,所有的接口中的方法都重写为调用 InvocationHandler.invoke()方法。这个类的代码类似于这样:
-
-
classAdderProxyextendsProxyimplementsAdder{
-
protectedAdderProxy(InvocationHandlerh){
-
super(h);
-
}
-
-
@Override
-
publicintadd(inta,intb){
-
try{
-
Methodm=Adder.class.getMethod("add",newClass[]{int.class,int.class});
-
Object[]args={a,b};
-
return(Integer)h.invoke(this,m,args);
-
}catch(Throwablee){
-
thrownewRuntimeException(e);
-
}
-
}
-
}
据api说,所有生成的代理类都是Proxy的子类。当然,生成的这个类的代码你是看不到的,而且Proxy里面也是调用sun.XXX包的api 生成;一般情况下应该是直接生成了字节码。然后,使用你提供的ClassLoader将这个类加载并实例化一个对象作为代理返回。
看明白这个方法后,我们来改造一下main()方法。
// Test.java
-
packagetest;
-
-
importjava.lang.reflect.InvocationHandler;
-
importjava.lang.reflect.Proxy;
-
-
publicclassTest{
-
publicstaticvoidmain(String[]args)throwsException{
-
Addercalc=newAdderImpl();
-
-
-
ClassLoaderloader=Test.class.getClassLoader();
-
-
Class[]interfaces={Adder.class};
-
-
InvocationHandlerh=newAdderHandler(calc);
-
-
calc=(Adder)Proxy.newProxyInstance(loader,interfaces,h);
-
-
-
-
-
-
-
-
intresult=calc.add(1,2);
-
System.out.println("Theresultis"+result);
-
}
-
}
输出结果会是什么呢?
Proxy: invoke add() at 2009-12-16 18:21:33
The result is 3
对比一下之前的结果,你会发现这点东西写了我一个多小时。再来看看JDK有多懒:target完全可以在代理类中生成。
实际方法都需要手动调用,可见代理类中重写所有的方法都只有一句话:return xxx.invoke(ooo);不过这么写也有他的理由,target自己管理,方法你爱调不调 ﹃_﹃;如果他调了,InvocationHandler接口中恐怕就需要两个方法了,还要判断返回、处理参数等等。
分享到:
相关推荐
- 动态代理(Dynamic Proxies):允许在运行时创建满足特定接口的代理类。 - 枚举(Enums):增强了常量的使用,可以定义枚举类型,支持枚举常量的比较和遍历。 - 集合框架增强:增加了`java.util.concurrent`包...
2. 动态代理:引入了`java.lang.reflect.Proxy`类,使得动态创建代理对象成为可能,便于实现AOP(面向切面编程)。 3. NIO.2:Java 7引入了NIO.2,但1.6版本已经包含了一些改进,如更好的文件系统访问API,异步I/O...
JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring会创建一个代理对象,这个代理对象实现了与目标对象相同的接口,并在调用接口方法时插入额外的切面逻辑。代理对象在运行...
JDK动态代理的核心API包括`java.lang.reflect.Proxy`和`java.lang.reflect.InvocationHandler`。 - **`java.lang.reflect.Proxy`**:提供了创建动态代理类和实例的方法。通过`newProxyInstance`方法,传入...
本压缩包包含关于三种主要的动态代理实现方式:JDK动态代理、CGLIB以及javassist的相关资料。 首先,JDK动态代理是Java标准库提供的一种动态代理机制,它依赖于java.lang.reflect包中的Proxy和InvocationHandler...
6. **动态代理**:动态代理提供了在运行时创建代理对象的能力,用于实现接口的动态方法调用。 7. **NIO.2**:Java 6引入了新的I/O API,提供了更好的异步I/O操作和文件系统访问。 8. **改进的JDBC**:对JDBC API...
JDK 1.5(代号Tiger)引入了许多重要的新特性,如增强的for循环(foreach),枚举类型,泛型,匿名内部类的改进,动态代理等,这些特性极大地提高了Java的生产力和代码的可读性。 总结来说,JDK 1.5.0_17是Java开发...
2. **动态代理**:允许创建在运行时动态生成的代理类。 3. **增强的Swing组件**:如JTabbedPane和JSplitPane的改进,以及更好的国际化支持。 API文档(Application Programming Interface)是开发者与JDK交互的...
- **动态代理类**:允许在运行时创建实现了指定接口的新类,这在实现AOP(面向切面编程)或事件处理中非常有用。 - **改进的XML处理**:增强了Java API for XML Processing (JAXP),支持XPath 2.0和XSLT 2.0,使...
4. **动态代理**:Java代理机制在1.6版本中得到扩展,支持动态生成代理类,使得在运行时创建和修改代理更加便捷。 5. **改进的XML处理**:JDK 1.6对DOM、SAX和StAX等XML解析API进行了改进,处理XML文档的速度和效率...
本资源"jdk-api-1.6-English.rar"提供了JDK 1.6版本的英文API文档,为Java开发者提供了详尽的参考信息。 **API文档的重要性和作用** 1. **学习和理解类库**:API文档详细列出了Java平台中的所有类、接口、方法和...
4. **动态代理**:JDK 6引入了动态代理类的实现,允许在运行时创建代理类,增强了面向切面编程(AOP)的能力。 5. **Swing增强**:Swing组件库得到了大量改进,包括更好的外观和感觉,以及新的组件和事件处理机制。...
JDK 1.8,也称为Java 8,引入了许多重要的新特性,例如Lambda表达式、函数式接口、Stream API和Date/Time API等,这些都极大地提高了代码的简洁性和效率。对于ActiveMQ这样的Java应用程序来说,JDK 1.8提供了必要的...
1. **JDK6**:引入了改进的Swing组件、动态代理增强、增强的for循环(foreach)、可变参数(varargs)、枚举类型的遍历、异常链以及注解(Annotations)。其中,注解是面向元数据编程的重要特性,可以用于编译时和...
3. **动态代理**:JDK 1.6引入了`java.lang.reflect.Proxy`类,允许开发者创建动态代理对象,方便实现回调和AOP(面向切面编程)。 4. **NIO.2**:新I/O API,也称为NIO.2,提供了更现代、更易用的文件系统操作接口...
- **动态代理类**:允许在运行时创建实现一组给定接口的新类。 - **NIO.2(New IO)**:提供了更高级别的文件系统操作,如异步I/O和路径操作。 - **改进的XML处理**:包括对StAX API的支持,使得XML处理更为高效...
3. **动态代理**:`java.lang.reflect.Proxy`类允许动态创建代理对象,实现接口的动态代理。 4. **增强的for循环**:也称为foreach循环,简化了迭代集合元素的代码。 5. **NIO.2**:添加了新的非阻塞I/O API,包括...
为了支持Java泛型,可能需要像cglib或javassist这样的类代理库。这些依赖库确保了json-lib能正确地执行其功能。 在Java开发中,使用jar包时,确保所有依赖项都已包含是非常重要的。如果不完整,可能会导致运行时...
8. **Java动态代理(Dynamic Proxies)**: JDK 8提供了更强大的动态代理机制,允许在运行时创建接口的实现类,这对于AOP(面向切面编程)和测试框架非常有用。 9. **Nashorn JavaScript引擎**: JDK 8引入了Nashorn ...
此外,JDK 6还引入了新的并发工具类、NIO.2文件系统接口、动态代理等增强功能,提高了开发效率和代码质量。 总之,"jdk-6u45-windows-x64.rar"是Java 6 64位版本的安装包,适用于Windows 64位系统,其安装与配置是...