这个也是去年面试遇到的问题,年过了记录一下,西安不知名的小公司,名字都忘记了,记得当时回答上来了,但是不够全面。
Java 中的动态代理是一种在运行时创建代理对象的机制,这使得我们可以在运行时动态地创建和使用接口的代理对象,而无需在编译时确定具体的实现类。动态代理通常用于实现 AOP(面向切面编程)和其他类似的横切关注点。
简述
它允许在运行时动态地创建代理类和代理对象,从而可以在代理对象上拦截对真实对象的方法调用,并在调用前后执行自定义的逻辑。动态代理通常用于实现一些横切关注点(cross-cutting concerns),如日志记录、性能监控、事务管理、安全检查等。
动态代理的一些常见应用包括:
- AOP(面向切面编程): 动态代理是 AOP 的核心实现机制之一。通过动态代理,可以在不修改原始类代码的情况下,为类的方法添加额外的功能,如日志记录、性能监控、事务管理等。
- 远程代理: 动态代理可以用于实现远程调用(RPC)。在远程调用中,客户端通过代理对象调用远程服务器上的方法,而不需要知道具体的网络通信细节。
- 延迟加载: 动态代理可以用于延迟加载对象。在某些情况下,我们可能希望在首次访问对象时才进行初始化,而不是在对象创建时立即进行初始化。
- 安全控制: 动态代理可以用于实现安全控制机制。例如,可以在代理对象中检查用户的权限,然后决定是否允许执行特定的操作。
- 缓存: 动态代理可以用于实现缓存机制。例如,可以在代理对象中检查是否存在缓存结果,并在缓存中找到结果时直接返回,而不需要执行真正的方法调用。
为什么需要动态代理呢?
动态代理的出现主要是为了实现面向切面编程(AOP)中的横切关注点(cross-cutting concerns)。在实际开发中,很多功能可能会散布在应用程序的多个模块中,例如日志记录、事务管理、权限控制等。如果每个模块都直接嵌入到业务逻辑中,会导致代码重复、耦合度高、维护困难等问题。动态代理通过将这些横切关注点与核心业务逻辑分离开来,使得代码更加模块化、可维护性更高,并且提高了代码的重用性和灵活性。
Java 中动态代理主要有两种实现方式:
-
基于接口的动态代理(JDK 动态代理): 基于 Java 标准库中的
java.lang.reflect.Proxy
类和InvocationHandler
接口。该机制要求目标对象必须实现至少一个接口。 -
基于类的动态代理(CGLIB 动态代理): 使用第三方库 CGLIB(Code Generation Library)实现,不要求目标对象实现接口。
JDK 动态代理
JDK 动态代理通过 Proxy.newProxyInstance
方法创建代理对象。代理对象实现了指定的接口,并将方法的调用委托给实现了 InvocationHandler
接口的代理处理器。
以下是一个简单的 JDK 动态代理的示例:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 接口
interface MyInterface {
void doSomething();
}
// 实现类
class MyInterfaceImpl implements MyInterface {
@Override
public void doSomething() {
System.out.println("Real object is doing something.");
}
}
// InvocationHandler 实现
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method invocation");
Object result = method.invoke(target, args);
System.out.println("After method invocation");
return result;
}
}
public class DynamicProxyExample {
public static void main(String[] args) {
// 创建真实对象
MyInterface realObject = new MyInterfaceImpl();
// 创建代理处理器
MyInvocationHandler handler = new MyInvocationHandler(realObject);
// 创建代理对象
MyInterface proxyObject = (MyInterface) Proxy.newProxyInstance(
DynamicProxyExample.class.getClassLoader(),
new Class[]{MyInterface.class},
handler
);
// 通过代理对象调用方法
proxyObject.doSomething();
}
}
CGLIB 动态代理
CGLIB 动态代理通过创建目标对象的子类来实现代理。CGLIB 动态代理不要求目标对象实现接口,但不能代理 final
类。
以下是一个简单的 CGLIB 动态代理的示例:
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类
class MyTargetClass {
public void doSomething() {
System.out.println("Real object is doing something.");
}
}
// MethodInterceptor 实现
class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method invocation");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method invocation");
return result;
}
}
public class CGLIBProxyExample {
public static void main(String[] args) {
// 创建目标对象
MyTargetClass targetObject = new MyTargetClass();
// 创建代理拦截器
MyMethodInterceptor methodInterceptor = new MyMethodInterceptor();
// 创建 Enhancer 对象,设置父类和拦截器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyTargetClass.class);
enhancer.setCallback(methodInterceptor);
// 创建代理对象
MyTargetClass proxyObject = (MyTargetClass) enhancer.create();
// 通过代理对象调用方法
proxyObject.doSomething();
}
}
无论是 JDK 动态代理还是 CGLIB 动态代理,都是在运行时生成代理类,并动态地处理被代理方法的调用。选择使用哪一种取决于具体的需求和场景,例如是否需要代理非接口类型的类,是否要求被代理的类实现接口等。