什么是动态代理?
动态代理(Dynamic Proxy)是一种在运行时动态生成代理对象的技术。它是一种设计模式,用于在不修改原始对象的情况下,通过代理对象来间接访问原始对象,并在访问前后执行额外的操作。也就是说,动态代理通过生成代理对象,可以扩展原对象的功能。
动态代理是实现面向切面的编程AOP(Aspect Oriented Programming)的基础。切面的例子有日志、性能监控、权限检查、数据库事务等,它们在程序的很多地方都会用到,代码都差不多,但与某个具体的业务逻辑关系也不太密切,如果在每个用到的地方都写,代码会很冗余,也难以维护,AOP将这些切面与主体逻辑相分离,代码简单优雅得多。
在Java中,实现动态代理有两种方式:
一种是通过JAVA SDK提供的,另一种是使用第三方库,如cdlib;
JDK实现动态代理
JDK动态代理主要通过两个核心类来实现:Proxy和InvocationHandler。Proxy类用于动态创建代理类,而InvocationHandler接口负责处理代理对象的方法调用。通过实现InvocationHandler接口,开发人员可以在代理对象的方法调用前后添加自定义的逻辑,实现对原始对象的控制和增强。
通过JDK实现动态代理要求被代理类,必须为接口;
- 创建接口和实现类
interface IService{
void sayHello();
}
static class RealService implements IService{
@Override
public void sayHello() {
System.out.println("hello");
}
}
2.创建实现InvocationHandler的对象 ,在invoke方法调用代理对象的方法;
public simpleInvocationHandler(Object realObj) {
this.realObj = realObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("entering "+method.getName());
Object result = method.invoke(realObj, args);
System.out.println("leaving "+method.getName());
return result;
}
}
- 生成代理对象
public static void main(String[] args) {
RealService realService = new RealService();
IService proxyInstance=(IService)Proxy.newProxyInstance(IService.class.getClassLoader(), new Class<?>[]{IService.class},new simpleInvocationHandler(realService));
proxyInstance.sayHello();
}
输出为
entering sayHello
hello
leaving sayHello
public static Object newProxyInstance(ClassLoader loader,
Class<? >[] interfaces, InvocationHandler h)
它有三个参数,具体如下。
1)loader表示类加载器
2)interfaces表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中只有一个IService。
3)h的类型为InvocationHandler,它是一个接口,也定义在java.lang.reflect包中,它只定义了一个方法invoke,对代理接口所有方法的调用都会转给该方法。
newProxyInstance的返回值类型为Object,可以强制转换为interfaces数组中的某个接口类型。这里我们强制转换为了IService类型,需要注意的是,它不能强制转换为某个类类型,比如RealService,即使它实际代理的对象类型为RealService。
cglib实现动态代理
Java SDK动态代理的局限在于,它只能为接口创建代理,返回的代理对象也只能转换到某个接口类型,如果一个类没有接口,或者希望代理非接口中定义的方法,那就没有办法了。有一个第三方的类库cglib(https://github.com/cglib/cglib),可以做到这一点,Spring、Hibernate等都使用该类库。
cglib实现动态代理,首先要导入cglib库
Maven项目导入依赖如下
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
使用cglib生成代理对象如下
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class SimpleCGLibDemo {
static class RealService{
public void sayHello(){
System.out.println("hello CGLib");
}
}
static class SimpleInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("entering "+method.getName());
Object result = methodProxy.invokeSuper(o, args);
System.out.println("leaving "+method.getName());
return result;
}
}
private static <T> T getProxy(Class<T> cls){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
enhancer.setCallback(new SimpleInterceptor());
return (T)enhancer.create();
}
public static void main(String[] args) {
RealService proxy = getProxy(RealService.class);
proxy.sayHello();
}
}
RealService表示被代理的类,它没有接口。getProxy()为一个类生成代理对象,这个代理对象可以安全地转换为被代理类的类型,它使用了cglib的Enhancer类。Enhancer类的setSuperclass设置被代理的类,setCallback设置被代理类的public非final方法被调用时的处理类。Enhancer支持多种类型,这里使用的类实现了MethodInterceptor接口,它与Java SDK中的InvocationHandler有点类似,方法名称变成了intercept,多了一个MethodProxy类型的参数。
与前面的InvocationHandler不同,SimpleInterceptor中没有被代理的对象,它通过MethodProxy的invokeSuper方法调用被代理类的方法:
Object result = proxy.invokeSuper(object, args);
注意,它不能这样调用被代理类的方法:
Object result = method.invoke(object, args);
object是代理对象,调用这个方法还会调用到SimpleInterceptor的intercept方法,造成死循环。
cglib的实现机制与Java SDK不同,它是通过继承实现的,它也是动态创建了一个类,但这个类的父类是被代理的类,代理类重写了父类的所有public非final方法,改为调用Callback中的相关方法,在上例中,调用SimpleInterceptor的intercept方法。