JDK代理

代理模式

代理模式介绍

代理模式就是某一个类需要访问一个目标类,不是通过直接访问的方式,而是先访问一个中间类,再由这个中间类去访问目标类。代理模式一般用于保护目标类,或者增强目标类。

代理模式类图

interface是顶层接口,proxy是代理类,Impl是目标类,client通过访问proxy进而访问到真正的Impl。

在这里插入图片描述

代理模式的优缺点

  • 优点

1.代理模式能的能保护目标类的实现细节。
2.增强目标对类,可以不修改目标类的情况下在代理类中添加一些其他实现。
3.降低了目标类和调用类之间的耦合。

  • 缺点

1.代理模式会造成系统设计中类的数目的增加。
2.在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
3.增加了系统的复杂度。

JDK代理分类

  • 静态代理
  • JDK动态代理

JDK静态代理

静态代理

静态代理的目标类和代理类需要实现同一接口。

优点:静态代理可以做到在不修改目标类的前提下,对目标类的功能进行扩展。

缺点:但是这种方式会导致多一些接口,并且接口增加了方法,目标类和代理类都需要修改。

示例代码

  • 接口
public interface UserService {
    void eat();
}
  • 目标类
public class UserServiceImpl implements UserService{
    @Override
    public void eat() {
        System.out.println("eat");
    }
}
  • 代理类
public class ProxyImpl implements UserService{
    private UserService userService;
    public ProxyImpl(UserService userService) {
        this.userService = userService;
    }
    @Override
    public void eat() {
        System.out.println("吃饭之前");
        userService.eat();
        System.out.println("吃饭之后");
    }
}
  • 调用者
public class Demo {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        ProxyImpl proxyImpl = new ProxyImpl(userService);
        proxyImpl.eat();
    }
}

JDK动态代理

动态代理

javajava.lang.reflect包下面有一个InvocationHandler接口和动态代理类Proxy,用来实现jdk的动态代理。

示例代码

  • 接口
public interface UserService {
    void sleep();
    void eat();
}
  • 实现类
public class UserServiceImpl implements UserService {
    @Override
    public void sleep() {
        System.out.println("sleep");
    }
    @Override
    public void eat() {
        System.out.println("eat");
    }
}
  • 处理器类
public class InvocationHandlerImpl implements InvocationHandler {

    /**
     * 要代理的真实对象
     */
    private Object target;

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

    /**
     *
     * 该方法负责处理动态代理类上的所有方法调用
     * 调用处理器根据这三个参数进行预处理或分派到委托实例上反射执行
     * @param proxy 代理类实例
     * @param method 被调用的方法
     * @param args 调用参数
     * @return 返回值
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法"+ method.getName() +"调用前");
        Object returnValue = method.invoke(target, args);
        System.out.println("方法"+ method.getName() +"调用后");
        return returnValue;
    }
}
  • 调用者
public class Demo {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        InvocationHandler invocationHandler = new InvocationHandlerImpl(userService);
        UserService userService1 = (UserService)Proxy
                .newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
        /*
         * newProxyInstance 返回一个$Proxy0类,该类继承了Proxy类,实现了UserService接口
         * 实现了Userservice接口的方法,是调用了InvocationHandler的invoke方法
         * 并且把 UserServiceImpl 的Method穿个InvocationHandler的invoke方法
         */
        userService1.sleep();
        userService1.eat();
    }
}

InvocationHandler

InvocationHandler是一个接口,该接口中只有一个方法invoke。在该方法内可以实现一些代理类需要做的事情,比如日志处理,权限判断等等。

public interface InvocationHandler {
    
    /**
     *
     * @param proxy 代理类
     * @param method 被代理类的方法
     * @param args 被代理类的方法的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

Proxy

Porxy

Proxy是动态代理类,负责代码在执行过程中通过调用newProxyInstance动态生成一个代理对象。这个代理对象继承了Proxy类并且和被代理类实现了同样的接口。

/**
  *
  * 删掉一些异常处理等代码
  *
  * @param var0 被代理类的类加载器
  * @param var1 被代理类的方法集合
  * @param var2 InvocationHandler接口的实现类
  */
public static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2)  {
        Objects.requireNonNull(var2);
        Class[] var3 = (Class[])var1.clone();
        // 生成代理类的Class
        Class var5 = getProxyClass0(var0, var3);
				
  		// 获取代理类的构造方法
        final Constructor var6 = var5.getConstructor(constructorParams);
        if (!Modifier.isPublic(var5.getModifiers())) {
          AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
              var6.setAccessible(true);
              return null;
            }
          });
        }
  		// 反射生成代理类对象
        return var6.newInstance(var2);
    }

$Proxy

ProxynewProxyInstance方法会生成一个$Proxy类,通过这个类再生成代理对象。在调用newProxyInstance方法之前调用方法System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")就会在根目录下生成$Proxy类。(不同版本的jdk调用的方法不一样,具体可以看ProxyGenerator类里面的saveGeneratedFiles字段后面的字符串)

$Proxy继承了Proxy类并且和被代理类实现了同样的接口。$Proxy有一个构造方法,只有一个参数就是InvocationHandler接口,还有一些Method字段,一般前三个是不变的主要是toStringequalshashCode后面的Method是实现的接口的Method

$Proxy中实现的代理接口的方法,并且调用InvocationHandlerinvoke方法,将被代理的一些信息传给invoke方法。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.lee.study.aop.dynamic.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

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

    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 void eat() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.lee.study.aop.dynamic.UserService").getMethod("eat");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.lee.study.aop.dynamic.UserService").getMethod("sleep");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

动态代理流程

在这里插入图片描述
1.首先初始化UserService,然后将初始化的Userservice作为参数传给InvocationHandler的构造方法

2.初始化InvocationHandler,并且将初始化了的InvocationHandler作为参数传递给$Proxy的构造方法

3.调用调用$Proxyeat方法

4.在$Proyxeat方法里面掉用了InvocationHandlerinvoke方法

5.在InvocationHandlerinvoke 方法里面通过反射调用了真正的eat方法,并且可以在invoke方法内调用eat方法前后做一些业务相关的操作。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值