java面试常见模式问题---代理模式

目录
  • 1、静态代理
  • 2、动态代理
    • 面试题一:JDK动态代理和CGLIB动态代理区别?
    • 面试题二:JDK 动态代理为什么只能对实现了接口的类生成代理?
  • 总结
    • 本篇总结的是 代理设计模式,后续会经常更新~
    • 代理模式最直观的解释就是,通过代理,将被代理对象 “增强”!(即,扩展被代理对象的功能)
    • 代理模式分为静态代理,和动态代理:动态代理的代理类是动态生成的 , 静态代理的代理类是我们提前写好的逻辑。
    • Java 中实现动态代理的方式有 2 种:
    • JDK 动态代理
    • CGLIB 动态代理

    1、静态代理

    静态代理角色分析

    • 抽象角色 :一般使用接口或者抽象类来实现。
    • 真实角色 :被代理的角色。
    • 代理角色: 代理真实角色 , 代理真实角色后 ,一般会做一些附属的操作。
    • 调用方:使用代理角色来进行一些操作。

    我们以租客租客租房子为例,涉及到的对象有:租客、中介、房东。(房东即为被代理对象,中介即为代理对象)

    租客通过中介之手租住房东的房子,代理对象中介需要寻找租客租房,并从中获取中介费用。

    代码实现

    Rent.java 即抽象角色

    // 抽象角色:租房
    public interface Rent {
       public void rent();
    }

    Host.java 即真实角色

    // 真实角色: 房东,房东要出租房子
    public class Host implements Rent{
       public void rent() {
           System.out.println("房屋出租");
      }
    }

    Proxy.java 即代理角色

    //代理角色:中介
    public class Proxy implements Rent {
       private Host host;
       public Proxy() { }
       public Proxy(Host host) {
           this.host = host;
      }
       // 租房
       public void rent(){
           seeHouse();
           host.rent();
           fare();
      }
       // 看房
       public void seeHouse(){
           System.out.println("带房客看房");
      }
       // 收中介费
       public void fare(){
           System.out.println("收中介费");
      }
    }
    

    Client.java 调用方,即客户

    // 客户类,一般客户都会去找代理!
    public class Client {
       public static void main(String[] args) {
           // 房东要租房
           Host host = new Host();
           // 中介帮助房东
           Proxy proxy = new Proxy(host);
           // 你去找中介!
           proxy.rent();
      }
    }

    静态代理的缺点

    需要手动创建代理类,如果需要代理的对象多了,那么代理类也越来越多。

    为了解决,这个问题,就有了动态代理 !

    2、动态代理

    说到动态代理,面试的时候肯定会问动态代理的两种实现方式:

    先来看公共的 UserService 接口,和 UserServiceImpl 实现类:

    /**
     * @author csp
     * @date 2025-06-03
     */
    public interface UserService {
        /**
         * 登录
         */
        void login();
        /**
         * 登出
         */
        void logout();
    }
    
    /**
     * @author csp
     * @date 2025-06-03
     */
    public class UserServiceImpl implements UserService{
        @Override
        public void login() {
            System.out.println("用户登录...");
        }
        @Override
        public void logout() {
            System.out.println("用户推出登录...");
        }
    }
    

    JDK 动态代理

    代码如下

    /**
     * @author csp
     * @date 2025-06-03
     */
    public class JDKProxyFactory implements InvocationHandler {
        // 目标对象(被代理对象)
        private Object target;
        public JDKProxyFactory(Object target) {
            super();
            this.target = target;
        }
        /**
         * 创建代理对象
         *
         * @return
         */
        public Object createProxy() {
            // 1.得到目标对象的类加载器
            ClassLoader classLoader = target.getClass().getClassLoader();
            // 2.得到目标对象的实现接口
            Class<?>[] interfaces = target.getClass().getInterfaces();
            // 3.第三个参数需要一个实现invocationHandler接口的对象
            Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, this);
            return newProxyInstance;
        }
        /**
         * 真正执行代理增强的方法
         *
         * @param proxy  代理对象.一般不使用
         * @param method 需要增强的方法
         * @param args   方法中的参数
         * @return
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("JDK 动态代理:登录/登出前逻辑校验......");
            Object invoke = method.invoke(target, args);
            System.out.println("JDK 动态代理:登录/登出后日志打印......");
            return invoke;
        }
        public static void main(String[] args) {
            // 1.创建对象
            UserServiceImpl userService = new UserServiceImpl();
            // 2.创建代理对象
            JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(userService);
            // 3.调用代理对象的增强方法,得到增强后的对象
            UserService userServiceProxy = (UserService) jdkProxyFactory.createProxy();
            userServiceProxy.login();
            System.out.println("==================================");
            userServiceProxy.logout();
        }
    }
    

    输出结果如下

    JDK 动态代理:登录/登出前逻辑校验......
    用户登录...
    JDK 动态代理:登录/登出后日志打印......
    ==================================
    JDK 动态代理:登录/登出前逻辑校验......
    用户推出登录...
    JDK 动态代理:登录/登出后日志打印......

    CGLIB 动态代理

    代码如下:

    /**
     * @author csp
     * @date 2025-06-03
     */
    public class CglibProxyFactory implements MethodInterceptor {
        // 目标对象(被代理对象)
        private Object target;
        // 使用构造方法传递目标对象
        public CglibProxyFactory(Object target) {
            super();
            this.target = target;
        }
        /**
         * 创建代理对象
         *
         * @return
         */
        public Object createProxy() {
            // 1.创建Enhancer
            Enhancer enhancer = new Enhancer();
            // 2.传递目标对象的class
            enhancer.setSuperclass(target.getClass());
            // 3.设置回调操作
            enhancer.setCallback(this);
            return enhancer.create();
        }
        /**
         * 真正执行代理增强的方法
         * @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 invoke = method.invoke(target, objects);
            System.out.println("cglib 动态代理:登录/登出后日志打印......");
            return invoke;
        }
        public static void main(String[] args) {
            // 1.创建对象
            UserServiceImpl userService = new UserServiceImpl();
            // 2.创建代理对象
            CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(userService);
            // 3.调用代理对象的增强方法,得到增强后的对象
            UserService userServiceProxy = (UserService) cglibProxyFactory.createProxy();
            userServiceProxy.login();
            System.out.println("==================================");
            userServiceProxy.logout();
        }
    }
    

    测试结果如下

    cglib 动态代理:登录/登出前逻辑校验......
    用户登录...
    cglib 动态代理:登录/登出后日志打印......
    ==================================
    cglib 动态代理:登录/登出前逻辑校验......
    用户推出登录...
    cglib 动态代理:登录/登出后日志打印......

    面试题一:JDK动态代理和CGLIB动态代理区别?

    ① JDK 动态代理本质上是实现了被代理对象的接口,而 CGLib 本质上是继承了被代理对象,覆盖其中的方法。

    ② JDK 动态代理只能对实现了接口的类生成代理,CGLib 则没有这个限制。但是 CGLib 因为使用继承实现,所以 CGLib 所以无法对 final private 方法static方法进行代理。

    ③ JDK 动态代理是 JDK 里自带的,CGLib 动态代理需要引入第三方的 jar 包。

    ④ 在调用代理方法上,JDK动态代理是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用。(看过一篇文章,介绍说 FastClass 简单的理解,就是使用一个 index 下标作为入参,可以直接定位到要调用的方法直接,并进行调用)

    在性能上,JDK1.7 之前,由于使用了 FastClass 机制,CGLib 在执行效率上比 JDK 快,但是随着 JDK 动态代理的不断优化,从 JDK 1.7 开始,JDK 动态代理已经明显比 CGLib 更快了。

    面试题二:JDK 动态代理为什么只能对实现了接口的类生成代理?

    根本原因是通过 JDK 动态代理生成的类已经继承了 Proxy 类,所以无法再使用继承的方式去对类实现代理。

    总结

    文章会不定时更新,有时候一天多更新几篇,如果帮助您复习巩固了知识点,还请三连支持一下,后续会一点点的更新!希望大家多多关注的其他内容!

    本文转自网络,如有侵权请联系客服删除。