在软件开发中,代理模式是一种非常常见的设计模式。它允许我们通过一个代理对象间接访问目标对象,从而在不修改目标对象的情况下增强其功能。代理模式广泛应用于日志记录、权限校验、事务管理、延迟加载等场景。本文将带你深入了解Java中的代理模式,包括静态代理和动态代理的实现方式。
一、什么是代理
给目标对象提供一个代理对象,并且由代理对象控制着对目标对象的引用
在程序中,代理模式的核心思想是:
代理对象:代替目标对象执行某些操作。
目标对象:实际执行业务逻辑的对象。
增强功能:在调用目标对象方法前后,代理对象可以添加额外的逻辑。
二、代理的目的
代理模式的主要目的是在不修改目标对象的情况下增强其功能。具体来说,代理模式可以实现以下目标:
1、控制访问:通过代理对象的方式间接的访问目标对象,防止直接访问目标对象给系统带来的不必要的复杂性
2、功能增强:通过代理业务对原有业务进行增强。
三、如何实现代理
Java中实现代理模式主要有两种方式:
静态代理,通过JDK实现
动态代理,通过JDK或者cglib实现
下面我们分别介绍这两种方式。
静态代理
在Java中,静态代理是一种设计模式,它通过创建一个代理类来间接访问目标类,从而在不修改目标类的情况下增强其功能。静态代理的核心思想是:手动编写代理类,代理类和目标类实现相同的接口,代理类持有目标类的引用,并在调用目标类方法的前后添加额外的逻辑。
代码实例:
(1)目标类ClothesFactory
public class ClothesFactory implements ByClothes{
public void clothes(String size){
System.out.println("已经给您定制好了一件大小为"+size+"的衣服");
}
public void 机器处理(){
}
public void 原材料(){}
}
这是实际执行业务逻辑的类,它实现了 ByClothes 接口,并提供了 clothes 方法的具体实现。
(2)代理类Proxy
public class Proxy implements ByClothes {
private ClothesFactory clothesFactory = new ClothesFactory();
@Override
public void clothes(String size) {
FrontService();
clothesFactory.clothes(size);
endService();
}
//前置服务
public void FrontService() {
System.out.println("根据您的需求进行市场调研");
}
//前置服务
public void endService() {
System.out.println("为您提供一条龙的包办服务");
}
}
代理类也实现了 ByClothes 接口,它持有一个 ClothesFactory 的实例(目标类的引用),在调用目标类的 clothes 方法前后,代理类可以添加额外的逻辑(如 FrontService 和 endService)。
(3)接口ByClothes
public interface ByClothes {
public abstract void clothes(String size);
}
这是一个接口,定义了 clothes 方法,目标类 ClothesFactory 和代理类 Proxy 都实现了这个接口。
(4)客户端Test
public class Test {
public static void main(String[] args){
Proxy proxy =new Proxy();
proxy.clothes("xxxl");
}
}
客户端通过代理类 Proxy 来调用 clothes 方法,客户端并不直接与目标类 ClothesFactory 交互,而是通过代理类间接访问。
静态代理的工作流程
客户端调用代理类的 clothes 方法。
代理类在调用目标类的 clothes 方法之前,执行前置逻辑(FrontService)。
代理类调用目标类的 clothes 方法,完成核心业务逻辑。
代理类在调用目标类的 clothes 方法之后,执行后置逻辑(endService)。
静态代理缺点:当目标类增多时,代理类直接增加代理的目标类,会造成代理关系混乱。
动态代理
动态代理是Java中一种更灵活的代理模式,动态代理的核心思想是:在运行时动态生成代理类,而不需要像静态代理那样为每个目标类手动编写代理类。
代码实例:
(1)InvocationHandler接口的实现类DyProxy
package com.qcby;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DyProxy implements InvocationHandler {
// 被代理的对象
private Object o;
// 构造方法,传入被代理的对象
public DyProxy(Object o) {
this.o = o;
}
// 获取代理对象
public Object getProxyInterFace() {
return Proxy.newProxyInstance(
o.getClass().getClassLoader(), // 目标类的类加载器
o.getClass().getInterfaces(), // 目标类实现的接口
this // InvocationHandler 实例
);
}
// 核心方法:代理逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
FrontService(); // 前置增强逻辑
Object result = method.invoke(o, args); // 调用目标类的方法
endService(); // 后置增强逻辑
return result; // 返回方法执行结果
}
// 前置服务
public void FrontService() {
System.out.println("根据您的需求进行市场调研");
}
// 后置服务
public void endService() {
System.out.println("为您提供一条龙的包办服务");
}
}
InvocationHandler:这是一个接口,动态代理的核心逻辑通过实现它的 invoke 方法来定义。
DyProxy:这是 InvocationHandler 的实现类,负责在运行时动态生成代理对象。
o:被代理的目标对象。
getProxyInterFace:通过 Proxy.newProxyInstance 方法生成代理对象。
invoke:在代理对象调用方法时,会执行 invoke 方法,在这里可以添加前置和后置逻辑。
(2)目标类ShootFactory
public class ShootFactory implements ByShoot {
public void Shoot(String size) {
System.out.println("已经为您定制好了一双大小为" + size + "的鞋子");
}
}
目标类是实现特定接口的类,包含核心业务逻辑,动态代理可以代理任意实现了接口的目标类。
(3)接口ByShoot
public interface ByShoot {
void Shoot(String size);
}
动态代理要求目标类必须实现接口,因为 Proxy.newProxyInstance 需要接口信息来生成代理对象。
(4)客户端Test
public class Test {
public static void main(String[] args) {
ShootFactory shootFactory = new ShootFactory();
ByShoot shoot = (ByShoot) new DyProxy(shootFactory).getProxyInterFace();
shoot.Shoot("42");
}
}
客户端通过 DyProxy 动态生成代理对象,并调用代理对象的方法,代理对象会在调用目标方法前后执行增强逻辑。
动态代理的工作流程
客户端调用 DyProxy 的 getProxyInterFace 方法,生成代理对象。
代理对象调用方法时,会触发 InvocationHandler 的 invoke 方法。
在 invoke 方法中:执行前置逻辑(FrontService),通过反射调用目标类的方法(method.invoke(o, args)),执行后置逻辑(endService)。
返回方法执行结果。
动态代理缺点:动态代理要求目标类必须实现接口,否则无法生成代理对象。
代理模式是Java中一种强大的设计模式,它通过代理对象间接访问目标对象,从而在不修改目标对象的情况下增强其功能。静态代理简单直观,但不够灵活;动态代理灵活性强,但必须依赖接口。在实际开发中,我们可以根据具体需求选择合适的代理方式。