代理模式与AOP实现原理:从静态代理到动态代理再到AOP
在面向对象编程中,代理模式是一种常见的设计模式,用于在不改变目标对象的前提下对其功能进行扩展。而面向切面编程(AOP)进一步通过代理模式,将横切关注点(如日志记录、事务管理等)与业务逻辑分离,从而实现更为优雅和模块化的设计。本文将详细介绍静态代理、动态代理(包括JDK动态代理和CGLIB动态代理)的实现方式,并进一步探讨AOP的原理及其在Spring中的应用。
1. 静态代理
静态代理概述
静态代理是指在代码中显式地创建代理类,代理类与目标对象实现相同的接口,并通过代理类的方法对目标对象进行增强。静态代理的优点在于可以在不修改目标对象的前提下扩展其功能,然而其缺点是需要实现与目标对象相同的接口,导致代理类繁多,增加了维护成本。
静态代理示例
目标对象:
public class Girl implements Human {
@Override
public void eat() {
System.out.println("Em mmm.. mm..");
}
}
抽象接口:
interface Human {
void eat();
}
代理类:
public class ProxyGirl implements Human {
private Human human;
public ProxyGirl(Human human) {
this.human = human;
}
@Override
public void eat() {
System.out.println("chiqian");
human.eat(); // 调用目标对象的方法
System.out.println("chihou");
}
}
测试类:
public class Main {
public static void main(String[] args) {
Girl girl = new Girl();
Human proxyGirl = new ProxyGirl(girl);
proxyGirl.eat(); // 输出增强后的方法
}
}
输出结果:
chiqian
Em mmm.. mm..
chihou
静态代理的优缺点
- 优点:在不修改目标对象的前提下扩展目标对象的功能。
- 缺点:需要实现与目标对象相同的接口,导致代理类繁多,维护成本高。
2. 动态代理
动态代理概述
动态代理与静态代理不同,它是在运行时动态生成代理对象,而不是在编译时静态生成。动态代理有两种主要实现方式:基于JDK的动态代理和基于CGLIB的动态代理。
- JDK动态代理:适用于目标对象实现了接口的情况,通过
InvocationHandler
和Proxy
类实现。 - CGLIB动态代理:适用于目标对象没有实现接口的情况,通过字节码生成工具库CGLIB实现。
2.1 JDK动态代理示例
接口及其实现类:
// 定义接口
public interface MyInterface {
void doSomething();
}
// 实现接口
public class MyInterfaceImpl implements MyInterface {
@Override
public void doSomething() {
System.out.println("Doing something...");
}
}
InvocationHandler:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
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 method execution.");
Object result = method.invoke(target, args);
System.out.println("After method execution.");
return result;
}
}
动态代理测试类:
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
MyInterfaceImpl myInterfaceImpl = new MyInterfaceImpl();
MyInvocationHandler handler = new MyInvocationHandler(myInterfaceImpl);
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class<?>[]{MyInterface.class},
handler);
proxy.doSomething(); // 调用代理对象的方法
}
}
输出结果:
Before method execution.
Doing something...
After method execution.
2.2 CGLIB动态代理示例
目标类:
public class MyClass {
public void doSomething() {
System.out.println("Doing something in MyClass...");
}
}
MethodInterceptor:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method execution.");
Object result = proxy.invokeSuper(obj, args); // 调用父类(被代理类)的方法
System.out.println("After method execution.");
return result;
}
}
CGLIB动态代理测试类:
import net.sf.cglib.proxy.Enhancer;
public class Main {
public static void main(String[] args) {
MyClass myClass = new MyClass();
MyMethodInterceptor interceptor = new MyMethodInterceptor();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyClass.class); // 设置要代理的类
enhancer.setCallback(interceptor); // 设置回调函数
MyClass proxy = (MyClass) enhancer.create(); // 创建代理对象
proxy.doSomething(); // 调用代理对象的方法
}
}
输出结果:
Before method execution.
Doing something in MyClass...
After method execution.
CGLIB动态代理的依赖
使用CGLIB时,需要在Maven项目中添加CGLIB依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 使用时请检查最新版本 -->
</dependency>
3. AOP实现原理
AOP概述
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在将横切关注点(如日志记录、事务管理、安全检查等)与业务逻辑分离开来,从而实现模块化的代码设计。AOP通过将横切关注点定义为独立的切面,并在特定的切入点上织入这些切面,来实现对目标对象方法的增强。
AOP的基本概念
- 切面(Aspect):封装横切关注点的模块,由切入点和通知组成。
- 切入点(Pointcut):定义切面应该应用到程序的哪些部分,通过表达式来指定目标方法、类或包。
- 通知(Advice):定义在切入点处执行的动作,可以在方法执行前、执行后、抛出异常时执行。
- 织入(Weaving):将切面应用到目标对象的过程,分为编译时织入、类加载时织入和运行时织入。
AOP实现的三种方式
- 编译时织入:在编译过程中将切面代码织入目标类中,例如AspectJ。
- 类加载时织入:在类加载时由类加载器动态地将切面代码织入目标类中,使用Java的Instrumentation API。
- 运行时织入:在运行时通过代理对象将切面代码织入目标对象中,Spring AOP就是采用运行时织入。
AOP在Spring中的实现
Spring AOP主要使用动态代理来实现AOP功能:
- JDK动态代理:当目标对象实现了接口时,使用JDK动态代理。
- CGLIB动态代理:当目标对象没有实现接口时,使用CGLIB生成代理类。
Spring AOP示例:
定义切面类:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class LoggingAspect {
// 切入点表达式,表示在com.example包下的所有方法执行前执行这个切面
@Before("execution(* com.example.*.*(..))")
public void logBefore() {
System.out.println("Before method execution.");
}
}
配置和使用切面:
将切面类配置到Spring应用上下文中,Spring会自动检测@Aspect
注解并在运行时动态创建代理对象,从而在方法执行前自动调用logBefore()
方法。
Spring AOP的优点:
- 非侵入性:不需要修改现有的业务代码,通过配置切面即可实现增强。
- 灵活性:可以通过简单的配置文件或注解轻松调整横切关注点的应用范围。
- 可重用性:切面可以被多个不同的模块使用,提高了代码的可重用性
和模块化程度。
结论
通过本文对静态代理、动态代理以及AOP的详细介绍,可以看出代理模式在Java编程中的广泛应用。从静态代理的显式代码实现,到动态代理的运行时生成代理对象,再到AOP的高度模块化设计,代理模式不仅提升了代码的可维护性,还为横切关注点的处理提供了有效的手段。随着业务需求的复杂化,AOP在Spring等框架中的应用也愈发重要,使得代码更加优雅、灵活和可扩展。