Java的动态代理
以下是Java动态代理的逐步解释:
Java动态代理详解
1. 动态代理的核心组件
java.lang.reflect.Proxy:生成代理对象的工具类。java.lang.reflect.InvocationHandler:代理对象的方法调用处理器接口。
2. 实现步骤
步骤1:自定义接口
public interface UserService {
void addUser(String username);
void deleteUser(String username);
}
步骤2:实现自定接口
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("添加用户: " + username);
}
@Override
public void deleteUser(String username) {
System.out.println("删除用户: " + username);
}
}
步骤3:实现InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LoggingHandler implements InvocationHandler {
private final Object target; // 被代理的对象
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法执行前逻辑
System.out.println("调用方法前: " + method.getName());
// 执行目标方法
Object result = method.invoke(target, args);
// 方法执行后逻辑
System.out.println("调用方法后: " + method.getName());
return result;
}
}
步骤4:生成代理对象
import java.lang.reflect.Proxy;
public class DynamicProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService realService = new UserServiceImpl();
// 创建InvocationHandler实例
InvocationHandler handler = new LoggingHandler(realService);
// 生成代理对象
UserService proxyService = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(), // 类加载器
new Class[]{UserService.class}, // 代理的接口数组
handler // InvocationHandler实例
);
// 通过代理对象调用方法
proxyService.addUser("Alice");
proxyService.deleteUser("Bob");
}
}
3. 输出结果
调用方法前: addUser
添加用户: Alice
调用方法后: addUser
调用方法前: deleteUser
删除用户: Bob
调用方法后: deleteUser
4. 动态代理的底层原理
- 代理类生成:
Proxy.newProxyInstance()在运行时动态生成代理类字节码。 - 代理类结构:生成的代理类继承
Proxy并实现指定接口。 - 方法调用流程:
- 客户端调用代理对象方法(如
addUser())。 - 代理类将调用转发到
InvocationHandler.invoke()。 invoke()方法执行前置逻辑 → 调用目标方法 → 执行后置逻辑。
- 客户端调用代理对象方法(如
5. 动态代理的限制与解决方案
| 限制 | 说明 | 解决方案 |
|---|---|---|
| 只能代理接口 | 无法直接代理没有接口的类 | 使用CGLIB库(如Spring AOP默认行为) |
| 性能开销 | 反射调用比直接调用慢约5-20倍 | 对性能敏感场景使用AspectJ编译时织入 |
| 无法代理final方法 | final方法无法被重写 | 避免代理final方法或使用CGLIB |
6. 动态代理的典型应用场景
- AOP实现:日志、事务、权限校验等横切关注点。
- 远程方法调用(RPC):如Dubbo、gRPC的客户端代理。
- 延迟加载:例如Hibernate的延迟初始化代理。
- Mock测试:动态生成测试替身(Test Double)。
7. 动态代理与CGLIB对比
| 特性 | JDK动态代理 | CGLIB |
|---|---|---|
| 代理方式 | 基于接口 | 基于类继承 |
| 性能 | 较慢(反射调用) | 较快(方法调用无反射) |
| 依赖 | 无需第三方库 | 需要引入CGLIB库 |
| 方法限制 | 无法代理无接口的类 | 无法代理final方法或类 |
8. 高级技巧
查看生成的代理类字节码
// 在启动JVM时添加以下参数
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
处理Object方法(如toString)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
// 直接调用Object方法,不进行代理增强
return method.invoke(target, args);
}
// 其他方法的代理逻辑
// ...
}
动态代理实现RPC框架伪代码
public class RpcProxy implements InvocationHandler {
private final String host;
private final int port;
public RpcProxy(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 1. 序列化请求参数
byte[] requestData = serialize(method, args);
// 2. 发送网络请求
byte[] responseData = sendRequest(host, port, requestData);
// 3. 反序列化响应结果
return deserialize(responseData, method.getReturnType());
}
}
9. 总结
- 核心机制:通过反射动态生成代理类,拦截方法调用。
- 适用场景:需要无侵入式增强方法功能的场合。
- 性能权衡:灵活性与性能之间的取舍,需根据场景选择方案。
- 扩展应用:结合其他技术(如注解、反射工厂)可实现更复杂的功能增强。
Java 的动态代理是一种在运行时创建接口实现的技术,允许开发者在不修改源代码的情况下,为接口添加行为(例如日志记录、事务管理等)。动态代理基于 Java 的反射机制,并且格外适用于需要代理接口的情况。
以下是 Java 动态代理的几个关键点:
-
接口:动态代理只能代理接口,不能代理类。这意味着你需要有一个或多个接口来定义要代理的方法。
-
InvocationHandler接口:实现这个接口的类将定义在调用代理对象的方法时应该执行的操作。这个接口有一个方法invoke,它会在代理对象的方法被调用时执行。 -
Proxy类:这是 Java 提供的用于创建动态代理实例的类。它有两个静态方法newProxyInstance,用于生成代理对象。
示例代码
以下是一个简单的 Java 动态代理示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口
interface HelloService {
void sayHello();
}
// 实现这个接口的类
class HelloServiceImpl implements HelloService {
@Override
public void sayHello() {
System.out.println("Hello, World!");
}
}
// 实现 InvocationHandler 接口的类
class HelloServiceInvocationHandler implements InvocationHandler {
private Object target;
public HelloServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法调用前添加逻辑
System.out.println("Before method call");
// 调用目标方法
Object result = method.invoke(target, args);
// 在方法调用后添加逻辑
System.out.println("After method call");
return result;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
// 创建原始对象
HelloService helloService = new HelloServiceImpl();
// 创建 InvocationHandler 实例
InvocationHandler handler = new HelloServiceInvocationHandler(helloService);
// 创建代理对象
HelloService proxyInstance = (HelloService) Proxy.newProxyInstance(
helloService.getClass().getClassLoader(),
helloService.getClass().getInterfaces(),
handler
);
// 调用代理对象的方法
proxyInstance.sayHello();
}
}
输出结果
Before method call
Hello, World!
After method call
解释
- 接口:
HelloService接口定义了一个方法sayHello。 - 实现类:
HelloServiceImpl实现了HelloService接口。 InvocationHandler:HelloServiceInvocationHandler实现了InvocationHandler接口,并在invoke方法中添加了前置和后置逻辑。- 创建代理对象:使用
Proxy.newProxyInstance方法创建代理对象,并传入目标对象、目标对象的接口和InvocationHandler实例。 - 调用代理方法:调用代理对象的方法时,会触发
InvocationHandler的invoke方法,并在其中执行前置和后置逻辑。
通过这种方式,你可以在运行时动态地为接口添加行为,而无需修改接口实现类的代码。
Java的动态代理
什么是Java动态代理?
Java动态代理是Java提供的一种代理机制,允许在运行时动态生成代理类并为接口创建代理对象[@1@][@3@]。这种代理类能够拦截接口方法的调用,并在调用过程中添加自定义逻辑。
Java动态代理的工作原理
Java动态代理的工作原理主要依赖于Java反射机制和java.lang.reflect.Proxy类。在程序运行时,通过反射获取目标对象的接口信息,并动态生成一个实现了这些接口的代理类。这个代理类会重写接口中的方法,并在方法调用时,将调用转发给一个调用处理器(InvocationHandler)。调用处理器中的invoke方法会负责执行实际的逻辑,包括在方法调用前后添加自定义代码[@1@][@2@]。
简单的Java动态代理实现示例
以下是一个简单的Java动态代理实现示例,展示了如何为接口Star创建代理对象,并在方法调用前后添加日志记录:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 目标接口
interface Star {
String sing(String name);
void dance();
}
// 目标类的实现
class BigStar implements Star {
private String name;
public BigStar(String name) {
this.name = name;
}
public String sing(String name) {
System.out.println(this.name + "正在唱歌儿:" + name);
return "谢谢儿!谢谢儿!";
}
public void dance() {
System.out.println(this.name + "正在跳舞儿");
}
}
// 动态代理处理器
class ProxyUtil implements InvocationHandler {
private Object target;
public ProxyUtil(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法调用前添加日志
System.out.println("准备调用方法: " + method.getName());
// 执行目标方法
Object result = method.invoke(target, args);
// 在方法调用后添加日志
System.out.println("方法调用结束: " + method.getName());
return result;
}
// 创建代理对象的方法
public static Star createProxy(BigStar bigStar) {
return (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
new Class[]{Star.class},
new ProxyUtil(bigStar)
);
}
}
// 测试类
public class Test {
public static void main(String[] args) {
BigStar bigStar = new BigStar("杨超越");
Star starProxy = ProxyUtil.createProxy(bigStar);
starProxy.sing("好日子");
starProxy.dance();
}
}
Java动态代理的使用场景和优势
Java动态代理广泛应用于日志记录、性能监控、权限控制、事务管理等领域[@1@][@3@]。其优势在于能够在不修改原始业务代码的情况下,灵活地为目标方法添加额外的逻辑。这种“解耦”的特性使得动态代理成为实现AOP(面向切面编程)的关键技术之一。
使用Java动态代理时可能遇到的限制或问题
使用Java动态代理时,需要注意以下限制或问题:
- 只能代理接口:Java动态代理只能为接口创建代理对象,无法直接代理类。如果需要代理类,可以考虑使用CGLib等第三方库[@2@]。
- 性能开销:由于动态代理涉及到反射机制和代理类的生成,因此在高性能要求的场景下,可能会带来一定的性能开销。
- 代码可读性:动态代理的代码往往比直接实现逻辑的代码更加复杂,可能会降低代码的可读性和可维护性。因此,在使用动态代理时,需要权衡其灵活性和代码可读性之间的关系。
Java的动态代理是一种在运行时创建代理对象的技术,它允许开发者在不修改原始类的情况下,对类的方法进行控制和扩展。这种技术在很多高级编程场景中非常有用,尤其是在需要实现AOP(面向切面编程)、事务管理、日志记录等功能时。
动态代理的基本概念
动态代理的核心在于它能够在程序运行期间动态地创建代理对象,并且这些代理对象可以拦截方法调用,从而实现在调用前后执行额外的操作。Java提供了两种主要的动态代理方式:JDK原生动态代理和CGLIB动态代理。
JDK原生动态代理
JDK提供的动态代理机制是基于接口的。这意味着你只能为实现了接口的类生成代理对象。JDK动态代理通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。下面是一个简单的例子:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call");
Object result = method.invoke(new HelloWorld(), args);
System.out.println("After method call");
return result;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
new Class[] { Hello.class },
handler);
hello.morning("Bob");
}
}
在这个例子中,我们定义了一个InvocationHandler,它负责处理代理对象上的所有方法调用。然后使用Proxy.newProxyInstance()方法创建一个实现了Hello接口的代理对象。
CGLIB动态代理
与JDK动态代理不同的是,CGLIB(Code Generation Library)可以为没有实现任何接口的普通类创建代理对象。它通过继承目标类的方式来实现代理,并通过重写目标类的方法来添加额外的行为。由于CGLIB代理是基于类继承的,因此不能代理final类或final方法。
动态代理的应用场景
动态代理广泛应用于许多框架和技术中,例如Spring AOP、Hibernate数据查询、测试框架的后端mock等。在Spring AOP中,动态代理用于实现面向切面编程,允许将横切关注点(如事务管理、安全性检查、日志记录等)从业务逻辑代码中分离出来,这样可以使业务逻辑更加清晰,便于维护。
此外,动态代理还常用于远程过程调用(RPC),在这种情况下,客户端并不直接调用远程服务的方法,而是通过本地代理对象来间接调用。这个代理对象会负责网络通信、序列化/反序列化等底层细节,使得远程调用看起来就像本地调用一样。
动态代理的工作原理
在JVM层面,静态代理在编译时就已经确定了所有的代理关系,并生成了相应的字节码文件;而动态代理则是在运行时动态生成类字节码,并加载到JVM中的。这使得动态代理具有更高的灵活性,因为它可以在运行时根据需求调整代理行为,而不需要重新编译代码。
总结来说,Java的动态代理提供了一种强大的工具,用于在运行时动态地增强现有类的功能,而无需修改其源代码。这对于构建高度模块化的应用程序,以及实现复杂的系统架构至关重要。无论是通过JDK提供的原生支持还是第三方库如CGLIB,开发者都能够利用动态代理来简化代码结构,提高代码的可维护性和复用性。
Java的动态代理是一种强大的机制,它允许在运行时创建接口的实现类实例,并将方法调用委托给一个InvocationHandler。这种技术广泛应用于各种框架中,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用等。
动态代理的核心概念
Proxy 类和 InvocationHandler 接口
在Java中,动态代理主要依赖于两个核心组件:Proxy类和InvocationHandler接口。Proxy类提供了一个静态方法newProxyInstance()用于创建代理对象。这个方法接受三个参数:
- ClassLoader:指定代理类的类加载器。
- Class[] interfaces:代理类需要实现的接口列表。
- InvocationHandler:定义了代理对象的方法调用逻辑。
InvocationHandler接口则包含了一个唯一的方法invoke(Object proxy, Method method, Object[] args),该方法会在代理对象上调用任何方法时被触发。
创建动态代理对象
要创建一个动态代理对象,首先需要定义一个接口以及其实现类(可选)。然后,通过实现InvocationHandler接口来定义方法调用的处理逻辑。最后,使用Proxy.newProxyInstance()方法生成代理对象。例如:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
new Class[] { Hello.class },
new MyInvocationHandler());
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}
class MyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + " is called with argument: " + args[0]);
return null;
}
}
JDK动态代理 vs CGLIB
Java提供了JDK动态代理机制,但是这种方式仅适用于实现了接口的类。如果需要对没有实现任何接口的普通类进行代理,则可以使用CGLIB库。CGLIB通过继承的方式实现代理,因此可以对任何非final类进行代理。
应用场景
动态代理的一个典型应用场景是AOP(面向切面编程),通过它可以透明地为应用程序添加横切关注点,如日志记录、事务管理和性能监控等。另一个常见用途是在远程服务调用中作为客户端和服务端之间的桥梁,使得客户端能够像调用本地方法一样调用远程服务。
此外,动态代理还被用来简化代码结构,避免大量的重复代码。例如,在不修改原始类的情况下,可以通过动态代理来增强其功能,如添加权限检查、异常处理等。
总结
Java的动态代理提供了一种灵活的方式来创建代理对象,并且可以在不修改原有代码的基础上扩展或改变行为。虽然JDK原生支持的动态代理只能对接口进行代理,但结合CGLIB等第三方库,我们可以构建出更加健壮和灵活的应用程序架构。无论是开发新的应用还是优化现有系统,理解并掌握动态代理的概念和技术都是非常有价值的。
请注意,由于动态代理涉及到反射机制,可能会带来一定的性能开销,所以在实际应用中应当权衡利弊,合理使用这一特性。同时,随着Java版本的不断更新,动态代理的相关API也可能得到进一步优化和增强。对于开发者来说,持续学习最新的Java特性和最佳实践是非常重要的。
在 Java 中,动态代理是一种强大的机制,它允许在运行时创建代理类和对象,而不需要在编译时就确定代理类的具体实现。动态代理主要用于实现 AOP(面向切面编程)、日志记录、事务管理等功能。Java 提供了两种实现动态代理的方式:基于接口的 JDK 动态代理和基于类的 CGLIB 动态代理。下面分别对这两种方式进行详细介绍。
JDK 动态代理
JDK 动态代理是 Java 原生提供的动态代理机制,它只能代理实现了接口的类。
实现步骤
- 定义接口:定义一个或多个接口,被代理的类需要实现这些接口。
- 实现接口的类:创建一个实现上述接口的具体类。
- 创建 InvocationHandler 实现类:实现
java.lang.reflect.InvocationHandler接口,该接口中的invoke方法会在代理对象调用方法时被调用。 - 创建代理对象:使用
java.lang.reflect.Proxy类的newProxyInstance方法创建代理对象。
示例代码
// 定义接口
interface Subject {
void request();
}
// 实现接口的类
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 实现 InvocationHandler 接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
class ProxyHandler implements InvocationHandler {
private final Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call");
Object result = method.invoke(target, args);
System.out.println("After method call");
return result;
}
}
// 测试代码
public class JdkDynamicProxyExample {
public static void main(String[] args) {
// 创建真实对象
RealSubject realSubject = new RealSubject();
// 创建 InvocationHandler 实例
ProxyHandler proxyHandler = new ProxyHandler(realSubject);
// 创建代理对象
Subject proxySubject = (Subject) java.lang.reflect.Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class<?>[]{Subject.class},
proxyHandler
);
// 调用代理对象的方法
proxySubject.request();
}
}
代码解释
Subject是一个接口,定义了一个request方法。RealSubject是实现了Subject接口的具体类。ProxyHandler实现了InvocationHandler接口,在invoke方法中可以添加额外的逻辑,如日志记录、事务管理等。Proxy.newProxyInstance方法用于创建代理对象,该方法需要三个参数:类加载器、代理对象要实现的接口数组和InvocationHandler实例。
CGLIB 动态代理
CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展 Java 类和实现 Java 接口。与 JDK 动态代理不同,CGLIB 可以代理没有实现接口的类。
实现步骤
- 添加依赖:如果使用 Maven 项目,需要在
pom.xml中添加 CGLIB 依赖。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.4.0</version>
</dependency>
- 创建被代理的类:创建一个普通的 Java 类。
- 创建 MethodInterceptor 实现类:实现
net.sf.cglib.proxy.MethodInterceptor接口,该接口中的intercept方法会在代理对象调用方法时被调用。 - 创建代理对象:使用
net.sf.cglib.proxy.Enhancer类创建代理对象。
示例代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 被代理的类
class TargetClass {
public void sayHello() {
System.out.println("Hello!");
}
}
// 实现 MethodInterceptor 接口
class CglibProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method call");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method call");
return result;
}
}
// 测试代码
public class CglibDynamicProxyExample {
public static void main(String[] args) {
// 创建 Enhancer 实例
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(TargetClass.class);
// 设置回调对象
enhancer.setCallback(new CglibProxyInterceptor());
// 创建代理对象
TargetClass proxy = (TargetClass) enhancer.create();
// 调用代理对象的方法
proxy.sayHello();
}
}
代码解释
TargetClass是一个普通的 Java 类,没有实现任何接口。CglibProxyInterceptor实现了MethodInterceptor接口,在intercept方法中可以添加额外的逻辑。Enhancer类用于创建代理对象,通过setSuperclass方法设置被代理的类,通过setCallback方法设置回调对象,最后调用create方法创建代理对象。
总结
- JDK 动态代理:基于接口,只能代理实现了接口的类,性能相对较高。
- CGLIB 动态代理:基于类,通过继承被代理的类来实现代理,可以代理没有实现接口的类,性能相对较低,但功能更强大。
1. 什么是Java动态代理?
Java动态代理是一种在运行时动态创建代理对象的技术。它允许开发者在不修改原始类代码的情况下,为某个对象添加额外的行为。动态代理是实现AOP(面向切面编程)的核心技术之一,广泛应用于权限控制、事务管理、日志记录等场景。
2. Java动态代理的实现机制
Java动态代理主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。
(1)java.lang.reflect.Proxy类
Proxy类是动态代理的核心类,它提供了创建动态代理对象的方法。动态代理对象必须实现一个或多个接口,这是因为Proxy类会根据接口生成代理类的字节码。
(2)java.lang.reflect.InvocationHandler接口
InvocationHandler接口是动态代理的回调接口,它定义了一个invoke方法。当代理对象的方法被调用时,会通过invoke方法进行转发,从而实现额外的逻辑处理。
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
proxy:代理对象。method:被调用的方法。args:方法的参数。
3. 使用动态代理的步骤
以下是实现动态代理的基本步骤:
(1)定义接口
动态代理要求目标对象必须实现一个接口,因为代理类是基于接口生成的。
public interface MyService {
void doSomething();
}
(2)实现接口
实现接口的具体类,称为目标类。
public class MyServiceImpl implements MyService {
@Override
public void doSomething() {
System.out.println("Doing something...");
}
}
(3)实现InvocationHandler
创建一个InvocationHandler的实现类,用于定义代理对象的行为。
import java.lang.reflect.*;
public class MyInvocationHandler implements InvocationHandler {
private final 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: " + method.getName());
Object result = method.invoke(target, args); // 调用目标对象的方法
System.out.println("After method: " + method.getName ());
return result;
}
}
(4)创建代理对象
通过Proxy类创建代理对象。
public class DynamicProxyDemo {
public static void main(String[] args) {
MyService target = new MyServiceImpl(); // 目标对象
MyInvocationHandler handler = new MyInvocationHandler(target); // 调用处理器
MyService proxy = (MyService) Proxy.newProxyInstance(
MyService.class.getClassLoader(), // 目标类的类加载器
new Class<?>[]{MyService.class}, // 目标类实现的接口
handler // 调用处理器
);
proxy.doSomething(); // 调用代理对象的方法
}
}
输出结果:
Before method: doSomething
Doing something...
After method: doSomething
4. 动态代理的特点
- 动态性:代理类是在运行时动态生成的,而不是在编译时固定。
- 灵活性:可以通过修改
InvocationHandler的实现来改变代理对象的行为,而无需修改目标类。 - 接口限制:目标类必须实现接口,否则无法使用
java.lang.reflect.Proxy。
5. 动态代理的原理
动态代理的核心原理是通过Proxy类动态生成代理类的字节码,并加载到JVM中。代理类会实现与目标类相同的接口,并将方法调用转发到InvocationHandler的invoke方法中。
- 字节码生成:
Proxy类会根据接口信息动态生成一个代理类的字节码,通常以$Proxy开头。 - 方法调用转发:代理类的方法调用会通过
InvocationHandler的invoke方法进行转发,从而实现额外的逻辑。
6. 动态代理的优缺点
优点:
- 解耦:将业务逻辑与额外功能(如日志、事务)分离,提高代码的可维护性。
- 灵活性:可以在运行时动态添加或修改代理行为。
- 符合设计原则:符合开闭原则,无需修改目标类代码。
缺点:
- 接口限制:目标类必须实现接口,否则无法使用
java.lang.reflect.Proxy。 - 性能开销:动态生成字节码和方法调用转发会带来一定的性能开销。
7. 动代理态的替代方案
如果目标类没有实现接口,可以使用CGLIB动态代理。CGLIB通过继承目标类生成代理类,而不需要接口支持。它通过字节码操作技术(如ASM)动态生成子类,从而实现代理功能。
总结
Java动态代理是一种强大的技术,广泛应用于AOP编程。它通过Proxy类和InvocationHandler接口实现动态代理对象的创建和方法调用转发。虽然它有一些限制(如接口依赖),但它的灵活性和解耦能力使其成为Java开发中不可或缺的技术之一。
计划:
- 定义一个接口,代理类和实际类都将实现这个接口。
- 创建一个实际类,实现上述接口。
- 创建一个实现了InvocationHandler接口的处理器类,用于处理代理实例上的方法调用。
- 在处理器类中,实现invoke方法,在该方法中包含对实际类的调用以及额外的逻辑。
- 使用Proxy类的newProxyInstance方法创建代理实例。
- 测试代理实例,验证动态代理是否按预期工作。
代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 步骤1: 定义接口
interface MyInterface {
void doSomething();
}
// 步骤2: 创建实际类
class RealObject implements MyInterface {
public void doSomething() {
System.out.println("RealObject is doing something.");
}
}
// 步骤3: 创建处理器类
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
// 步骤4: 实现invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before calling " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After calling " + method.getName());
return result;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
// 创建实际对象
RealObject realObject = new RealObject();
// 创建处理器实例
MyInvocationHandler handler = new MyInvocationHandler(realObject);
// 步骤5: 创建代理实例
MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class[] { MyInterface.class },
handler
);
// 步骤6: 测试代理实例
proxyInstance.doSomething();
}
}
在这个例子中,当调用proxyInstance.doSomething()时,会先打印"Before calling doSomething",然后调用实际对象的doSomething方法,最后打印"After calling doSomething"。
Java 动态代理概述
Java 动态代理允许在运行时创建接口的代理实例,并拦截对该接口的方法调用。这种方式提供了极大的灵活性,使得开发者能够在不改变原有业务逻辑的前提下增强功能。
实现原理
动态代理主要依赖于 java.lang.reflect.Proxy 和 InvocationHandler 接口来完成。当客户端请求访问某个对象时,实际执行的是由 Proxy.newProxyInstance() 方法生成的对象;而所有的方法调用都会被转发给实现了 InvocationHandler 的处理器类处理,在这里可以加入自定义行为。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public 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 calling " + method.getName());
// 执行目标对象的方法
Object result = method.invoke(target, args);
System.out.println("After called " + method.getName());
return result;
}
}
这段代码展示了如何编写一个简单的 InvocationHandler 来包裹任何实现了特定接口的目标对象。每当有针对该接口的方法调用发生时,就会先触发这里的前置操作(打印消息),再真正去调用原生的方法体,最后还可以做些收尾工作。
创建并使用代理对象
下面是一个完整的例子说明怎样利用上述 handler 构建出一个新的代理对象:
假设有一个名为 ServiceInterface 的接口以及它的具体实现 ServiceImpl:
// 定义服务接口
interface ServiceInterface {
void doSomething();
}
// 提供具体的实现
class ServiceImpl implements ServiceInterface{
@Override
public void doSomething() {
System.out.println("Doing something...");
}
}
现在可以通过如下方式获取到带有额外功能的服务代理:
public static void main(String[] args) {
// 原始的服务提供者
ServiceInterface service = new ServiceImpl();
// 将其封装成具有附加特性的版本
ServiceInterface proxiedService = (ServiceInterface) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new MyInvocationHandler(service));
// 测试新特性
proxiedService.doSomething();
}
此程序会输出类似于这样的内容:
Before calling doSomething
Doing something...
After called doSomething
这表明每次调用 doSomething() 之前和之后都自动加入了新的动作——即通过动态代理机制成功地向原始方法注入了前后置的通知逻辑。