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

代理模式是什么?

代理模式是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的实现方式?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值