动态代理
动态代理是相对于静态代理而提出的设计模式。在 Spring 中,有两种方式可以实现动态代理 —— JDK 动态代理和 CGLIB 动态代理。
JDK 动态代理
对于静态代理,一个代理只能代理一个对象,如果有多个对象需要被代理,就需要很多代理类,造成代码的冗余。JDK 代理的对象是动态生成的。JDK 代理的条件是被代理对象必须实现接口。
一个简单案例说明 JDK 动态代理的实现方式,如一个 Animal
接口中定义一个 eat()
方法,表示动物需要吃饭。
public interface Animal {
void eat();
}
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog 要啃骨头");
}
}
需要创建动态代理类,动态代理类需要实现 InvocationHandler 接口:
public class AnimalInvocationHandler implements InvocationHandler {
/**
* 被代理对象
*/
private Object target;
public Object bind(Object target) {
this.target = target;
// 通过反射机制,创建一个代理对象实例并返回。用户进行方法调用时使用
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// 方法执行前加一段逻辑
System.out.println("------调用前处理-------");
// 调用真正的业务
result = method.invoke(target, args);
System.out.println("------调用后处理-------");
return result;
}
}
在动态代理类中,在被代理的方法前后各加了一段输出逻辑,不会破坏原方法,测试代码:
public class JDKDynamicProxyTest {
@Test
public void testJDKDynProxy() {
// 被代理对象
Dog dog = new Dog();
// 动态代理类对象
AnimalInvocationHandler handler = new AnimalInvocationHandler();
// 代理对象
Animal proxy = (Animal) handler.bind(dog);
proxy.eat();
}
}
这种方式的调用前逻辑和调用后逻辑,不必在原有方法上进行修改,通过以上方式就可以实现。上边的实现方式就是 Spring AOP 的基本原理,只是 Spring 不需要开发人员自己维护代理类,其实已经帮开发人员生成了代理类。Spring AOP 的实现是通过在从程序运行时,根据具体的类对象和方法等信息动态生成了一个代理类的 class 文件的字节码,再通过 ClassLoader 将代理类加载到内存中,最后通过生成的代理对象进行程序的方法调用。
CGLIB 动态代理
在上面实现的 JDK 动态代理的方式中,不难发现 JDK 动态代理有一个缺点,即被代理类必须实现接口。这显然不能满足开发过程中的需要。有没有可能实现接口,直接就对 Java 类进行代理呢?这就需要 CGLIB 发挥作用了。
一个简单案例说明 CGLIB 是如何实现动态代理的:
public class Cat {
public void cry() {
System.out.println("喵喵喵");
}
}
CGLIB 动态代理的实现需要实现 MethodInterceptor 接口,重写 intercept() 方法:
public class CatMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 方法执行前加一段逻辑
System.out.println("------调用前处理-------");
// 对被代理对象方法的调用
Object object = methodProxy.invokeSuper(obj, objects);
System.out.println("------调用后处理-------");
return object;
}
}
以下测试代码:
public class CglibDynamicProxyTest {
@Test
public void testCglibDynProxy() {
Enhancer enhancer = new Enhancer();
// 被代理类:Cat
enhancer.setSuperClass(Cat.class);
// 设置回调
enhancer.setCallback(new CatMethodInterceptor());
// 生成代理对象
Cat cat = (Cat) enhancer.create();
// 调用代理类的方法
cat.cry();
}
}