代理模式

代理模式

定义:

  • 代理对象具有和目标对象(真实业务对象)相同的方法,即实现共同的接口或继承同一个类
  • 代理对象持有目标对象的引用,并由代理对象控制对目标对象的操作

为什么需要代理模式?

  • 最主要的原因就是,在不改变目标对象方法的情况下对方法进行额外的处理
    代理对象可以在真实对象方法调用的前后增加一些业务逻辑,来完成一些功能且并不改变原有代码的实现。在这里插入图片描述

根据代理类的创建时机和创建方式的不同,我们可以将代理模式分为静态代理和动态代理两种形式;
静态代理:在程序运行前就已经存在编译好的代理类
动态代理:在程序运行期间根据需要动态的创建代理类及其实例来完成具体的功能是为动态代理。

静态代理:

例如 以下例子,我们添加打印日志的功能,对方法的调用做日志记录

用户服务接口类

public interface UserService {

    public UserVo login(String telephone, String password);

    public UserVo register(String telephone, String password);
}

用户服务具体实现类

public class UserServiceImpl implements UserService {

    @Override
    public UserVo login(String telephone, String password) {
        //...省略login逻辑...
        return null;
    }

    @Override
    public UserVo register(String telephone, String password) {
        //...省略register逻辑...
        return null;
    }
}

代理类

public class UserServiceImplProxy implements UserService {

    // 持有目标对象
    private UserService userService;

    // 通过构造方法传入目标对象
    public UserServiceImplProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public UserVo login(String telephone, String password) {
        //添加打印日志的功能
        System.out.println("start-->login()");
        //调用目标对象方法处理
        UserVo userVo =userService.login(telephone, password);
        //添加打印日志的功能
        System.out.println("start-->end()");
        return userVo;
    }

    @Override
    public UserVo register(String telephone, String password) {
        //...省略...
        return null;
    }
}

客户端测试类


public class Client {    
 
    public static void main(String[] args){

        //UserService userService=new UserServiceImpl();
        //将UserServiceImpl类对象替换为UserServiceImplProxy类对象,不需要改动太多代码
        UserService userService=new UserServiceImplProxy(new UserServiceImpl());
        userService.login("1767489757", "passWord");
    }
 
} 

缺点

  • 接口与代理类是1对1的,有多个接口需要代理,就需要新建多个代理类,繁琐,类爆炸。
  • 对同一个类的多个方法做相同的增强操作(如增加日志记录功能),同样的逻辑需要反复实现(需重写接口中定义的所有方法)

那么,我们是否可以使用同一个代理类来代理任意对象呢?例如根据所需要代理的目标对象动态生成不同的代理类对象,代理的逻辑也可以自己指定;

动态代理

我们可以使用动态代理来解决这个问题。所谓动态代理(Dynamic Proxy),就是我们不事先为每个目标类编写代理类,而是在运行的时候,动态地创建目标类对应的代理类,然后在应用程序中用代理类替换掉目标类。 那如何实现动态代理呢 ?

jdk动态代理

UML类图

在这里插入图片描述

相关的类和接口
要了解 Java 动态代理的机制,首先需要了解以下相关的类或接口:

java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

.Proxy 的静态方法

   //方法 1: 该方法用于获取指定代理对象所关联的调用处理器
    static InvocationHandler getInvocationHandler(Object proxy) 
    
    //方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
    static Class getProxyClass(ClassLoader loader, Class[] interfaces) 
    
    //方法 3:该方法用于判断指定类对象是否是一个动态代理类
    static boolean isProxyClass(Class cl) 
    
    //方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, 
        InvocationHandler h)

JDK动态代理实现步骤

  • (1)创建一个 跟被代理目标对象 实现相同接口的 Proxy代理类。
  • (2)通过实现 InvocationHandler 接口创建自己的调用处理器,并持有被代理对象的引用。然后在invoke方法中利用反射调用被代理对象的方法。
  • (3)利用Proxy.newProxyInstance方法创建代理对象,利用代理对象实现真实对象方法的调用。
代理类创建代码

 /** parameter types of a proxy class constructor
  *  代理类 构造函数的参数类型
 */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * the invocation handler for this proxy instance.
     * 代理类实例 所持有的调用处理器
     */
    protected InvocationHandler h;


/**
     * Returns an instance of a proxy class for the specified interfaces
     * 返回 实现指定接口的代理类的实例
     * that dispatches method invocations to the specified invocation handler.
     * 将方法调用分派给指定的调用处理器
     *
     */
 public static Object newProxyInstance(ClassLoader loader,Class>  [] interfaces,
  InvocationHandler h)  throws IllegalArgumentException {    
        if (h == null) {    
        throw new NullPointerException();    
    	}  
	/*
	查找或生成指定的代理类
	*/
    Class cl = getProxyClass(loader, interfaces);  

    try {    
   		/*
			通过反射机制获得代理类的构造函数,其唯一参数类型是调用处理器接口类型;
		*/
        Constructor cons = cl.getConstructor(constructorParams);    

		/*
			通过构造函数创建代理类实例,调用处理器对象作为参数被传入。
		*/
        return (Object) cons.newInstance(new Object[]   { h });  

    } catch (NoSuchMethodException e) {    

        throw new InternalError(e.toString());    

    ....

} 

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法有三个入参:

  • ClassLoader:用来创建并加载Proxy代理类的ClassLoader,通常这个Loader和被代理的类是同一个Loader类。
  • interfaces:被代理对象的接口列表,这是JDK强制要求的,被代理的对象必须实现某一个接口。其目的是在生成Proxy代理类时也实现这些接口,做到能够替代被代理类。
  • invoactionhandler对象:用于自定义代理增强处理逻辑,所有代理方法的调用都分派给它处理,再由它内部实现对真正目标对象方法的调用。

接下来我们再来看newProxyInstance( )方法中几个关键的地方

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..); 

// Proxy 使用指定的ClassLoader 动态创建实现了Interface 接口的代理类Class对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 

// 通过反射机制获得代理类的构造函数
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 

// 通过构造函数创建代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

自定义逻辑
JDK动态代理将代理的处理逻辑也抽离出来,定义了InvocationHandler接口, 通过实现该接口,处理自定义逻辑:
public interface InvocationHandler {
    void invoke(Object proxy, Method method, Object[] args);
}
  • proxy => 这个参数指定动态生成的代理对象
  • method => 这个参数是被调用的方法对象
  • args => 这个参数对应当前method方法中的参数

调用处理器根据这三个参数进行预处理或分派到目标类实例上执行

JDK动态代理实现示例代码

创建被代理对象的接口类Subject

public interface Subject {
    void request();
}

创建Subject接口的实现类:简单打印一句输出

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("request invoke");
    }
}

创建一个InvocationHandler的实现类

public class ConcreteInvocationHandler implements InvocationHandler {

  // 目标对象
    private Subject target;

    public ConcreteInvocationHandler(Subject target) {
        this.target = target;
    }

    //    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        ...//自定义代理增强处理逻辑    
        //执行方法,返回结果
        return method.invoke(target, args);
    }
}

客户端测试类

public class JDKDynamicProxyTest {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        InvocationHandler handler = new ConcreteInvocationHandler(subject);
        Subject proxy = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
                RealSubject.class.getInterfaces(), handler);
        proxy.request();
    }
}

查看JDK在运行时生成的字节码文件,用反编译工具看下$Proxy0的实现

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final void request() throws  {
        try {
        	// 
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

     public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
    	//省略...
    }
    
    public final int hashCode() throws  {
   		//省略...
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("main.java.design.service.Subject").getMethod("request");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

参考文章:

设计模式(11)动态代理 JDK VS CGLIB面试必问
Java 动态代理机制分析及扩展
Java 动态代理机制详解

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值