【长话短说】听说你知道Java代理模式?

本文详细介绍了代理模式的概念及其在Java中的应用。包括静态代理和动态代理的实现方式,以及JDK动态代理和CGLib动态代理的具体操作步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式是什么?

代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。举个例子:比如我们平常相亲前会找媒婆帮忙,此时媒婆的作用就是代理。

为什么要用代理模式?

因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。

如何用代理模式?

举个例子,我有一个接口类和一个实现类

public interface HelloWorld {
     public void sayHelloWorld(String name);
} 

public class HelloWorldImpl1 implements HelloWorld {
    public void sayHelloWorld(String name) {
        System.out.println("hello,world," + name);
    }
}

此时我想在调用HelloWordImpl的sayHelloWorld方法前输出:before,方法后输出:after,要怎么写?简单粗暴的写法为:

public class HelloWorldImpl1 implements HelloWorld {
    public void sayHelloWorld(String name) {
        before();
        System.out.println("hello,world" + name);
        after();
    }

    public void before() {
        System.out.println("before");
    }

    public void after() {
        System.out.println("after");
    }
}

直接在实现类HelloWorldImpl1 中加入两个方法,分别输出before和after。但是我就是不想在HelloWorldImpl1 里面写呢?那我是不是要找个代理类帮我写?好,代理类来了:

public class HelloWorldProxy implements HelloWorld {
    private HelloWorldImpl1 helloWorldImpl1;

    public HelloWorldProxy(HelloWorldImpl1 helloWorldImpl1) {
        this.helloWorldImpl1 = helloWorldImpl1;
    }

    public void sayHelloWorld(String name) {
        before();
        System.out.println("hello,world," + name);
        after();
    }

    public void before() {
        System.out.println("before");
    }

    public void after() {
        System.out.println("after");
    }
}

//写个manin方法验证下
HelloWorldImpl1 helloWordImpl1 = new HelloWorldImpl1();
HelloWorldProxy helloWorldProxy = new HelloWorldProxy(helloWordImpl1);
helloWorldProxy.sayHelloWorld("Jerry");
//结果输出:
before
hello,world,Jerry
after

此时HelloWorldProxy 就是我们的“媒婆”,也就是代理类。但是如果我们有另外一个实现类比如HelloWorldImpl2,要实现同样的功能那不是又要写另外一个代理类吗?
没错,这样写的话就不够通用,这种代理写法属于“静态代理”,我们需要改造一下,改成“动态代理”,如下:

1、使用JDK动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CommonProxy implements InvocationHandler {
    private Object target;

    public CommonProxy(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object invokeMethodResult = method.invoke(target, args);
        after();
        return invokeMethodResult;
    }

    public void before() {
        System.out.println("before");
    }

    public void after() {
        System.out.println("after");
    }
}

我们新建一个CommonProxy,作为通用的代理包装类,这个类实现InvocationHandler接口,同时在构造方法中传入指定接口的实现类,在invoke方法中插入我们自己所需的before和after方法,有了这个代理包装类后,要怎么调用呢?如下:

//写个main方法测试

HelloWorldImpl1 helloWorld = new HelloWorldImpl1();
CommonProxy commonProxy = new CommonProxy(helloWorld);
// 创建代理实现类
HelloWorldImpl1 helloWorldProxy = (HelloWorldImpl1) Proxy.newProxyInstance(helloWorld.getClass()
            .getClassLoader(), helloWorld.getClass().getInterfaces(), commonProxy);
helloWorldProxy.sayHelloWorld("Puma");

//执行结果:
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.test.HelloWorldImpl1
    at com.test.HelloWorldTest.main(HelloWorldTest.java:22)

。。好像报错了,查看报错信息是该代理类不能够转为HelloWorldImpl1 ,原因是因为JDK动态代理有个缺点:必须代理有接口的类,在Proxy.newProxyInstance调用后返回的必须是以接口来接收,我们修改下:

HelloWorldImpl1 helloWorld = new HelloWorldImpl1();
CommonProxy commonProxy = new CommonProxy(helloWorld);
// 创建代理类
HelloWorld helloWorldProxy = (HelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(),
            helloWorld.getClass().getInterfaces(), commonProxy);
helloWorldProxy.sayHelloWorld("Puma");

//执行结果:
before
hello,world,Puma
after

对于有点强迫症的人来说,在创建代理类的时候才不想一直写Proxy.newProxyInstance。。。这种代码,我们把它提取出来下放在CommonProxy 里

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

public class CommonProxy implements InvocationHandler {
    private Object target;

    public CommonProxy(Object target) {
        this.target = target;
    }

    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object invokeMethodResult = method.invoke(target, args);
        after();
        return invokeMethodResult;
    }

    public void before() {
        System.out.println("before");
    }

    public void after() {
        System.out.println("after");
    }
}

//写个main方法测试
HelloWorldImpl1 helloWorld1 = new HelloWorldImpl1();
CommonProxy commonProxy = new CommonProxy(helloWorld1);
HelloWorld helloWorldProxy = commonProxy.getProxy();
helloWorldProxy.sayHelloWorld("Puma");

//执行结果:
before
hello,world,Puma
after

由上可见,CommonProxy 实现了通用的代理功能,下次如果HelloWorld 接口有新的实现类比如HelloWorldImpl2,则代理类不用改,只改接口实现类就行。
但是如我们刚才所说,JDK动态代理的缺点是必须代理有接口的类,如果我有一个类没有实现接口呢?那就需要另一种代理模式

2、使用CGlib动态代理

假设我有个新的类,没有实现任何接口 ,如下:

public class HelloWorldImpl3 {
    public void sayHelloWorld(String name) {
        System.out.println("hello,world," + name);
    }
}

既然要使用代理,那就建个使用CGlib的代理类,如下:

package com.test;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CGlibProxy implements MethodInterceptor {
    private Object target;

    public CGlibProxy(Object target) {
        this.target = target;
    }

    public static <T> T getProxy(T t) {
        Enhancer enhancer = new Enhancer();
        // 设置被代理的类
        enhancer.setSuperclass(t.getClass());
        // 设置回调方法,待会执行intercept方法
        enhancer.setCallback(new CGlibProxy(t));
        // 创建代理对象
        return (T) enhancer.create();
    }

    public Object intercept(Object target, Method method, Object[] params, MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(target, params);
        after();
        return result;
    }

    public void before() {
        System.out.println("before");
    }

    public void after() {
        System.out.println("after");
    }
}

新建一个CGlib代理类CGlibProxy,实现MethodInterceptor 接口,在intercept方法里面,调用代理对象proxy调用被代理类(即HelloWorldImpl3 )的方法,并在方法执行前后插入我们所需要的信息,其中如何生成代理对象在getProxy方法里,通过创建Enhancer 对象来生成,我们来验证下:

//写个main方法
HelloWorldImpl3 helloWorldImpl3 = new HelloWorldImpl3();
HelloWorldImpl3 helloWorldImpl3Proxy = CGlibProxy.getProxy(helloWorldImpl3);
helloWorldImpl3Proxy.sayHelloWorld("Jerry");

//查看执行结果
before
hello,world,Jerry
after

总结:以上我们已经通过使用JDK动态代理和使用CGlib动态代理两种方式来创建代理对象,通过代理对象来帮助我们实现被代理类不能或者不好实现的功能,这个“媒婆”作用 不小,对于一些通用的功能,比如记录日志等等,使用非常方便,spring的aop主要也是用代理模式来实现,下一次我们聊聊:听说你知道spring aop的实现方式?

以下是一个简单的成语消消乐 Java 代码示例: ```java import java.util.*; public class ChengYuXiaoXiaoLe { public static void main(String[] args) { String[] chengyu = {"天上人间", "人山人海", "海阔天空", "空空如也", "也就是说", "说一不二", "二话不说", "说三道四", "四面楚歌", "歌功颂德", "德高望重", "重整旗鼓", "鼓起勇气", "气吞山河", "河东狮吼", "吼声震天", "天下无敌", "敌不过我", "我行我素", "素不相识", "识趣的人", "人云亦云", "云淡风轻", "轻松愉快", "快人快语", "语重心长", "长话短说", "说来说去", "去而复返", "返老还童", "童叟无欺"}; int score = 0; int count = 0; int round = 1; while (true) { System.out.println("第" + round + "回合,当前得分:" + score); System.out.println("当前成语列表:"); for (int i = 0; i < chengyu.length; i++) { System.out.print(chengyu[i] + " "); } System.out.println(); Scanner scanner = new Scanner(System.in); System.out.println("请输入一个成语:"); String input = scanner.nextLine(); boolean found = false; for (int i = 0; i < chengyu.length; i++) { if (input.equals(chengyu[i])) { found = true; chengyu[i] = ""; score += input.length(); count++; break; } } if (!found) { System.out.println("输入的成语不合法或已被消除,请重新输入!"); } if (count == chengyu.length) { System.out.println("恭喜你,已经消除了所有成语!"); System.out.println("最终得分:" + score); break; } round++; } } } ``` 此代码实现了一个简单的成语消消乐游戏,通过在控制台输入成语进行游戏。游戏的规则是,输入的成语必须与列表中的成语相连(即第一个成语的最后一个字必须与第二个成语的第一个字相同),并且列表中的成语只能使用一次。每次输入一个成语,如果输入的成语合法且在列表中存在,则该成语将从列表中消除,并且得分将增加该成语的长度。当所有成语都被消除时,游戏结束,输出最终得分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值