【设计模式】代理模式之JDK动态代理(案例详解)

简介: 案例讲解代理模式之JDK动态代理!
【辰兮要努力】:hello你好我是辰兮,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行!

博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端、后台、数据库、项目案例等相关知识点总结,感谢你的阅读和关注,希望我的博客能帮助到更多的人,分享获取新知,大家一起进步!

吾等采石之人,应怀大教堂之心,愿大家奔赴在各自的热爱里…

一、初识代理模式

关于jdk的动态代理和cglib的动态代理,我会通过两篇文章案例,和大家一起学习

首先我们一起了解一下什么是代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理访问目标对象。这样好处:可以在目标对象实现的基础上,增强额外的功能操作。(扩展目标对象的功能)。

在这里插入图片描述

CGLIB相关学习参考:【设计模式】代理模式之CGLIB动态代理(案例详解)


本期重点还是一起学习JDK动态代理

JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

JDK动态代理
1、实现InvocationHandler 
2、使用Proxy.newProxyInstance产生代理对象
3、被代理的对象必须要实现接口

JDK动态代理和CGLIB动态代理的区别?

1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类。

2、CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法, 并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。

请添加图片描述

Spring如何选择用JDK还是CGLiB?

1、当Bean实现接口时,Spring就会用JDK的动态代理。

2、当Bean没有实现接口时,Spring使用CGlib是实现。

3、可以强制使用CGlib(在配置文件中配置)

在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

二、代码案例学习

【模拟代码】JDK动态代理-相关实现代码

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * JDK动态代理具体实现原理:
 *
 * 1、通过实现InvocationHandler接口创建自己的调用处理器;
 * 2、通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
 * 3、通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
 * 4、通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
 */
public class JDKProxy implements InvocationHandler {

    private Object targetObject;

    /**
     * 将真实对象和代理对象建立联系,通过真实对象来返回一个代理对象(猜测该代理对象可能为真实对象的子类)
     * @param targetObject 真实对象
     * @return 代理对象
     */
    public Object newProxy(Object targetObject){
        this.targetObject = targetObject;

        /**
         * 参数1:类加载器,采用target本身的类加载器
         * 参数2:生成的动态代理对象挂在那个接口下,采用target实现的接口下,即Woker接口。
         * 参数3:定义实现方法逻辑的代理类,this表示当前对象,,它必须实现InvocationHandler的invoke方法。
         */
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(), this);
    }

    /**
     * 代理方法逻辑
     * @param proxy 代理对象
     * @param method 当前调度方法
     * @param args 当前方法参数
     * @return  代理结果返回
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        handle();
        System.out.println("拦截方法:" + method);
        System.out.println("方法参数:" + Arrays.toString(args));
        Object obj = method.invoke(targetObject,args);
        return obj;
    }


    private void handle(){
        //处理一些业务逻辑
        System.out.println("JDKProxy处理业务逻辑");
    }
}

创建一个user对象

@Data
public class User {

    private String id;
    private String name;
    private Integer age;

}

创建一个接口

public interface UserManager {
    public void addUser(String id, String userName);
}

创建如上接口的实现类

public class UserManagerImpl implements UserManager {

    @Override
    public void addUser(String id, String password) {
        System.out.println("调用了UserManagerImpl.addUser()方法!"+id+"-"+password);
    }

}

创建一个测试类

/**
 * @program: demo
 * @description: ProxyTest 测试类
 * @author: 辰兮要努力
 * @create: 2021-10-25 21:08
 */
public class ProxyTest {

    public static void main(String[] args) {
        JDKProxy jdkProxy = new JDKProxy();
        UserManager userManagerJDK = (UserManager)jdkProxy.newProxy(new UserManagerImpl());
        userManagerJDK.addUser("1", "chenXI");
    }
}

UserManager userManagerJDK = (UserManager)jdkProxy.newProxy(new UserManagerImpl());

如上的代码逻辑我可以理解为一个帮助类,帮助我们获取了一个代理对象

userManagerJDK.addUser("1", "chenXI");

我们在通过代理对象调用我们对象本身的方法


执行如上程序,输出结果

JDKProxy处理业务逻辑
拦截方法:public abstract void com.chenxi.demo.dao.UserManager.addUser(java.lang.String,java.lang.String)
方法参数:[1, chenXI]
调用了UserManagerImpl.addUser()方法!1-chenXI

Process finished with exit code 0

ps:我把代码都粘贴下来是希望如果你看不太懂可以一起本地实践


三、断点深入探究

我们一起debug执行一下对应的流程

1、首先是一个帮助类传入我们需要的对象后帮助我们生成一个代理对象

2、代理对象执行原对象的方法(功能)

在这里插入图片描述

真正执行方法的代码 Object obj = method.invoke(targetObject,args);

在这里插入图片描述

同理我们可以在此方法 Object obj = method.invoke(targetObject,args);前后新增方法可以增强原方法


JDK动态代理是java.lang.reflect.*包所提供的方式,它所代理的真实对象必须实现一个接口,依据该接口才能生成真实对象的代理

在这里插入图片描述

更详细的学习可以学习一下对应生成代理对象的源码newProxyInstance

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //如果h为空将抛出异常
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();//拷贝被代理类实现的一些接口,用于后面权限方面的一些检查
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            //在这里对某些安全权限进行检查,确保我们有权限对预期的被代理类进行代理
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 下面这个方法将产生代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 使用指定的调用处理程序获取代理类的构造函数对象
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //假如代理类的构造函数是private的,就使用反射来set accessible
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //根据代理类的构造函数来生成代理类的对象并返回
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

小结

AOP面向切面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

思考:有了动态代理,如果我们想在原有方法前后新增逻辑,如权限,日志等,我们就可以通过如上方式实现。


四、拓展面试相关

如何强制使用CGLIB实现AOP?

1、添加CGLIB库

(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)

2、在Spring配置文件中加入

<aop:aspectj-autoproxy proxy-target-class="true"/>

那到底何时使用JDK何时使用CGLIB?

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP。

3、如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

请添加图片描述


非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤️ 分享👥 留言💬thanks!!!

愿你们奔赴在自己的热爱里!

目录
相关文章
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
2天前
|
设计模式 前端开发 数据安全/隐私保护
前端必须掌握的设计模式——代理模式
代理模式(Proxy Pattern)是一种结构型设计模式,通过引入“替身”对象来间接访问真实对象,从而解耦并提升性能和安全性。例如,知名艺人复出后,经纪人作为代理筛选商单,确保只处理符合团队利益的请求。代码实现中,定义接口`IService`,艺人和经纪人都实现该接口,经纪人在访问时进行过滤和转发。代理模式常用于权限控制、性能优化等场景,如前端中的Tree-shaking和ES6的Proxy构造方法。
前端必须掌握的设计模式——代理模式
|
1月前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
46 4
|
3月前
|
Oracle Java 关系型数据库
CentOS 7.6操作系统部署JDK实战案例
这篇文章介绍了在CentOS 7.6操作系统上通过多种方式部署JDK的详细步骤,包括使用yum安装openjdk、基于rpm包和二进制包安装Oracle JDK,并提供了配置环境变量的方法。
295 80
|
2月前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
34 0
[Java]静态代理与动态代理(基于JDK1.8)
|
2月前
|
设计模式 网络协议 Java
06.动态代理设计模式
本文详细介绍了动态代理设计模式,包括其必要性、概念、实现方式及案例分析。动态代理允许在运行时动态创建代理对象,增强代码复用性和灵活性,减少类膨胀。文章通过对比静态代理,深入解析了动态代理的实现机制,如基于接口和类的动态代理,以及其在Retrofit中的应用。同时,讨论了动态代理的优势和潜在问题,如性能开销和调试难度。最后,提供了丰富的学习资源链接,帮助读者进一步理解和掌握动态代理。
25 1
|
2月前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
23 1
|
3月前
|
设计模式 缓存 安全
设计模式——代理模式
静态代理、JDK动态代理、Cglib 代理
设计模式——代理模式
|
3月前
|
设计模式 Java 数据安全/隐私保护
Java设计模式-代理模式(7)
Java设计模式-代理模式(7)
|
4月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
294 0