代理模式简述

发布于:2025-04-15 ⋅ 阅读:(30) ⋅ 点赞:(0)

目录

一、主要角色

二、类型划分

三、静态代理

示例

缺点

四、动态代理

JDK动态代理

示例

 缺点

CGLib动态代理

导入依赖

示例

五、Spring AOP


        代理模式是一种结构型设计模式,通过代理对象控制对目标对象的访问,可在不改变目标对象情况下增强其功能,隐藏实现细节。

一、主要角色

  • Subject(业务接口类):可以是抽象类或接口
  • RealSubject(业务实现类):具体的业务执行,即被代理的对象
  • Proxy(代理类):RealSubject的代理

二、类型划分

根据代理的创建时期,可分为静态代理和动态代理

  • 静态代理:由程序员创建或工具自动生成代理类源代码并编译,程序运行前代理类的字节码文件已经存在;
  • 动态代理:在程序运行时运用反射机制动态创建而成

为方便下面的讲解,我们先提前创建业务接口类和业务实现类

//业务接口类
public interface HouseSubject {
    void rentHouse();
}
//业务实现类(被代理类)
public class RealHouseSubject implements HouseSubject {
    @Override
    public void rentHouse() {
        System.out.println("我是房东,我出租房子");
    }
}

三、静态代理

示例
//代理类
public class HouseProxy implements HouseSubject{
    //中介出售房子前要先有房东的授权
    private HouseSubject houseSubject;

    public HouseProxy(HouseSubject houseSubject) {
        this.houseSubject = houseSubject;
    }

    @Override
    public void rentHouse() {
        System.out.println("我是中介,我开始代理房东出租房子");
        houseSubject.rentHouse();
        System.out.println("我是中介,代理结束");
    }
}

使用代理类执行

public class Main {
    public static void main(String[] args) {
        //创建代理对象
        HouseSubject subject=new HouseProxy(new RealHouseSubject());
        subject.rentHouse();
    }
}

缺点
  • 为每个被代理类编写代理类,代码冗余,随着业务的发展,类数量膨胀,项目管理难度增大
  • 被代理类接口变化时,所有相关代理类都需修改,易出错,可维护性差
  • 只能服务特定被代理类,难以应对新类和多变的额外逻辑需求,缺乏灵活性

四、动态代理

JDK动态代理
示例

自定义JDKInvocationHandler 并重写invoke(),在invoke()中会调用目标方法,并自定义一些处理逻辑

import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//实现 InvocationHandler接口可以被代理对象的方法进行功能增强
@Slf4j
public class JDKInvocationHandler implements InvocationHandler {

    //目标类
    private Object target;

    public JDKInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 调用目标方法,并对方法进行增强
     * @param proxy 代理对象
     * @param method 代理对象需要实现的方法
     * @param args method方法所对应的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("JDK动态代理开始");
        //调用目标函数
        Object result=method.invoke(target,args);
        log.info("JDK动态代理结束");
        return null;
    }
}

创建代理对象,执行逻辑 

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        RealHouseSubject target=new RealHouseSubject();

        //通过被代理类、被代理类实现的接口、方法调用处理器来创建一个代理类
        /**
         *  ClassLoader loader 类加载器,用于加载代理对象
         *  Class<?>[] interfaces 被代理类实现的一些接口(jdk只能代理实现了接口的类)
         *  java.lang.reflect.InvocationHandler h 实现了InvocationHandler接口的对象
         */
        HouseSubject proxy=(HouseSubject) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            new Class[]{HouseSubject.class},
            new JDKInvocationHandler(target)
        );
        //使用代理类
        proxy.rentHouse();
    }
}

 缺点

只能代理实现了接口的类,不能代理普通类

CGLib动态代理

导入依赖
<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>3.3.0</version>
</dependency>
示例
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

@Slf4j
public class CGLibMethodInterceptor implements MethodInterceptor {
    private Object target;
    public CGLibMethodInterceptor(Object target) {
        this.target = target;
    }

    /**
     *
     * @param o 被代理的对象
     * @param method 目标方法
     * @param objects 方法入参
     * @param methodProxy 用于调用原始方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //代理增强内容
        System.out.println("CGLib代理开始");
        //通过反射调用被代理的方法
        Object retVal=methodProxy.invoke(target,objects);
        System.out.println("CGLib代理结束");
        return retVal;
    }
}
import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        HouseSubject target=new RealHouseSubject();
        HouseSubject proxy= (HouseSubject)Enhancer.create(target.getClass(),new CGLibMethodInterceptor(target));
        proxy.rentHouse();
    }
}

五、Spring AOP

Spring AOP是基于动态代理实现的,动态代理有JDK和CGLIB两种方式,运行时使用哪种方式与项目配置和代理的对象有关。

proxy-target-class="false"情况下,若代理对象实现接口,默认使用JDK动态代理;若未实现接口,则会用CGLIB动态代理。当然,即便对象实现接口,也能通过xml配置proxy-target-class="true"或在配置类上使用注解@EnableAspectJAutoProxy(proxyTargetClass = true) 强制使用CGLIB动态代理。

Spring Boot 2.X开始默认使用CGLIB代理,也就是proxy-target-class="true"。

二者各有优劣,需依项目实际情况抉择。JDK动态代理优势在于基于Java原生支持,无需额外依赖,性能较好,适用于对性能要求高且目标对象有接口的场景;但是只能代理实现了接口的对象。

CGLIB动态代理优势在于能代理无接口的普通类,功能更强大、灵活性更高;但是需额外注入依赖,且通过继承创建代理,若类被final修饰则无法代理,生成代理对象的性能开销相对较大。


网站公告

今日签到

点亮在社区的每一天
去签到