什么是动态代理
在Java中,动态代理是一种机制,它允许程序在运行时动态地生成代理类,从而可以在不修改源代码的情况下,为原有的类提供额外的功能或者控制访问。
动态代理主要通过反射机制来实现,其基本原理是:在运行时创建一个实现了指定接口的代理类对象,然后将请求转发给实际的目标对象,并在转发过程中执行额外的操作。
由于动态代理可以在运行时生成代理类,因此它比静态代理更加灵活和易于扩展,常用于实现各种框架、AOP等技术。
如何实现动态代理
实现动态代理的方式有两种,一种是使用Java中的动态代理。一种是使用Cglib提供动态代理。
假设现在有一个需求,需要在不改变原有代理逻辑的情况下,输出代码的执行时间,此时我们可以使用动态代理来实现,使用动态代理将原有逻辑进行增强。
java实现动态代理
现在我们有一个接口:
public interface Calculator {
int add(int a,int b);
int minus(int a,int b);
}
然后他有一个实现类:
public class CalculatorImpl implements Calculator{
@Override
public int add(int a, int b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
@Override
public int minus(int a, int b) {
System.out.println(a + "-" + b + "=" + (a - b));
return a - b;
}
}
方法非常的简单,就是一个简单的加减运算。下面我们需要再每次执行运算后输出这个方法执行了多长时间,使用Java的动态代理来实现:
public class ProxyDemo01 {
public static void main(String[] args) {
CalculatorImpl calculatorImpl = new CalculatorImpl();
/**
* 1.classloader
* 2.将来生成的代理对象实现的接口/被代理对象实现的接口,
*
*Proxy.newProxyInstance 方法返回值其实就是生成的代理对象,这个对象是系统自动为Calculator
* 提供的一个实现类,这个类就是(com.sun.proxy.$Proxy0),相当于系统重,现在Calculator接口有两个实现类,一个是自动生成的
* 一个是CalculatorImpl。
* 我们实际上调用的是自动生成的代理对象中的 add或者minus方法。
*
* 自动生成的代理对象,逻辑类似于下面这样:
* public class $Proxy0 implements Calculator{
* public int add(int a,int b){
* long s = System.nanoTime();
* //执行反射中的方法
* Object invoke = method.invoke(calculatorImpl, args);
* long end = System.nanoTime();
* System.out.println("执行方法耗时:"+(end-s));
* return invoke;
* }
*
* }
*/
Calculator calculator = (Calculator) Proxy.newProxyInstance(ProxyDemo01.class.getClassLoader(), new Class[]{Calculator.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long s = System.nanoTime();
Object invoke = method.invoke(calculatorImpl, args);
long end = System.nanoTime();
System.out.println("执行方法耗时:"+(end-s));
return invoke;
}
});
calculator.add(2,1);
}
}
我们使用Proxy.newProxyInstance()方法来创建一个被代理了的增强类。这样就能够实现输出方法的执行时间了,而且不改变原有的逻辑。最后我们使用新生成的增强代理类去执行相应的方法即可。
然后代理类的大概逻辑可以参考代码中的注释部分。代理类也相当于是实现了Calculator 接口的一个实现类。
Proxy.newProxyInstance 方法返回值其实就是生成的代理对象:这句话的解释我们来看一下:
//我们改用实现类来接收这个返回值。
CalculatorImpl calculator = (CalculatorImpl) Proxy.newProxyInstance(ProxyDemo01.class.getClassLoader(), new Class[]{Calculator.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long s = System.nanoTime();
Object invoke = method.invoke(calculatorImpl, args);
long end = System.nanoTime();
System.out.println("执行方法耗时:"+(end-s));
return invoke;
}
});
运行后会报下面的错误:
可以看出是类强转错误,说明动态代理是重新生成了一个增强的代理类com.sun.proxy.$Proxy0,去执行代码逻辑。
Cglib实现动态代理
CGLIB(Code Generation Library),是一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类与实现 Java 接口。
同样的还是上边那个需求我们使用Cglib来实现:
现在有一个Dog类:
public class Dog {
public void eat(){
System.out.println("dog eat");
}
}
此时如果使用Cglib的动态代理的话需要这样来写:
public class DogInterceptor implements MethodInterceptor {
/**
* 这个方法类似于 InvocationHandler 方法
* @param o 代理对象
* @param method 代理方法
* @param objects 方法的参数
* @param methodProxy 方法对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long s = System.nanoTime();
Object o1 = methodProxy.invokeSuper(o, objects);
long end = System.nanoTime();
System.out.println("执行方法耗时:"+(end-s));
return o1;
}
}
需要写一个类实现MethodInterceptor 接口,然后重新方法。这个方法类似于 InvocationHandler 方法。
然后写一个main方法来执行这个增强类:
public class ProxyDemo02 {
public static void main(String[] args) {
//创建一个字节码增强器
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(Dog.class);
//设置代理类
enhancer.setCallback(new DogInterceptor());
//这里拿到的dog对象,实际上不是自己定义的Dog对象,而是通过动态代理为Dog类 自动生成的子类对象
Dog dog = (Dog) enhancer.create();
dog.eat();
}
}
这样就可以通过动态代理的方式进行代码功能的增强了。
总结
两者的区别:
- JDK 动态代理:被代理的对象要有接口,才能使用 JDK 动态代理。
- CGLIB动态代理:所有的对象都能代理,无论被代理的对象有没有接口。