我们大家都知道,一般情况下,如果一个接口没有实现类的话,我们直接调用该接口的方法会报错。但我们用过的Mybatis中Dao接口,或者Spring Data JPA接口,其实我们没有手动为他们编写实现类,那为什么仍然能正常调用呢?这里就涉及到了动态代理。下面我演示一下,如何利用JDK为一个接口创建动态实现。
代码目录大致如下,在com.company.proxy包下有三个类文件
一、SaleService 这个是一个接口声明,后面我们将为该接口创建一个动态实现。其源码如下:
package com.company.proxy;
/**
*
* @author LICHUANG
*/
public interface SaleService {
/**
* 出售
* @param goodsName 商品名称
* @param price 价格
* @return
*/
String sale(String goodsName,Integer price);
}
二、SaleInvocation 该类实现了InvocationHandler接口,并重写其invoke方法。其源码如下:
package com.company.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
*
* @author LICHUANG
*/
public class SaleInvocation implements InvocationHandler {
private Class<?> clazz;
public SaleInvocation(Class<?> o) {
this.clazz = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//一般DEBUG时,我们查看变量时,会调用其toString()方法,由于该方法也会被代理,所以出现递归调用,
//这里为了避免DEBUG时,重复调用方法,所以作特殊处理
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this,args);
}
System.out.println("调用前");
//这里是为接口的方法构造实现逻辑,
// 由于我们已经获取到了方法名称和参数值,所以可以根据实际需要去创建实现逻辑。
Object result = "方法名称:" + method.getName() + ",参数值:" + Arrays.toString(args) + ",clazz:" + clazz;
System.out.println("调用后result:" + result);
return result;
}
}
三、ProxyTest 这个类主要是包含main方法,用来测试的。其源码如下:
package com.company.proxy;
import java.lang.reflect.Proxy;
/**
*
*
* @author LICHUANG
*/
public class ProxyTest {
public static void main(String[] args) {
Class<?> clazz = SaleService.class;
SaleInvocation invocation = new SaleInvocation(clazz);
SaleService proxyObject = (SaleService) Proxy.newProxyInstance(clazz.getClassLoader(),new Class[] {clazz},invocation);
String result = proxyObject.sale("商品名称",5);
System.out.println("得到返回结果:" + result);
}
}
可以注意到,以上SaleService接口,我并没有手动编写任何实现类,但我仍然能正常调用其String sale(String goodsName,Integer price)方法。
最后main方法运行结果如下:
调用前
调用后result:方法名称:sale,参数值:[商品名称, 5],clazz:interface com.company.proxy.SaleService
得到返回结果:方法名称:sale,参数值:[商品名称, 5],clazz:interface com.company.proxy.SaleService
Process finished with exit code 0