一、介绍
Java的代理模式是一种结构型设计模式,它为另一个对象提供一个代理或占位符,以便控制对该对象的访问。代理模式的主要作用包括:
-
远程代理(Remote Proxy):为一个位于不同地址空间的对象提供一个本地的代理,隐藏了对真实对象的访问细节,使得客户端能像访问本地对象一样访问远程对象。
-
虚拟代理(Virtual Proxy):在需要创建开销很大的对象时,可以使用虚拟代理来代替真实对象,直到真实对象真正需要时才去创建它。
-
保护代理(Protection Proxy):用于控制对一个对象的访问权限,可以让代理完全拒绝或允许访问某些方法。
-
智能引用代理(Smart Reference Proxy):当一个对象被引用时,进行一些附加操作,比如计算该对象被引用的次数。
代理模式的核心思想是引入一个中介对象,来包装、增强或替换原有对象。代理对象与真实对象实现相同的接口,在客户端看来是无差别的,但代理对象可以在访问真实对象前后做一些控制和处理工作。
代理模式的主要角色有:
- Subject(抽象主题角色):定义了代理和真实对象实现的接口。
- Proxy(代理角色):持有一个真实对象的引用,并且提供与真实对象相同的接口,在访问真实对象之前或之后完成自己的逻辑。
- RealSubject(真实主题角色):定义代理所代表的真实对象,是我们最终要引用的对象。
下面是一个简单的虚拟代理的示例代码:
//抽象主题
interface Image {
void display();
}
//真实主题
class RealImage implements Image {
private final String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
}
//代理
class ProxyImage implements Image {
private RealImage image = null;
private final String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (image == null) {
image = new RealImage(fileName);
}
image.display();
}
}
在这个例子中,ProxyImage
是一个代理,它有一个字段持有RealImage
的引用。只有当display()
方法被调用时,才会去创建RealImage
对象,从而达到了延迟加载的目的。通过使用代理,可以节省内存并提高性能。
二、影院系统项目案例使用
需求分析:我们有一个影院系统,用户需要先购买电影票,然后才能进入影院观看电影。我们可以使用代理模式来控制对影厅的访问权限。
1.首先,定义一个Theater
接口,它包含了观看电影的watchMovie
方法:
public interface Theater {
void watchMovie(String movie);
}
2.实现一个真实的Theater
对象,CinemaTheater
:
public class CinemaTheater implements Theater {
@Override
public void watchMovie(String movie) {
System.out.println("您正在观看电影: " + movie);
}
}
3.定义一个代理TheaterProxy
,它持有一个CinemaTheater
对象的引用,并在watchMovie
方法中添加权限控制逻辑:
public class TheaterProxy implements Theater {
private CinemaTheater theater;
private boolean ticketBought;
public TheaterProxy(CinemaTheater theater) {
this.theater = theater;
}
@Override
public void watchMovie(String movie) {
if (ticketBought) {
theater.watchMovie(movie);
} else {
System.out.println("您需要先购买电影票!");
}
}
public void buyTicket() {
System.out.println("您已购买电影票!");
ticketBought = true;
}
}
在这个代理类中,我们有一个buyTicket
方法,用户需要先调用该方法购买电影票,然后才能通过watchMovie
方法观看电影。如果没有购买票,代理会拒绝访问。
4.客户端代码中使用代理:
public class Client {
public static void main(String[] args) {
CinemaTheater theater = new CinemaTheater();
TheaterProxy proxy = new TheaterProxy(theater);
// 没有买票,无法观看电影
proxy.watchMovie("复仇者联盟4");
// 购买票后,可以观看电影
proxy.buyTicket();
proxy.watchMovie("复仇者联盟4");
}
}
输出结果:
您需要先购买电影票!
您已购买电影票!
您正在观看电影: 复仇者联盟4
在这个示例中,TheaterProxy
充当了控制对CinemaTheater
访问权限的代理。通过引入代理,我们可以在不修改CinemaTheater
类的情况下,添加额外的控制逻辑。这样做增强了代码的可维护性和可扩展性。
三、JDK动态代理实现业务
有一个订单服务接口OrderService
和一个具体的实现类OrderServiceImpl
。在处理订单时,我们需要对订单进行一些额外的操作,比如记录日志、进行权限检查等。这时我们可以使用动态代理来实现这些横切逻辑,而无需修改OrderServiceImpl
的代码。
首先,定义OrderService
接口和OrderServiceImpl
类:
// 订单服务接口
public interface OrderService {
void createOrder(String name, double amount);
}
// 订单服务实现
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder(String name, double amount) {
System.out.println("Creating order for " + name + ", amount: $" + amount);
// 执行创建订单的具体逻辑
}
}
接下来,创建一个InvocationHandler
实现类,在这个类中我们可以添加横切逻辑:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class OrderServiceInvocationHandler implements InvocationHandler {
private final OrderService orderService;
public OrderServiceInvocationHandler(OrderService orderService) {
this.orderService = orderService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行前置逻辑
System.out.println("Performing additional operations before creating order...");
// 调用目标方法
Object result = method.invoke(orderService, args);
// 执行后置逻辑
System.out.println("Performing additional operations after creating order...");
return result;
}
}
在invoke
方法中,我们可以在调用目标方法之前和之后执行额外的逻辑,如记录日志、权限检查等。
最后,创建一个客户端类,使用Proxy.newProxyInstance
方法创建代理对象并调用其方法:
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// 创建目标对象
OrderService orderService = new OrderServiceImpl();
// 创建代理对象
OrderService proxyOrderService = (OrderService) Proxy.newProxyInstance(
OrderService.class.getClassLoader(),
new Class[]{OrderService.class},
new OrderServiceInvocationHandler(orderService));
// 调用代理对象的方法
proxyOrderService.createOrder("John", 99.99);
}
}
运行Client
类的main
方法,输出结果如下:
Performing additional operations before creating order...
Creating order for John, amount: $99.99
Performing additional operations after creating order...
在这个示例中,我们使用JDK动态代理创建了一个OrderService
的代理对象proxyOrderService
。当调用proxyOrderService.createOrder
方法时,实际上会执行OrderServiceInvocationHandler
的invoke
方法。在invoke
方法中,我们添加了一些前置和后置逻辑,如记录日志。最后,代理对象会调用真正的OrderServiceImpl
对象执行业务逻辑。
四、CGLib动态代理实现业务
与JDK动态代理不同的是,CGLib可以对任何类进行代理,而不仅限于实现了接口的类。
有一个商品服务类ProductService
和一个具体的实现ProductServiceImpl
。在调用商品服务的方法时,能够记录日志和进行权限检查。
使用CGLib需要引入相关的依赖库,例如在Maven项目中添加以下依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
1.定义ProductService
和ProductServiceImpl
类:
// 商品服务类
public class ProductService {
public void createProduct(String name, double price) {
System.out.println("Creating product: " + name + ", price: $" + price);
// 执行创建商品的具体逻辑
}
}
// 商品服务实现
public class ProductServiceImpl extends ProductService {
}
2.使用CGLib创建一个方法拦截器ProductServiceMethodInterceptor
,在这个拦截器中我们可以添加横切逻辑:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProductServiceMethodInterceptor implements MethodInterceptor {
private final Object target; // 目标对象
public ProductServiceMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 执行前置逻辑
System.out.println("Performing additional operations before creating product...");
// 调用目标方法
Object result = proxy.invokeSuper(obj, args);
// 执行后置逻辑
System.out.println("Performing additional operations after creating product...");
return result;
}
}
在intercept
方法中,我们可以在调用目标方法之前和之后执行额外的逻辑,如记录日志、权限检查等。
3.创建一个客户端类,使用CGLib创建代理对象并调用其方法:
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 创建目标对象
ProductService productService = new ProductServiceImpl();
// 创建增强器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductService.class);
enhancer.setCallback(new ProductServiceMethodInterceptor(productService));
// 创建代理对象
ProductService proxyProductService = (ProductService) enhancer.create();
// 调用代理对象的方法
proxyProductService.createProduct("iPhone", 999.99);
}
}
运行Client
类的main
方法,输出结果如下:
Performing additional operations before creating product...
Creating product: iPhone, price: $999.99
Performing additional operations after creating product...
使用CGLib创建了一个ProductService
的代理对象proxyProductService
。当调用proxyProductService.createProduct
方法时,实际上会执行ProductServiceMethodInterceptor
的intercept
方法。在intercept
方法中,我们添加了一些前置和后置逻辑,如记录日志。最后,代理对象会调用真正的ProductServiceImpl
对象执行业务逻辑。
总结:使用CGLib动态代理可以为任何类添加横切逻辑,而不仅限于实现了接口的类。与JDK动态代理相比,CGLib动态代理的实现方式更加底层,它通过字节码技术为目标类创建一个子类,并在子类中采用方法拦截的方式来实现代理。
五、JDK动态代理和CGLib动态代理源码分析
在Java中,代理模式主要通过JDK动态代理和CGLib动态代理两种方式来实现。下面我将分别介绍这两种实现方式的源码。
- JDK动态代理
JDK动态代理主要通过java.lang.reflect.Proxy
类实现。该类提供了一个静态方法newProxyInstance
用于创建代理对象。我们来看一下newProxyInstance
方法的实现:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
// 一些参数校验...
// 获取代理类的构造器
Class<?> cl = getProxyClass(loader, interfaces);
final Constructor<?> cons = cl.getConstructor(constructorParams);
// 创建代理对象
final InvocationHandler ih = h;
return cons.newInstance(new Object[]{h});
}
这个方法首先通过getProxyClass
方法获取代理类的构造器,然后通过反射创建代理对象。其中getProxyClass
方法主要做了两件事:
- 缓存代理类构造器,避免重复生成
- 使用
ProxyClassFactory
生成代理类的字节码
生成的代理类实现了被代理对象的所有接口,并继承了Proxy
类。当调用代理对象的方法时,实际上是调用了Proxy
类的invoke
方法,由InvocationHandler
处理实际逻辑。
- CGLib动态代理
CGLib是一个第三方库,它可以在运行时生成被代理对象的子类作为代理对象。这种方式不需要被代理对象实现接口,因此有更好的通用性。来看一下CGLib的实现:
Enhancer enhancer = new Enhancer();
// 设置被代理对象
enhancer.setSuperclass(RealSubject.class);
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 执行前置处理
// ...
// 调用目标方法
Object result = proxy.invokeSuper(obj, args);
// 执行后置处理
// ...
return result;
}
});
// 创建代理对象
RealSubject proxySubject = (RealSubject) enhancer.create();
在上面的代码中,Enhancer
是CGLib的核心类,用于生成代理对象。首先通过setSuperclass
方法设置被代理对象的类,然后通过setCallback
方法设置一个MethodInterceptor
对象作为回调函数,该对象中的intercept
方法会在代理对象的任意方法调用时被执行。最后通过create
方法创建代理对象。
代理对象是被代理对象的子类,当执行代理对象的方法时,实际上会执行MethodInterceptor
中的intercept
方法。在该方法中,可以通过proxy.invokeSuper
调用目标方法,也可以在目标方法执行前后加入其他逻辑。
六、Spring AOP框架源码分析
1. 基于JDK动态代理
对于实现了接口的目标对象,Spring AOP使用JDK动态代理来创建代理对象。核心源码在JdkDynamicAopProxy
类中:
// JdkDynamicAopProxy.java
public Object getProxy(ClassLoader classLoader) {
// 使用Proxy创建动态代理
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
// ...
Object retVal = invocation.proceed();
// ...
return retVal;
}
在getProxy
方法中,Spring使用Proxy.newProxyInstance
创建了一个代理对象,并将JdkDynamicAopProxy
自身作为InvocationHandler
的实例传入。
当调用代理对象的方法时,实际上会执行JdkDynamicAopProxy
的invoke
方法。在该方法中,Spring会创建一个ReflectiveMethodInvocation
对象,通过proceed
方法执行目标对象的方法。在proceed
方法执行前后,Spring会根据配置插入不同的通知(Advice),从而实现AOP的功能。
2. 基于CGLib动态代理
对于没有实现接口的目标对象,Spring AOP使用CGLib动态代理来创建代理对象。核心源码在CglibAopProxy
类中:
// CglibAopProxy.java
public Object getProxy(ClassLoader classLoader) {
// 创建Enhancer
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.advised.getTargetClass());
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndecidedStrategy(classLoader));
// 设置回调
Callback[] callbacks = getCallbacks(this.advised);
enhancer.setCallbackFilter(new CglibAopProxy.DynamicAdvisedInterceptor(this.advised.getConfigurationOnlyCopy()));
enhancer.setCallbacks(callbacks);
// 创建代理对象
return enhancer.create();
}
在上面的代码中,Spring使用CGLib的Enhancer
为目标对象创建一个子类作为代理对象。Enhancer
设置了目标对象的父类、接口等信息,并设置了一个DynamicAdvisedInterceptor
作为回调。
当调用代理对象的方法时,实际上会执行DynamicAdvisedInterceptor
的intercept
方法,在该方法中,Spring会根据配置决定是执行目标方法还是执行通知方法。如果执行目标方法,则通过methodProxy.invokeSuper
调用目标对象的方法。在目标方法执行前后,Spring会根据配置插入不同的通知(Advice),从而实现AOP的功能。
3. 选择代理方式
Spring AOP会根据目标对象是否实现了接口来选择使用JDK动态代理还是CGLib动态代理:
// AopProxyFactory.java
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// ...
if (config.isInterfaceProxied()) {
// 使用JDK动态代理
return new JdkDynamicAopProxy(config);
} else {
// 使用CGLib动态代理
return new ObjenesisCglibAopProxy(config);
}
}
在上面的代码中,如果目标对象实现了接口,则使用JDK动态代理;否则使用CGLib动态代理。
通过代理模式的应用,Spring AOP能够在不修改目标对象的情况下,为其增加横切逻辑(如日志、事务管理等)。代理对象充当了中介的角色,在目标方法执行前后插入了通知代码,从而实现了横切关注点的织入。这种设计模式提高了代码的可维护性和可扩展性,符合开闭原则。