为什么JDK代理只能对接口实现代理?
1、动态代理的几种方式
Java主要有两种代理,JDK和Cglib动态代理。先看JDK代理实例如下:
JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。
JDK创建代理有一个限制,即它只能为接口创建代理实例。举个例子如下:
public interface Advice {
void beforeMethod();
void afterMethod();
}
public class TimeAdvice implements Advice {
long startTime;
long endTime;
public void beforeMethod() {
startTime = System.nanoTime(); // 获取开始时间
System.out.println("开始计算程序运行时间" + startTime);
}
public void afterMethod() {
endTime = System.nanoTime(); // 获取结束时间
System.out.println("计算程序运行时间: " + (endTime - startTime) + "ns");
}
}
public interface SalaryInterface {
public void doSalary();
}
public class Salary implements SalaryInterface{
public void doSalary() {
System.out.println("进行薪资计算的逻辑处理");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 每一个代理实例都必须指定一个调用处理器,代理对象调用方法时,
* 该方法会指派到调用处理器的invoke()中去。代理的方法封装成
* invoke中的method对象,其中的参数封装成Object[].
*/
public class MyProxy implements InvocationHandler{
private Object obj; // 希望被代理的对象
private Advice advice;
// 绑定代理对象
public Object bind(Object obj, Advice advice) {
this.obj = obj;
this.advice = advice;
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(), // 类加载器
obj.getClass().getInterfaces(), // 创建目标类所需要使用的一组接口
this // 一个实现InvocationHandler的实例,用来整合横切与业务逻辑
);
}
/*
* 实现代理
* method为方法名,args为代理实例某一方法的入参数组,而obj为所属的实例对象
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
advice.beforeMethod();
result = method.invoke(obj, args);
advice.afterMethod();
} catch (Exception e){
e.printStackTrace();
}
return result;
}
}
public class Bootstrap {
public static void main(String[] args) {
Advice advice = new TimeAdvice();
SalaryInterface p = new Salary();
// Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象
MyProxy proxy = new MyProxy();
SalaryInterface y = (SalaryInterface)proxy.bind(p, advice);
y.doSalary(); // 相当于调用proxy.invoke(proxy, "doSalary, null);
}
}
开始计算程序运行时间43041762316001 进行薪资计算的逻辑处理 计算程序运行时间: 882610ns
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。
CGGlib创建的代理对象要比JDK的性能高很多,但是创建时所花费的时间却比JDK动态代理要多。所以对于singleton的代理对象或者具有实例池的代码,由于无须频繁创建代码对象,用CGLib比较合适。也就是生命周期长的实例用CGLib比较合适。并且无法对final方法进行代理
/**
* 使用cglib动态代理
*/
public class Cglib implements MethodInterceptor {
private Object target;
long startTime;
long endTime;
/**
* 创建代理对象
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass()); // 设置需要创建子类的类
enhancer.setCallback(this); // 回调方法
return enhancer.create(); // 通过字节码技术动态创建子类实例
}
@Override
// 回调方法 ,拦截所有的父类方法调用
public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {
startTime = System.nanoTime(); // 获取开始时间
System.out.println("开始计算程序运行时间" + startTime);
Object result = proxy.invokeSuper(obj, args); // 通过代码类调用父类中的方法
endTime = System.nanoTime(); // 获取结束时间
System.out.println("计算程序运行时间: " + (endTime - startTime) + "ns");
return result;
}
}
/*
* 采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截
* 所有父类方法的调用,并顺势织入横切逻辑
*
* singletom的代理对象或者具有实例池的代理,因为无须频繁创建代理对象,比较适合用CGLib动态代理技术
*
* 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行代理
*/
public class TestCglib {
public static void main(String[] args) {
Cglib cglib=new Cglib();
Salary salary=(Salary)cglib.getInstance(new Salary());
salary.doSalary();
}
}