Java静态代理和动态代理

Java静态代理和动态代理

@author:Jingdai
@date:2021.05.03

代理模式

为目标对象提供(包装)一个代理,这个代理可以控制对目标对象的访问。

  • 通过代理对象调用目标对象
  • 代理对象可以添加监控和审查处理

静态代理

代理类和目标对象都要实现同一个接口,同时,代理对象需要持有目标对象,外界需要调用目标对象时,直接调用代理对象。同时,代理对象在调用时可以增加一些前置处理和后置处理。下面是一个例子。

接口

package proxy;

// interface
public interface Subject {
    
    void doRequest();
}

目标对象

package proxy;

public class SubjectImpl implements Subject {
    
    @Override
    public void doRequest() {
        System.out.println("Real Do Request!");
    }
}

代理对象

package proxy;

public class StaticProxy implements Subject {

    Subject subject;

    public StaticProxy(Subject subject) {
        this.subject = subject;
    }
    
    @Override
    public void doRequest() {
        System.out.println("Proxy pre process!");
        subject.doRequest();
        System.out.println("Proxy post process!");
    }
}

测试类

package proxy;

public class Test {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        StaticProxy staticProxy = new StaticProxy(subject);
        staticProxy.doRequest();
    }

}

输出

Proxy pre process!
Real Do Request!
Proxy post process!

从上面的例子可以看出,静态代理实现简单、容易理解,只要注意需要在代理对象中增加一个目标对象的属性。但是静态代理也有缺点,比如接口中的方法增加了一个,那么不光目标对象需要同时增加一个方法的实现,静态代理对象也需要增加一个方法的实现,非常繁琐。由此,就有了动态代理。

JDK动态代理

动态代理相比静态代理更加复杂一些,看下面这个图。(这里说的是JDK的动态代理,不是CGlib的动态代理)

在这里插入图片描述

这里的代理处理器类似于静态代理中的代理对象,它拥有目标对象,由它来完成目标对象的调用和前置、后置处理。Java通过一个InvocationHandler接口来实现一个代理处理器,这个接口中有一个invoke() 方法,它的形参method,就是指目标对象的方法,根据此来调用目标对象的方法。

定义好InvocationHandler之后,通过Proxy类的静态方法自动创建一个代理对象,然后通过这个自动创建的代理对象完成代理功能。看下面这个例子。注意这里的接口和目标对象都和静态代理一样。

接口

package proxy;

// interface
public interface Subject {
    
    void doRequest();
}

目标对象

package proxy;

public class SubjectImpl implements Subject {
    
    @Override
    public void doRequest() {
        System.out.println("Real Do Request!");
    }
}

代理处理器

package proxy;

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

public class ProxyHandler implements InvocationHandler{

    public Subject subject;

    public ProxyHandler(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
        throws IllegalAccessException, InvocationTargetException {

        System.out.println("Handler pre process!");
        Object object = method.invoke(subject, args);
        System.out.println("Handler post process!");
        return object;
    }
}

测试类

package proxy;

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

public class Test {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler handler = new ProxyHandler(subject);
        Subject dynamicProxy = (Subject) Proxy.newProxyInstance(SubjectImpl.class.getClassLoader(), 
            SubjectImpl.class.getInterfaces(), handler);
        dynamicProxy.doRequest();
    }

}

输出

Handler pre process!
Real Do Request!
Handler post process!

这里可以看出动态代理的代理对象是Java内部自动产生的,不是我们定义类去实现的,同时这些代理对象的类都是继承自Proxy类。

这里要注意一点,如果代理的目标对象中有多个方法,那么多个方法都会通过InvocationHandler的invoke()方法进行处理,那么多个方法的前置处理和后置处理就会变得一样,怎么对不同的方法进行不同的处理呢?很简单,可以通过对invoke()方法的method参数和args参数进行判断,根据不同的方法名和调用参数做出不同的处理,看下面的例子。

接口

package proxy;

// interface
public interface Subject {
    
    void doRequest();

    void doAnotherRequest();
}

目标对象

package proxy;

public class SubjectImpl implements Subject {
    
    @Override
    public void doRequest() {
        System.out.println("Real Do Request!");
    }

    @Override
    public void doAnotherRequest() {
        System.out.println("Real Do Another Request!");
    }
}

代理处理器

package proxy;

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

public class ProxyHandler implements InvocationHandler{

    public Subject subject;

    public ProxyHandler(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
        throws IllegalAccessException, InvocationTargetException {
        
        String methodName = method.getName();
        Object object = null;

        if ("doRequest".equals(methodName)) {
            System.out.println("Handler pre process doRequest!");
            object = method.invoke(subject, args);
            System.out.println("Handler post process doRequest!");
        } else if ("doAnotherRequest".equals(methodName)) {
            System.out.println("Handler pre process doAnotherRequest!");
            object = method.invoke(subject, args);
            System.out.println("Handler post process doAnotherRequest!");
        }
        return object;
    }
}

测试类

package proxy;

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

public class Test {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler handler = new ProxyHandler(subject);
        Subject dynamicProxy = (Subject) Proxy.newProxyInstance(SubjectImpl.class.getClassLoader(), 
            SubjectImpl.class.getInterfaces(), handler);
        dynamicProxy.doRequest();
        dynamicProxy.doAnotherRequest();
    }

}

输出

Handler pre process doRequest!
Real Do Request!
Handler post process doRequest!
Handler pre process doAnotherRequest!
Real Do Another Request!
Handler post process doAnotherRequest!

关于JDK动态代理原理可以参考下一篇学习笔记

参考

  • MOOC Java核心技术(高阶)华东师范大学(陈良育)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值