【设计模式】Java设计模式 - 动态代理

简介: 动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。动态代理就需要建立真实对象和代理对象关系,再去实现代理逻辑方法。

【设计模式】Java设计模式 - 动态代理

😄 不断学习才是王道
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 关注我的CSDN: 一个有梦有戏的人
👊 打算连载Java设计模式,记录自己的学习心得,分享学习经验。

1、简介

动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。动态代理就需要建立真实对象和代理对象关系,再去实现代理逻辑方法。
Java中实现动态代理有许多方法,JDK、CGLIB、Javassist、ASM,常用的是JDKCGLIB,在spring中就是使用了这两种,然而mybatis还是用了Javassist

2、JDK动态代理

提供简单的接口类以及其实现类,在通过建立真是对象与代理对象的关系,并且实现代理逻辑。

(1)、准备接口类

先提供接口类:
HelloWorldService:

package com.lyd.demo.service;

/**
 * @Author: lyd
 * @Description: 普通的接口
 * @Date: 2022-08-17
 */
public interface HelloWorldService {
    public void sayHelloWorld();
}

HelloWorldServiceImpl:

package com.lyd.demo.service.impl;

import com.lyd.demo.service.HelloWorldService;

/**
 * @Author: lyd
 * @Description: 接口实现类
 * @Date: 2022-08-17
 */
public class HelloWorldServiceImpl implements HelloWorldService {
    @Override
    public void sayHelloWorld() {
        System.out.println("Hello World!");
    }
}

(2)、jdk动态代理

在JDK动态代理中,通过bind将真实对象和代理对象绑定起来,实现代理逻辑就要去实现java.lang.reflect.InvocationHandler接口,并且去实现invoke方法

①、首先需要声明 bind 方法去建立真实对象与代理对象的关系,把本类中的target保存真实对象。在通过Proxy的newProxyInstance方法来建立并生成对象,target.getClass().getClassLoader():target本身的类加载器,target.getClass().getInterfaces():把生成的动态代理对象下挂在接口中,this:当前对象,是定义实现方法逻辑的代理类。
②、实现InvocationHandler类中的invoke方法,可以实现代理逻辑,当我们使用了代理对象调度方法后就会进入到invoke方法中。

代码如下:

package com.lyd.demo.jdk;

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

/**
 * @Author: lyd
 * @Description: 动态代理绑定和代理逻辑实现
 * @Date: 2022-08-17
 */
public class JdkProxyExample implements InvocationHandler {

    // 真实对象
    private Object target = null;

    /**
     * 建立代理对象和真实对象的代理关系,并且返回代理逻辑实现
     * @param target 真实对象
     * @return 代理对象
     */
    public Object bind(Object target) {
        this.target = target; // 绑定对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 代理方法逻辑
     * @param proxy 代理对象
     * @param method 当前调度方法
     * @param args 当前方法的参数
     * @return  代理结果返回
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("进入代理逻辑方法");
        System.out.println("在调度真实对象之前的服务");
        Object obj = method.invoke(target, args); // 相当于调用了sayHelloWorld的方法
        System.out.println("在调度真实对象之后的服务");
        return obj;
    }
}

(3)、实例

测试jdk动态代理
通过 HelloWorldService proxy = (HelloWorldService) jdkProxyExample.bind(new HelloWorldServiceImpl()); 去代理对象,然后就是使用proxy去点接口里面的方法了。
代码如下:

package com.lyd.demo.test;

import com.lyd.demo.jdk.JdkProxyExample;
import com.lyd.demo.service.HelloWorldService;
import com.lyd.demo.service.impl.HelloWorldServiceImpl;

/**
 * @Author: lyd
 * @Description: 测试jdk动态代理
 * @Date: 2022-08-17
 */
public class jdkProxyText {
    public static void main(String[] args) {
        JdkProxyExample jdkProxyExample = new JdkProxyExample();
        // 绑定关系,因为挂载带接口下,因此声明一个代理对象
        HelloWorldService proxy = (HelloWorldService) jdkProxyExample.bind(new HelloWorldServiceImpl()); // 对象是new 实现类
        // 调用方法
        proxy.sayHelloWorld();
    }
}

实验结果
image.png

可以带入参数:以下是带入参数的例子

在接口中的方法添加参数
public void sayHelloWorld(String name);
在实现类的实现方法中打印出来
System.out.println("Hello World! " + name);

调用方法的时候添加参数

// 调用方法
proxy.sayHelloWorld("lyd");

代理模式十分重要,要理解里面的逻辑,可以通过debug打断点去一步一步查看。

3、CGLIB 动态代理

JDK动态代理需要接口才能完成,而如果不提供接口,只有实现的方法类,可以使用三方插件CGLIB来动态代理,采用这个动态代理技术,需要引入三方jar包,可以搭建maven项目,引入CGLIB jar包 ,通过maven官网搜索添加,亦可以直接下载jar文件。

(1)、加入CGLIB依赖

maven引入依赖:

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

(2)、准备实现类

准备一个实现类,cglib不需要接口,只要实现就可以:

package com.lyd.demo.impl;

/**
 * @Author: lyd
 * @Description: 实现类
 * @Date: 2022-08-17
 */
public class HelloWorldServiceImpl {
    public void sayHelloWorld(String name) {
        System.out.println("Hello World! " + name);
    }
}

(3)、代理类

代理类需要MethodInterceptor去实现方法

这里使用了增强者enhancer,通过设置超类和使用setCallback方法设置代理类,CGLIB是通过invokeSuper方法代理逻辑的。
package com.lyd.demo.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @Author: lyd
 * @Description:
 * @Date: 2022-08-17
 */
public class CglibProxyExample implements MethodInterceptor {

    /**
     * 生成CGLIB对象
     * @param cls -----对象类
     * @return Class的CGLIB代理对象
     */
    public Object getProxy(Class cls) {
        //CGLIB的增强类对象
        Enhancer enhancer = new Enhancer();
        //设置增强对象
        enhancer.setSuperclass(cls);
        //定义代理逻辑对象为当前对象,要求对象实现MethodInterceptor方法
        enhancer.setCallback(this);
        //生成返回代理对象
        return  enhancer.create();
    }

    /**
     * 代理逻辑方法
     * @param proxy 代理对象
     * @param method 方法
     * @param args 参数
     * @param methodProxy 方法代理
     * @return 代理逻辑返回
     * @throws Throwable 异常处理
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用对象之前");
        //使用CGLIB反射真实对象的方法
        Object result = methodProxy.invokeSuper(proxy, args);
        System.out.println("调用对象之后");
        return result;
    }
}

(4)、实例

测试:

package com.lyd.demo.test;

import com.lyd.demo.cglib.CglibProxyExample;
import com.lyd.demo.impl.HelloWorldServiceImpl;

/**
 * @Author: lyd
 * @Description: 测试CGLIB
 * @Date: 2022-08-17
 */
public class CGLIBProxyTest {
    public static void main(String[] args) {
        CglibProxyExample cglibProxyExample = new CglibProxyExample();
        HelloWorldServiceImpl proxy = (HelloWorldServiceImpl) cglibProxyExample.getProxy(HelloWorldServiceImpl.class); // 获取对象,可以不需要接口类
        proxy.sayHelloWorld("lyd");
    }
}

结果
image.png

相关文章
|
13天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
155 37
|
9天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
13天前
|
设计模式 安全 算法
【Java面试题汇总】设计模式篇(2023版)
谈谈你对设计模式的理解、七大原则、单例模式、工厂模式、代理模式、模板模式、观察者模式、JDK中用到的设计模式、Spring中用到的设计模式
【Java面试题汇总】设计模式篇(2023版)
|
13天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑】设计模式——原型模式
对比原型模式和传统方式的实现思路、代码方案、优缺点,阐述原型模式的使用场景,以及深拷贝、浅拷贝等相关概念,并扩展原型模式在Spring源码中的应用。
【Java笔记+踩坑】设计模式——原型模式
|
4天前
|
设计模式 安全 Java
Java 编程中的设计模式:单例模式的深度解析
【9月更文挑战第22天】在Java的世界里,单例模式就像是一位老练的舞者,轻盈地穿梭在对象创建的舞台上。它确保了一个类仅有一个实例,并提供全局访问点。这不仅仅是代码优雅的体现,更是资源管理的高手。我们将一起探索单例模式的奥秘,从基础实现到高级应用,再到它与现代Java版本的舞蹈,让我们揭开单例模式的面纱,一探究竟。
22 11
|
28天前
|
设计模式 缓存 算法
揭秘策略模式:如何用Java设计模式轻松切换算法?
【8月更文挑战第30天】设计模式是解决软件开发中特定问题的可重用方案。其中,策略模式是一种常用的行为型模式,允许在运行时选择算法行为。它通过定义一系列可互换的算法来封装具体的实现,使算法的变化与客户端分离。例如,在电商系统中,可以通过定义 `DiscountStrategy` 接口和多种折扣策略类(如 `FidelityDiscount`、`BulkDiscount` 和 `NoDiscount`),在运行时动态切换不同的折扣逻辑。这样,`ShoppingCart` 类无需关心具体折扣计算细节,只需设置不同的策略即可实现灵活的价格计算,符合开闭原则并提高代码的可维护性和扩展性。
39 2
|
28天前
|
设计模式 Java
Java 设计模式之谜:工厂模式与抽象工厂模式究竟隐藏着怎样的神奇力量?
【8月更文挑战第30天】在Java编程中,设计模式为常见问题提供了高效解决方案。工厂模式与抽象工厂模式是常用的对象创建型设计模式,能显著提升代码的灵活性、可维护性和可扩展性。工厂模式通过定义创建对象的接口让子类决定实例化哪个类;而抽象工厂模式则进一步提供了一个创建一系列相关或相互依赖对象的接口,无需指定具体类。这种方式使得系统更易于扩展和维护。
31 1
|
26天前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
67 0
|
13天前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式
|
5天前
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。