当前位置: 首页 > article >正文

【面试题】什么是代理以及如何实现代理

在 Spring 中,代理主要用于实现 AOP(面向切面编程),它是一种在不修改目标对象代码的情况下,为目标对象添加额外功能的机制。
在 Spring 中,静态代理是一种在编译期就确定代理关系的方式。它需要手动创建代理类,代理类实现与目标对象相同的接口,并在代理类中调用目标对象的方法,同时可以在调用前后添加额外的逻辑。

一、静态代理的实现步骤

  1. 定义目标接口:

    • 首先定义一个接口,该接口包含了目标对象需要实现的方法。例如:
    public interface TargetInterface {
        void method();
    }
    
  2. 实现目标对象:

    • 创建一个实现目标接口的类,作为被代理的目标对象。例如:
    public class TargetObject implements TargetInterface {
        @Override
        public void method() {
            System.out.println("目标对象的方法被调用。");
        }
    }
    
  3. 创建代理对象:

    • 手动创建一个代理类,同样实现目标接口。在代理类中持有一个目标对象的引用,并在代理类的方法中调用目标对象的方法,同时可以在调用前后添加额外的逻辑。例如:
    public class StaticProxy implements TargetInterface {
        private TargetInterface target;
    
        public StaticProxy(TargetInterface target) {
            this.target = target;
        }
    
        @Override
        public void method() {
            System.out.println("在调用目标对象方法之前执行的逻辑。");
            target.method();
            System.out.println("在调用目标对象方法之后执行的逻辑。");
        }
    }
    
  4. 使用代理对象:

    • 在客户端代码中,创建目标对象和代理对象,并通过代理对象调用目标对象的方法。例如:
    public class Client {
        public static void main(String[] args) {
            TargetInterface target = new TargetObject();
            TargetInterface proxy = new StaticProxy(target);
            proxy.method();
        }
    }
    

二、静态代理的特点

  1. 明确的代理关系:

    • 静态代理在编译期就确定了代理对象和目标对象的关系,代码结构相对清晰,容易理解。
    • 可以清楚地看到代理对象对目标对象的方法调用以及在调用前后添加的额外逻辑。
  2. 灵活性有限:

    • 如果需要为多个不同的目标对象创建代理,需要为每个目标对象都手动创建一个代理类,代码冗余度较高。
    • 当目标接口发生变化时,代理类也需要相应地进行修改,不符合开闭原则。
  3. 适用于简单场景:

    • 静态代理适用于一些简单的场景,例如为单个目标对象添加特定的功能,或者在不需要频繁变化的情况下进行代理。

总的来说,静态代理在 Spring 中虽然不常用,但它是理解代理机制的基础。在实际应用中,Spring 更多地使用动态代理来实现 AOP,以提高代码的灵活性和可维护性。
在 Spring 中,动态代理是在运行时动态地生成代理对象的一种机制。与静态代理不同,动态代理不需要为每个目标对象都手动创建一个代理类,而是根据目标对象的类型和需要添加的额外逻辑,在运行时自动生成代理对象。

一、JDK 动态代理

  1. 实现原理:

    • JDK 动态代理是基于 Java 的反射机制实现的。它要求目标对象必须实现一个接口,通过实现InvocationHandler接口并使用Proxy类来生成代理对象。
    • InvocationHandler接口的invoke方法是代理对象的核心方法,在这个方法中可以拦截对目标对象方法的调用,并在调用前后执行额外的逻辑。
  2. 实现步骤:

    • 定义目标接口:与静态代理一样,首先定义一个接口,目标对象实现这个接口。例如:
    public interface TargetInterface {
        void method();
    }
    
    • 实现目标对象:创建一个实现目标接口的类。例如:
    public class TargetObject implements TargetInterface {
        @Override
        public void method() {
            System.out.println("目标对象的方法被调用。");
        }
    }
    
    • 创建代理对象:实现InvocationHandler接口,在invoke方法中添加额外的逻辑,并调用目标对象的方法。然后使用Proxy类的newProxyInstance方法创建代理对象。例如:
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JdkDynamicProxy implements InvocationHandler {
        private Object target;
    
        public JdkDynamicProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("在调用目标对象方法之前执行的逻辑。");
            Object result = method.invoke(target, args);
            System.out.println("在调用目标对象方法之后执行的逻辑。");
            return result;
        }
    
        public static Object createProxy(Object target) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkDynamicProxy(target));
        }
    }
    
    • 使用代理对象:在客户端代码中,使用createProxy方法创建代理对象,并通过代理对象调用目标对象的方法。例如:
    public class Client {
        public static void main(String[] args) {
            TargetInterface target = new TargetObject();
            TargetInterface proxy = (TargetInterface) JdkDynamicProxy.createProxy(target);
            proxy.method();
        }
    }
    

二、CGLIB 动态代理

  1. 实现原理:

    • CGLIB 动态代理是通过生成目标类的子类来实现代理的。它使用字节码生成技术,在运行时动态地生成一个继承目标类的子类,并在子类中重写目标方法,添加额外的逻辑。
  2. 实现步骤:

    • 定义目标类:创建一个没有实现接口的类。例如:
    public class TargetClass {
        public void method() {
            System.out.println("目标对象的方法被调用。");
        }
    }
    
    • 创建代理对象:使用 CGLIB 的Enhancer类来创建代理对象。设置代理对象的父类为目标类,并实现MethodInterceptor接口,在intercept方法中添加额外的逻辑,并调用目标对象的方法。例如:
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibDynamicProxy implements MethodInterceptor {
        public Object createProxy(Object target) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("在调用目标对象方法之前执行的逻辑。");
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("在调用目标对象方法之后执行的逻辑。");
            return result;
        }
    }
    
    • 使用代理对象:在客户端代码中,使用createProxy方法创建代理对象,并通过代理对象调用目标对象的方法。例如:
    public class Client {
        public static void main(String[] args) {
            TargetClass target = new TargetClass();
            TargetClass proxy = (TargetClass) new CglibDynamicProxy().createProxy(target);
            proxy.method();
        }
    }
    

三、动态代理的特点

  1. 灵活性高:

    • 动态代理可以在运行时根据需要为不同的目标对象生成代理对象,不需要为每个目标对象都手动创建代理类。
    • 当目标对象的类型或需要添加的额外逻辑发生变化时,只需要修改代理对象的生成逻辑,而不需要修改目标对象的代码。
  2. 符合开闭原则:

    • 可以在不修改目标对象代码的情况下,为目标对象添加新的功能,符合开闭原则。
  3. 适用于复杂场景:

    • 动态代理适用于需要为多个不同类型的目标对象添加相同或不同的额外逻辑的复杂场景,如在企业级应用中实现日志记录、事务管理、安全检查等功能。

http://www.kler.cn/a/303750.html

相关文章:

  • SQL面试题——蚂蚁SQL面试题 连续3天减少碳排放量不低于100的用户
  • AutoHotKey自动热键AHK-正则表达式
  • nacos配置中心入门
  • MySQL查询某个数据库中特定表的空间占用大小
  • 试编写算法将单链表就地逆置(默认是带头节 点,如果是不带头节点地逆置呢?)
  • Android 13 实现屏幕熄屏一段时候后关闭 Wi-Fi 和清空多任务列表
  • shader 案例学习笔记之将坐标系分成4个象限
  • JVM面试真题总结(八)
  • 浅谈WebApi
  • 低压电抗器与电容器安装距离
  • 爆改YOLOv8|利用yolov9的ADown改进卷积Conv-轻量化
  • MySQL--数据库基础
  • 【iOS】——应用启动流程
  • 【GBase 8c V5_3.0.0 分布式部署(单机安装)】
  • 软件开发人员的真实面
  • TinyRedis项目复盘
  • 【动态规划】子序列问题二(数组中不连续的一段)
  • 系统资源智能管理:zTasker软件的监控与优化
  • 小需求:(vue2) 判断el-table某一行某一格里面是否包含‘百度‘两个字,如果包含,点击‘百度‘两个字跳转到‘百度‘页面,并给‘百度‘两个字加蓝色颜色
  • HTML+CSS - 网页布局之网格布局
  • IO多路复用,服务器,广播与组播
  • Apache Cordova开发教程-入门基础
  • 全志T113方案OTA
  • npm镜像源证书过期的问题解决
  • 【智路】智路OS airos-vehicle
  • SpringBoot + Vue + ElementUI 实现 el-table 分页功能详解