一、原理
动态(程序运行时实现和目标类相同接口的java类)代理(类似购物中介一样为使用目标类方法的测试类在该方法添加注释等补充内容)
目标类:测试类需要使用的方法所在的类
二、实现
1.目标类接口
package com.jd.calculator;
public interface ICalculatorService {
int add(int a,int b);
int sub(int a,int b);
int mul(int a,int b);
int div(int a,int b);
}
2.目标类
package com.jd.calculator;
public class CalculatorService implements ICalculatorService {
@Override
public int add(int a, int b) {
int result = a+b;
return result;
}
@Override
public int sub(int a, int b) {
int result = a-b;
return result;
}
@Override
public int mul(int a, int b) {
int result = a*b;
return result;
}
@Override
public int div(int a, int b) {
int result = a/b;
return result;
}
}
3.测试类
package com.jd.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.jd.calculator.CalculatorService;
import com.jd.calculator.ICalculatorService;
public class Test {
CalculatorService calculatorService;
public Test(CalculatorService calculatorService) {
this.calculatorService = calculatorService;
}
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println(calculatorService.getClass().getName()+":The "+name+" method begins.");
System.out.println(calculatorService.getClass().getName()+":Parameters of the "+name+" method:["+args[0]+","+args[1]+"]");
Object result = method.invoke(calculatorService, args);//目标方法
System.out.println(calculatorService.getClass().getName()+":Result of the "+name+" method:"+result);
System.out.println(calculatorService.getClass().getName()+":The "+name+" method ends.");
return result;
}
};
public Object get() {
return Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {ICalculatorService.class}, h);//产生一个动态class类,
}
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Test test = new Test(new CalculatorService());
ICalculatorService calculatorService = (ICalculatorService) test.get();//获取代理对象
int result = calculatorService.add(1, 1);
System.out.println("-->"+result);
}
}
三、具体过程
首先我们需要写public Object get() {
return Proxy.newProxyInstance(loader, interfaces, h);//产生一个动态class类对象,
}
注:loader表示类装载器,即程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中。
则可以调用Test的类装载器(Test.class.getClassLoader())
interfaces表示目标类的接口的类,之所以数量不定是因为目标类可能继承一或多个接口 (new Class[] {ICalculatorService.class})
h类型为InvocationHandler,为继承InvocationHandler接口的类的对象,此类继承接口的public Object invoke(Object proxy, Method method, Object[] args)的方法。其中(proxy为动态生成的代理类的对象,method为ICalculatorService接口的方法
由动态生成类对象构造方法public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}及目标方法public final int add(int i, int j)
{
try
{
return ((Integer)super.h.invoke(this, m3, new Object[] {
Integer.valueOf(i), Integer.valueOf(j)
})).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}得到动态class生成对象应用了h的invoke方法,则this即表明proxy为动态生成的代理类的对象,m3由
private static Method m3;
static
{
try
{
m3 = Class.forName("com.jd.calculator.ICalculatorService").getMethod("add", new Class[] {
Integer.TYPE, Integer.TYPE
});
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
表明method为ICalculatorService接口的方法。
),args为方法的参数。
(原理:Proxy.newProxyInstance方法借助Class<?> cl = getProxyClass0(loader, intfs);创建动态class类
final Constructor<?> cons = cl.getConstructor(constructorParams);找到此动态class类的带constructorParams属性的构造方法,而constructorParams原型: private static final Class<?>[] constructorParams =
{ InvocationHandler.class };表示动态class类中有InvocationHandler属性
return cons.newInstance(new Object[]{h});表示创建带有InvocationHandler属性的一个动态class类对象
)
然后由CalculatorService calculatorService构造目标类对象属性,再由测试类构造方法为calculatorService赋值。接下来 InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println(calculatorService.getClass().getName()+":The "+name+" method begins.");
System.out.println(calculatorService.getClass().getName()+":Parameters of the "+name+" method:["+args[0]+","+args[1]+"]");
Object result = method.invoke(calculatorService, args);//目标方法
System.out.println(calculatorService.getClass().getName()+":Result of the "+name+" method:"+result);
System.out.println(calculatorService.getClass().getName()+":The "+name+" method ends.");
return result;
}
};创建内部类对象,且继承invoke方法,String name = method.getName();为接下来的增强确定目标方法的名字
Object result = method.invoke(calculatorService, args);前面两行代码为前补充(类比房产中介牵线搭桥),后面两行代码为后收尾(类比房产中介帮忙办理手续等业务),其中calculatorService.getClass().getName()获取calculatorService类信息,Parameters of the "+name+" method:["+args[0]+","+args[1]+"]"表明args为方法的参数。而Object result = method.invoke(calculatorService, args);为引用目标方法,之所以method为ICalculatorService接口的方法却可以使用CalculatorService类的方法是因为多态,即使用了上转型对象。
最后输出结果。
main方法先创建测试类对象并使用带有属性calculatorService的构造方法,之后再利用上转型对象ICalculatorService calculatorService = (ICalculatorService) test.get();获取代理对象,最后使用calculatorService的目标方法得到结果。
四、JDK动态代理与CGLib动态代理区别:
1、JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现;
2、JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。