Java 设计模式之代理模式

68 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情

一、代理模式

代理模式主要是使用代理对象劫持原始对象,达到对原始对象的控制

代理模式分为:

1、静态代理

2、动态代理

1.1、静态代理

//1、定义一个代理接口
public interface ITestProxy {

    void test();

    void login();

    void register();
}

//2、实现类
public class TestProxy implements ITestProxy{

    @Override
    public void test(){
        System.out.println("被代理对象执行了 test 方法");
    }

    @Override
    public void login() {
        System.out.println("被代理对象执行了 login 方法");
    }

    @Override
    public void register() {
        System.out.println("被代理对象执行了 register 方法");

    }
}

//3、代理类
public class StaticProxy implements ITestProxy{

    private final ITestProxy testProxy;

    public StaticProxy(ITestProxy testProxy) {
        this.testProxy = testProxy;
    }


    @Override
    public void test(){
        System.out.println("testBefore...");
        testProxy.test();
        System.out.println("testAfter...");
    }

    @Override
    public void login() {
        System.out.println("loginBefore...");
        testProxy.login();
        System.out.println("loginAfter...");
    }

    @Override
    public void register() {
        testProxy.register();
    }
}

//4、测试
public class Client {
    public static void main(String[] args) {
        //静态代理
        TestProxy testProxy = new TestProxy();
        StaticProxy staticProxy = new StaticProxy(testProxy);
        staticProxy.test();
        staticProxy.login();
        staticProxy.register();
    }
}
//打印结果
testBefore...
被代理对象执行了 test 方法
testAfter...
loginBefore...
被代理对象执行了 login 方法
loginAfter...
被代理对象执行了 register 方法

可以看到,上述我们使用代理对象 StaticProxy 劫持了原始对象 TestProxy,并插入了一些 log 打印

1.2、动态代理

动态代理主要分两种:

1、JDK 动态代理

2、cglib 动态代理

1.2.1、JDK 动态代理

//1、定义一个代理接口
public interface ITestProxy {

    void test();

    void login();

    void register();
}

//2、实现类
public class TestProxy implements ITestProxy{

    @Override
    public void test(){
        System.out.println("被代理对象执行了 test 方法");
    }

    @Override
    public void login() {
        System.out.println("被代理对象执行了 login 方法");
    }

    @Override
    public void register() {
        System.out.println("被代理对象执行了 register 方法");

    }
}

//3、定义一个动态代理 Handler
public class LogHandler implements InvocationHandler {

    final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if("test".equals(method.getName())){
            System.out.println("testBefore...");
            Object invoke = method.invoke(target, args);
            System.out.println("testAfter...");
            return invoke;
        }else if("login".equals(method.getName())){
            System.out.println("loginBefore...");
            Object invoke = method.invoke(target, args);
            System.out.println("loginAfter...");
            return invoke;
        }
        return method.invoke(target,args);
    }
}

//4、测试
public class Client {
    public static void main(String[] args) {
        //JDK 动态代理
        ClassLoader classLoader = ITestProxy.class.getClassLoader();
        Class<?>[] interfaces = new Class[]{ITestProxy.class};
      	//动态代理对象
        ITestProxy iTestProxy = (ITestProxy) Proxy.newProxyInstance(classLoader, interfaces, new LogHandler(new TestProxy()));
        iTestProxy.test();
        iTestProxy.login();
        iTestProxy.register();
    }

//打印结果
testBefore...
被代理对象执行了 test 方法
testAfter...
loginBefore...
被代理对象执行了 login 方法
loginAfter...
被代理对象执行了 register 方法

动态代理使用起来比较简单,关键方法是:java.langlreflect包下的 ProxynewProxyInstance方法

另外需要注意JDK 动态代理中被代理对象必须要实现一个接口。如果有一个需要被代理的对象没有实现接口,那么它就代理不了

如果我想实现被代理对象没有实现接口的这种方式要怎么办呢?

答:使用 cglib 动态代理

1.2.2、cglib 动态代理

首先我们需要进入,cglib 这个库:

implementation 'cglib:cglib:3.2.6'

接着来进行具体实现:

//1、定义一个需要被代理对象,可以看到 TestProxy 没有实现任何接口
public class TestProxy{

    public void test(){
        System.out.println("被代理对象执行了 test 方法");
    }

    public void login() {
        System.out.println("被代理对象执行了 login 方法");
    }

    public void register() {
        System.out.println("被代理对象执行了 register 方法");

    }
}

//2、定义 cglib 代理类
public class CglibProxy implements MethodInterceptor {

    //被代理对象
    private final Object object;

    public CglibProxy(Object object) {
        this.object = object;
    }

    public Object getProxyInstance(){
        //工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(object.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //常见代理对象并返回
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if("test".equals(method.getName())){
            System.out.println("testBefore...");
            Object invoke = method.invoke(object, args);
            System.out.println("testAfter...");
            return invoke;
        }else if("login".equals(method.getName())){
            System.out.println("loginBefore...");
            Object invoke = method.invoke(object, args);
            System.out.println("loginAfter...");
            return invoke;
        }
        return method.invoke(object,args);
    }
}

//3、测试
public class Client {
    public static void main(String[] args) {
        //cglib 动态代理
        TestProxy testProxy = new TestProxy();
        final TestProxy cgLibTestProxy = (TestProxy) new CglibProxy(testProxy).getProxyInstance();
        cgLibTestProxy.test();
        cgLibTestProxy.login();
        cgLibTestProxy.register();
    }
}

//打印结果
testBefore...
被代理对象执行了 test 方法
testAfter...
loginBefore...
被代理对象执行了 login 方法
loginAfter...
被代理对象执行了 register 方法

可以看到 cglib 动态代理我们主要使用了 Enhancer 工具类以及实现了 MethodInterceptor 接口

Tips: 如果被代理类对象实现了接口就使用 JDK 动态代理,否则使用 cglib 动态代理

二、总结

本篇文章我们介绍了 Java 设计模式之代理模式:

1、静态代理

2、动态代理:

1、JDK 动态代理

2、cglib 动态代理

好了,本篇文章到这里就结束了,希望能给你带来帮助 🤝