八股八股静态代理、动态代理、AOP随笔记
Jaron1. 静态代理:基础但“僵化”的起点
静态代理是最直观的实现方式,它要求我们为每一个需要被代理的业务类,都手动编写一个对应的代理类。
- 工作方式:代理类与业务类实现同一个接口,并在内部持有业务类实例的引用。这样,代理类就可以在调用实际业务方法前后,执行我们想要附加的操作。
代码示例
1 2 3 4
| public interface UserService { void saveUser(); }
|
1 2 3 4 5 6 7
| public class UserServiceImpl implements UserService { @Override public void saveUser() { System.out.println("核心逻辑:保存用户。"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class UserServiceStaticProxy implements UserService { private UserService target;
public UserServiceStaticProxy(UserService target) { this.target = target; }
@Override public void saveUser() { System.out.println("日志:saveUser 方法开始执行..."); target.saveUser(); System.out.println("日志:saveUser 方法执行完毕..."); } }
|
静态代理的“窘境”
初看起来,这种方式简单明了。但当系统规模扩大时,其固有的僵化和笨拙便暴露无遗。想象一下,我们的系统不仅有UserService,还有OrderService、ProductService等数十个服务,并且每一个服务的所有方法都需要添加日志。
这意味着,我们必须手动创建OrderServiceStaticProxy、ProductServiceStaticProxy……为每一个服务都编写一个结构几乎完全相同的代理类。这不仅是巨大的、重复的体力劳动,更是一场维护的噩梦,让静态代理在规模化应用面前显得捉襟见肘。
2. 动态代理:应对重复劳动的自动化方案
为了解决静态代理的僵化和重复劳动问题,Java提供了动态代理技术。它不再需要我们手动编写代理类,而是在程序运行时,根据我们的需要自动在内存中生成。
- 核心思想:我们只需编写一个通用的调用处理器(InvocationHandler),在其中定义好普适的增强逻辑(例如日志记录)。然后,动态代理机制就可以利用这个处理器,为任何接口的实现类创建代理对象。
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class LogInvocationHandler implements InvocationHandler { private Object target;
public LogInvocationHandler(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; } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| import java.lang.reflect.Proxy;
public class ProxyFactory { public static Object getProxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new LogInvocationHandler(target) ); } }
|
现在,无论是UserService还是OrderService,我们都可以使用同一个ProxyFactory来创建它们的日志代理,彻底摆脱了手动编写大量代理类的困境。动态代理以其自动化和通用性,完美地解决了静态代理的痛点。
3. AOP:日志场景下的更优选择
动态代理虽然强大,但我们仍然需要编写InvocationHandler,并处理反射API,这依然属于编程式的范畴。对于日志这种无处不在的横切关注点,业界发展出了一种更高级、更优雅的AOP(面向切面编程)思想。
- 核心思想:AOP让我们从“如何实现代理”的细节中解脱出来,转而以声明式的方式来描述“做什么”和“在哪里做”。我们只需定义好切面(Aspect),AOP框架(如Spring AOP)就会在底层自动利用动态代理来完成所有工作。
代码示例(Spring AOP 风格)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component;
@Aspect @Component public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))") public void serviceLayer() {}
@Before("serviceLayer()") public void logBefore(JoinPoint joinPoint) { System.out.println("日志:" + joinPoint.getSignature().getName() + " 方法开始执行..."); }
@After("serviceLayer()") public void logAfter(JoinPoint joinPoint) { System.out.println("日志:" + joinPoint.getSignature().getName() + " 方法执行完毕..."); } }
|
通过AOP,我们甚至不需要知道UserServiceImpl的存在,只需一个切面类,就能为整个service层提供日志功能。代码完全解耦,业务逻辑中没有任何代理或日志的痕迹。因此,对于日志、事务这类场景,AOP是比直接使用动态代理更优越、更专业的选择。
4. 动态代理的独有场景:RPC
既然AOP在日志等场景下表现更佳,那么动态代理是否还有其不可替代的用武之地?答案是肯定的。这个经典场景就是RPC(远程过程调用)框架的客户端。
AOP的核心前提是 “增强”一个已经存在的本地对象。Spring AOP需要先在容器里找到一个目标Bean(如UserServiceImpl的实例),然后才能为它创建代理。
但在RPC客户端场景中,我们面临一个根本性的不同:我们只有接口,没有任何本地实现类。OrderService的实现远在服务器上,客户端的JVM中并不存在可供AOP增强的目标。
这正是动态代理大显身手的地方。动态代理最强大的能力之一,就是可以根据接口凭空创造一个实现对象。
代码示例(伪代码)
1 2 3 4
| public interface OrderService { String findOrderById(String id); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.lang.reflect.Proxy;
public class RpcClientFactory { public static <T> T getRemoteService(Class<T> serviceInterface) { InvocationHandler handler = (proxy, method, args) -> { System.out.println("RPC代理:拦截到调用 " + method.getName()); return "从远程服务器获取到的订单信息"; };
return (T) Proxy.newProxyInstance( serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, handler ); } }
|
在这里,动态代理的作用不是“增强”,而是“创造”和“伪装”。它创造了一个OrderService的本地实例,并伪装成真正的实现,而其内部则是在勤勤恳恳地进行网络数据收发。这是AOP的设计模式无法覆盖的领域,也是动态代理不可或缺的独特价值所在。