Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java中的动态代理以及在框架中的应用

Java中的动态代理以及在框架中的应用

作者头像
烂猪皮
发布于 2021-06-09 02:39:19
发布于 2021-06-09 02:39:19
1.4K00
代码可运行
举报
文章被收录于专栏:JAVA烂猪皮JAVA烂猪皮
运行总次数:0
代码可运行

一、静态代理&动态代理

1. 静态代理

我们先假设现在有怎么一个需求,要求你在不改动原有代码的情况下在所有类的方法前后打印日志。我们很容易想到静态代理,具体做法如下:

  • 为现有的所有类都编写一个对应的代理类,并且还需要让代理类与原有类实现相同的接口;
  • 在创建代理对象时,通过构造器传入一个目标对象,然后在代理对象的方法内部调用目标对象同名方法,并且在调用方法的前后打印日志。换而言之,代理对象=增强代码+原对象。有了代理对象后,我们在客户端就不再使用源对象,而是使用代理对象了。

静态代理的缺陷:从上面的静态代理实现方式上,我们很容易发现静态代理的缺陷。假设我们现在有很多类,那么就需要手动去实现很多个代理类,这样并不现实,那么我们应该考虑将这个任务交由计算机完成,接下来我们就来讨论动态代理的实现。

2. 动态代理

在讲解动态代理实现之前,我们先来回顾一下对象的创建过程。

从上面我们可以看出,创建一个对象并不仅仅是写一行 new 这么简单,底层还是隐含了许多信息的。不过我们至少可以了解到,一个对象的生成至少经历了以下这几个阶段:

那么到这里我们应该有大概的思路了。我们或许可以不写代理类,然后通过拦截器得到我们要代理的Class对象,之后再根据它加上反射机制创建代理实例(JDK动态代理的实现);或者让代理对象的class文件加载进来,然后通过修改其字节码来生成一个子类从而完成我们要做到的效果(CGLIB动态代理的实现)。

二、动态代理的实现

1. JDK动态代理

JDK动态代理的实现是利用拦截器(这个拦截器需要实现InvocationHandler接口),以及反射机制最终实现一个代理接口的匿名类。

所以在JDK中,提供了java.lang.reflect.InvocationHandler接口,此外还有一个比较重要的类java.lang.reflect.Proxy类。利用这两个类之间的相互配合完成动态代理的配置。那接下来我们来看代码实现:

首先定义一个接口和一个实现类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


public interface UserService {
    void addUser(String username, String password);
}

---------------------------------------------------
public class UserServiceImpl implements UserService{
    @Override
    public void addUser(String username, String password) {
        System.out.println("UserService.addUser(String, String)方法被调用");
    }
}

接下来我们就需要去定义一个拦截器去实现InvocationHandler以实现我们的业务逻辑,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


public class JDKProxyHandler implements InvocationHandler {

    // 被代理的原对象
    private Object targetObject;

    /**
     * 传入一个目标对象,生成一个代理对象并返回
     *
     * @param targetObject 原对象(目标对象)
     * @return 代理对象
     */
    public Object newProxy(Object targetObject) {
        this.targetObject = targetObject;
        // 返回一个代理对象
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                                      targetObject.getClass().getInterfaces(), 
                                      this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用日志打印
        log("权限校验中...");
        // 声明方法的返回值
        Object ret = null;
        // 调用invoke方法,所返回的值赋值给ret
        ret = method.invoke(targetObject, args);
        return ret;
    }

    /**
     * 模拟日志打印
     *
     * @param message 信息
     */
    private void log(String message) {
        System.out.println("【" + new SimpleDateFormat("yy-MM-dd hh:mm:ss").format(new Date()) + "】" 
                           + message);
    }
}

接下来我们就可以在客户端进行测试了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


public class Client {
    public static void main(String[] args) {
        JDKProxyHandler jdkProxyHandler = new JDKProxyHandler();
        UserService userService = (UserService) jdkProxyHandler.newProxy(new UserServiceImpl());
        userService.addUser("jo", "8820");
    }
}

最终结果如下:

2. CGLIB动态代理

CGLIB采用了非常底层的字节码技术,其原理是通过目标类(原来的类)的字节码创建一个新的子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入增强代码,所以代理类会将目标类作为自己的父类并为其中每个方法创建两个方法:

  1. 一个是于目标方法签名相同的类,它在方法中通过调用super来调用目标类中的方法;
  2. 以及另外一个Callback回调方法,它会判断这个方法是否绑定了拦截器(即实现了MethodInterceptor接口的对象),若存在则将调用intercept方法对目标方法进行代理,也就是在前后加上一些增强逻辑。intercept中就会调用上面介绍的签名相同的方法。

简而言之,就是CGLIB底层使用了ASM字节码处理框架,来修改字节码并生成新的类。那么接下来我们就用CGLIB来实现动态代理。

首先接口和实现接口的业务类还是复用上面的代码,不过我们还需要引入cglib的依赖,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

实现一个代理类处理类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


public class CGLIBProxyHandler implements MethodInterceptor {

    // CGLIB 需要代理的目标对象
    private Object targetObject;

    /**
     * 创建一个代理对象
     *
     * @param targetObject 目标类
     * @return 代理对象
     */
    public Object crateProxyObject(Object targetObject) {
        this.targetObject = targetObject;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetObject.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object ret = null;
        // 过滤方法
        if ("addUser".equals(method.getName())) {
            // 打印日志
            log("权限检验中...");
        }
        ret = method.invoke(targetObject, objects);
        return ret;
    }

    /**
     * 模拟日志打印
     *
     * @param message 信息
     */
    private void log(String message) {
        System.out.println("【" + new SimpleDateFormat("yy-MM-dd hh:mm:ss").format(new Date()) + "】" + message);
    }
}

客户端调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制


public class Client {
    public static void main(String[] args) {
        CGLIBProxyHandler cglibProxyHandler = new CGLIBProxyHandler();
        UserService userService = (UserService) cglibProxyHandler.crateProxyObject(new UserServiceImpl());
        userService.addUser("jo", "8820");
    }
}

最终执行结果如下:

以上就是JDK以及CGLIB两种实现动态代理方式的演示了。

三、CGLIB和JDK两种动态代理的应用与区别

1. 两者间区别

其中最主要的区别莫过于JDK是针对接口类生成代理,而不是针对类。而CGLIB则是针对类实现的动态代理。除此之外,上面我们提到CGLIB实现是通过目标类的字节码生成一个子类,所以我们可以很明显知道,这种方式不能适用与被final修饰的类。

2. Spring中的动态代理

2.1 Spring何时使用JDK/CGLIB实现AOP
  • 如果目标对象实现了接口,默认情况下Spring会采用JDK的动态代理实现AOP(不过可以通过配置强制使用CGLIB实现);
  • 如果目标对象没有实现接口,那么Spring就只会采用CGLIB库来完成动态代理。
2.2 如何强制使用CGLIB
  1. 添加CGLIB库的引用(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar);
  2. 在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

作者:周二鸭 出处:https://www.cnblogs.com/jojop/p/14117982.html

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JAVA烂猪皮 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
面试必问的动态代理,我们来看看它们的源码
但是追求优雅的程序员是不会这么写的,修改源代码不就破坏了面向对象的开闭原则了么。那么我们创建一个子类去继承Phone
Lvshen
2022/05/05
1800
面试必问的动态代理,我们来看看它们的源码
静态代理与动态代理
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/details/52025353
DannyHoo
2018/09/13
3450
静态代理与动态代理
jdk静态代理,jdk动态代理,cglib动态代理
代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道。如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法。如果公司卖多少钱,代理商也卖多少钱,那么代理商就赚不了钱。所以代理商在调用公司的卖方法后,加上自己的利润然后再把产品卖给客户。而客户部直接跟公司打交道,或者客户根本不知道公司的存在,然而客户最终却买到了产品。
互扯程序
2019/07/01
4910
jdk静态代理,jdk动态代理,cglib动态代理
Spring的两种代理JDK和CGLIB的区别浅谈
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
全栈程序员站长
2022/06/29
5580
Spring的两种动态代理Jdk与Cglib
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调InvokeHandler来处理。 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。 1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
全栈程序员站长
2022/08/04
7320
Spring的两种动态代理Jdk与Cglib
Java 动态代理详解
动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用、Java注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。
小旋锋
2019/01/21
1.1K0
Spring5学习笔记—CGlib动态代理
前面文章我们学习了关于Spring的IOC与AOP相关知识点,在此之前,我们主要学习Spring的一些核心概念,IOC和AOP等等。我们之前学习了简单了解了AOP如何借助动态字节码技术来构建动态代理类。实现动态代理的方式不止一种。本次系列文章主要介绍两种:JDK动态代理和CGlib动态代理,主要主要介绍CGlib动态代理。好了,话不多说,让我们开始吧😎😎😎。
程序员Leo
2023/11/16
2590
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类 的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
用户7954602
2025/02/04
1340
探索Java动态代理的奥秘:JDK vs CGLIB
静态代理 VS 动态代理
1.通过DRP这个项目,了解到了动态代理,认识到我们之前一直使用的都是静态代理,那么动态代理又有什么好处呢?它们二者的区别是什么呢?
Java团长
2018/08/03
3800
静态代理 VS 动态代理
Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题。
林老师带你学编程
2019/05/25
3060
结构型设计模式:代理模式
代理模式主要作用是不改变原始类的情况下,通过代理类来访问原始类方法,从而对原始类方法进行增强。当然,把增强功能直接放入原始类也是可以的,但是这样不符合开闭原则,而且容易把业务代码和非业务代码耦合。代理模式的实现有2种方式
jinjunzhu
2020/08/20
2860
你必须会的 JDK 动态代理和 CGLIB 动态代理
我们在阅读一些 Java 框架的源码时,基本上常会看到使用动态代理机制,它可以无感的对既有代码进行方法的增强,使得代码拥有更好的拓展性。通过从静态代理、JDK 动态代理、CGLIB 动态代理来进行本文的分析。
ytao
2020/06/04
5260
你必须会的 JDK 动态代理和 CGLIB 动态代理
Java动态代理模式jdk和cglib
jdk动态代理模式里面有个拦截器的概念,在jdk中,只要实现了InvocationHandler接口的类就是一个拦截器类 还使用了些反射的相关概念。 拦截器的概念不了解没关系,假如写了个请求到action,经过拦截器,然后才会到action。然后继续有之后的操作。 拦截器就像一个过滤网,一层层的过滤,只要满足一定条件,才能继续向后执行。 拦截器的作用:控制目标对象的目标方法的执行。
JavaEdge
2021/02/22
1590
Java设计模式(八)----代理模式
代理描述 1.生活中: 代理就是一个人或者一个组织代表其他人去做一件事的现实生活中的。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间
汤高
2018/01/11
6880
Java设计模式(八)----代理模式
Java 静态代理、Java动态代理、CGLIB动态代理
Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理。为啥要这样呢, 是因为使用代理有 2 个优势:
java思维导图
2019/05/21
7.1K0
动态代理竟然如此简单!
动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志框架、全局性异常处理、事务处理等。
cxuan
2021/01/06
3720
动态代理竟然如此简单!
第06天 静态代理和动态代理
代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象 (real object) 的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
程序员Leo
2023/08/16
1840
第06天 静态代理和动态代理
深入浅出MyBatis:反射和动态代理
前三篇详细总结了Mybatis的基本特性、常用配置、映射器,相对于Hibernate,映射器的配置相对复杂,但有很好的灵活性和扩展性,可以应对各种业务场景。熟练掌握这些内容,可以流畅的使用MyBatis进行开发了。
情情说
2018/04/27
1.2K0
深入浅出MyBatis:反射和动态代理
Java设计模式(八)----代理模式
代理模式 1.生活中: 代理就是一个人或者一个组织代表其他人去做一件事的现实生活中的。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作
汤高
2018/01/11
8910
Java设计模式(八)----代理模式
浅谈 Java 动态代理
Proxy Pattern是程序设计中的一种设计模式,又称委托模式。代理对象对真实对象提供一种代理以控制其他对象对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。、
ph0ebus
2023/05/16
2850
浅谈 Java 动态代理
相关推荐
面试必问的动态代理,我们来看看它们的源码
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验