java第十六天 代理 、AOP

本文介绍了面向切面编程(AOP)的概念及其在Spring框架中的应用,并深入探讨了代理模式的作用及其实现方法,包括静态代理和Java动态代理的具体实现。

今天看的是高新技术的最后一天,看的是代理;
后面老师用代码演示时候看的很辛苦,最后一节《实现类似spring的可配置的AOP框架》看的真是抓狂了;
能够理解代理的意思,知道AOP编程的编程方法了。


Tips:


代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
 
1. 代理模式
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色 :声明真实对象和代理对象的共同接口;
代理角色 :代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色 :代理角色所代表的真实对象,是我们最终要引用的对象。
 
2. 动态代理类
Java 动态代理类位于 Java.lang.reflect 包下,一般主要涉及到以下两个类:
 (1). Interface InvocationHandler :该接口中仅定义了一个方法 Object invoke(Object obj,Method method, Object [] args) 。在实际使用时,第一个参数 obj 一般是指代理类, method 是被代理的方法,如上例中的 request() args 为该方法的参数数组。这个抽象方法在代理类中动态实现。
 (2).Proxy :该类即为动态代理类,作用类似于上例中的 ProxySubject ,其中主要包含以下内容:
Protected Proxy(InvocationHandler h) :构造函数,估计用于给内部的 h 赋值。
Static Class getProxyClass (ClassLoader loader, Class[] interfaces) :获得一个代理类,其中 loader 是类装载器, interfaces 是真实类所拥有的全部接口的数组。
Static Object newProxyInstance (ClassLoader loader, Class[] interfaces, InvocationHandler h) :返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

 

AOP
面向切面编程(也叫面向方面)Aspect Oriented Programming
AOPOOP的延续,是(Aspect Oriented Programming)的缩写,意思是面向切面(方面)编程。
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

<think>我们正在讨论Java动态代理的实现方式。根据引用内容,Java动态代理主要有两种实现方式:JDK原生动态代理和CGLib动态代理。下面我将详细解释这两种方式,并比较它们的异同。 ### 1. JDK原生动态代理 JDK动态代理Java标准库提供的一种代理机制,基于**反射**实现。它要求被代理的类必须实现至少一个接口。 #### 实现步骤: 1. **定义接口**:声明需要代理的方法。 2. **实现接口**:创建目标类,实现接口中的方法。 3. **实现InvocationHandler**:编写调用处理器,在`invoke`方法中添加增强逻辑。 4. **生成代理对象**:通过`Proxy.newProxyInstance`方法动态创建代理实例。 #### 示例代码: ```java // 接口 public interface UserService { void addUser(String name); } // 目标类 public class UserServiceImpl implements UserService { public void addUser(String name) { System.out.println("添加用户: " + name); } } // 调用处理器 public class LogHandler implements InvocationHandler { private Object target; public LogHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法调用前日志"); Object result = method.invoke(target, args); System.out.println("方法调用后日志"); return result; } } // 创建代理 public static void main(String[] args) { UserService target = new UserServiceImpl(); InvocationHandler handler = new LogHandler(target); UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, handler ); proxy.addUser("张三"); // 调用代理方法 } ``` #### 特点: - 只能代理接口 - 无需引入第三方库 - 执行效率相对较低(反射调用) ### 2. CGLib动态代理 CGLib(Code Generation Library)是一个基于**字节码操作**(通过ASM框架)的代理库。它通过生成目标类的子类来实现代理,因此不需要目标类实现接口。 #### 实现步骤: 1. **创建MethodInterceptor**:实现`MethodInterceptor`接口,在`intercept`方法中添加增强逻辑。 2. **生成代理对象**:使用`Enhancer`类创建代理实例。 #### 示例代码: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { private Object target; public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("方法调用前日志"); Object result = proxy.invokeSuper(obj, args); System.out.println("方法调用后日志"); return result; } } // 使用代理 public static void main(String[] args) { UserService target = new UserService(); // 注意:这里UserService是类,不是接口 CglibProxy cglibProxy = new CglibProxy(); UserService proxy = (UserService) cglibProxy.getInstance(target); proxy.addUser("李四"); } ``` #### 特点: - 可以代理普通类(无需实现接口) - 需要引入CGLib依赖 - 执行效率较高(通过FastClass机制直接调用方法) - 无法代理final类或final方法 ### 3. 两种代理方式对比 | 特性 | JDK动态代理 | CGLib动态代理 | |---------------------|--------------------------------|-------------------------------| | 代理对象要求 | 必须实现接口 | 任意Java类 | | 底层技术 | 反射 | 字节码操作(ASM) | | 执行速度 | 较慢(反射调用) | 较快(直接调用) | | 生成代理类速度 | 较快 | 较慢(需要生成字节码) | | 方法拦截 | 通过`InvocationHandler` | 通过`MethodInterceptor` | | 依赖 | JDK自带 | 需引入CGLib库 | | 对final类的支持 | 无此问题(代理接口) | 无法代理final类 | > 💡 **选择建议**: > - 若目标对象实现了接口,优先使用JDK动态代理(减少依赖) > - 若目标对象没有接口,必须使用CGLib > - Spring框架默认对接口使用JDK代理,对类使用CGLib[^3] ### 4. 应用场景 - **AOP编程**:方法增强(日志、事务等)[^1][^3] - **RPC框架**:远程方法调用的本地代理 - **测试框架**:Mock对象创建 - **延迟加载**:Hibernate的延迟加载机制 ### 总结 Java动态代理有两种主流实现: 1. **JDK动态代理**:基于接口和反射,适合代理接口实现类 2. **CGLib动态代理**:基于字节码生成,适合代理普通类 两者各有优劣,实际开发中可根据目标对象类型和性能要求选择合适方案。在Spring等框架中,这两种代理方式被智能组合使用以实现全面的AOP功能[^2][^3]。 --- §§相关问题§§ 1. 动态代理在Spring框架中是如何应用的? 2. 如何解决JDK动态代理只能代理接口的问题? 3. CGLIB实现动态代理的原理与JDK代理有何本质区别?</think>### Java 动态代理的实现方式详解 Java 动态代理主要有两种实现方式,它们在底层原理、使用场景和性能特点上存在显著差异: #### 一、JDK 原生动态代理(基于接口) **实现原理**: 使用 Java 反射机制,在运行时动态生成代理类(`$ProxyN`格式),要求**被代理对象必须实现至少一个接口**。 **核心组件**: 1. `java.lang.reflect.Proxy`:代理类工厂 2. `java.lang.reflect.InvocationHandler`:方法调用处理器 **实现步骤**: ```java // 1. 定义接口 public interface OrderService { void createOrder(String orderId); } // 2. 创建调用处理器 public class LogHandler implements InvocationHandler { private final Object target; public LogHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("[LOG] 调用方法: " + method.getName()); return method.invoke(target, args); // 实际方法调用 } } // 3. 生成代理对象 OrderService realService = new OrderServiceImpl(); OrderService proxy = (OrderService) Proxy.newProxyInstance( OrderService.class.getClassLoader(), new Class[]{OrderService.class}, // 代理接口 new LogHandler(realService) // 处理器 ); // 4. 使用代理 proxy.createOrder("ORD-2023-001"); ``` **特点**: - ✅ JDK 原生支持,无需额外依赖 - ✅ 接口方法自动代理 - ❌ 只能代理接口,不能代理类 - ⚠️ 反射调用有性能开销 #### 二、CGLIB 动态代理(基于继承) **实现原理**: 通过 ASM 字节码操作框架,在运行时**生成目标类的子类**作为代理类,通过方法重写实现代理,不要求实现接口。 **核心组件**: 1. `net.sf.cglib.proxy.Enhancer`:字节码增强器 2. `net.sf.cglib.proxy.MethodInterceptor`:方法拦截器 **实现步骤**: ```java // 1. 创建方法拦截器 public class UserServiceInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("[CGLIB] 方法调用: " + method.getName()); return proxy.invokeSuper(obj, args); // 调用父类方法 } } // 2. 生成代理对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); // 设置父类 enhancer.setCallback(new UserServiceInterceptor()); // 设置拦截器 UserService proxy = (UserService) enhancer.create(); // 创建代理实例 // 3. 使用代理 proxy.addUser("李四"); ``` **特点**: - ✅ 可代理普通类(无需接口) - ✅ 执行效率更高(避免反射调用) - ❌ 需要引入第三方库(cglib) - ❌ 无法代理 final 类/方法 - ⚠️ 生成字节码有初始性能开销 --- ### 两种方式对比分析 | 特性 | JDK 动态代理 | CGLIB 动态代理 | |---------------------|----------------------------------|---------------------------------| | **代理要求** | 必须实现接口 | 可代理任意类 | | **底层技术** | Java 反射机制 | ASM 字节码操作 | | **执行速度** | 较慢(反射调用) | 较快(直接方法调用) | | **初始化开销** | 较小 | 较大(需生成字节码) | | **方法拦截方式** | InvocationHandler | MethodInterceptor | | **final 方法支持** | 无此问题 | 无法代理 final 方法 | | **依赖** | JDK 原生支持 | 需引入 cglib 库 | > 💡 **最佳实践选择**: > - 当目标对象**已实现接口**时优先使用 JDK 代理(如 Spring 默认策略) > - 当需要代理**普通类**或追求更高性能时使用 CGLIB > - 在 Spring AOP 中可通过 `@EnableAspectJAutoProxy(proxyTargetClass=true)` 强制使用 CGLIB[^3] --- ### 应用场景示例 1. **日志记录**(两种代理均可实现) ```java public Object invoke(...) { log.info(">> 调用 {}.{}()", target.getClass().getSimpleName(), method.getName()); Object result = method.invoke(target, args); log.info("<< 方法执行完成 (耗时: {}ms)", System.currentTimeMillis()-start); return result; } ``` 2. **事务管理**(Spring 声明式事务底层实现) ```java public Object intercept(...) { Transaction tx = beginTransaction(); try { Object result = proxy.invokeSuper(obj, args); tx.commit(); // 成功提交 return result; } catch (Exception e) { tx.rollback(); // 异常回滚 throw e; } } ``` 3. **RPC 调用**(Dubbo 等框架的客户端代理) ```java public Object invoke(...) { // 将方法调用信息序列化 RpcRequest request = buildRequest(method, args); // 通过网络发送请求 return rpcClient.send(request); } ``` --- ### 性能优化建议 1. **缓存代理类**:避免重复生成代理类字节码(CGLIB 的 `Enhancer` 可缓存) 2. **减少拦截方法**:在处理器中过滤无需代理的方法 3. **使用 FastClass**(CGLIB):通过索引直接调用方法,避免反射查找 4. **JIT 优化**:JDK 代理经过多次调用后会被 JIT 编译优化 > 📌 **注意**:在 Java 16+ 中,由于模块系统限制,需添加 JVM 参数 `--add-opens java.base/java.lang=ALL-UNNAMED` 才能使用 CGLIB[^2] 两种动态代理技术各有优势,实际开发中常结合使用(如 Spring AOP 的智能代理选择机制),开发者应根据具体场景选择最合适的方案[^1][^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值