代理模式

代理模式指的就是使用现一个代理类来代理另一个目标类的行为,比如说我们一般在租房子的时候,不直接和房东进行交涉,而通过房屋中介来进行操作,因为他们手里可能有很多的房源,对于房东来说,就可以直接与房屋中介进行交流。类似的例子数不胜数,又比如好莱坞的明星,都有他们自己的经济人,当然这里指的可不是宝强的经济人,开个玩笑,他们的经济人负责明星的经济活动,这里指的就是代理模式!

代理模式分为:静态代理模式,动态代模式(jdk动态代理),cglib动态代理模式三种!

一、 静态代理模式:

我们这里就用租房子的例子来说明原理,

  • 类图如下 :

在这里插入图片描述
大概的说明一下:
有一个接口Host,里面有Rent租房的方法,然后让HostImpl房东来实现Host方法,这个就是目标类,真正要出租房子的房东,proxy为中介对象,它也实现Host方法,然后在类内部聚合了HostImpl对象,调用HostImpl,从而可以帮房东来出租房子,下面来看具体的代码实现:

  • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public interface Host {

    void Rent();

    }

    public class HostImpl implements Host {
    @Override
    public void Rent() {
    System.out.println("房东要租房子");
    }
    }

    public class proxy implements Host{

    //聚合HostImpl
    HostImpl host;

    public proxy(HostImpl host) {
    this.host = host;
    }

    @Override
    public void Rent() {
    System.out.println("代理开始工作");
    //调用HostImpl中的Rent方法
    host.Rent();
    }
    }
    public class Client {
    public static void main(String[] args) {
    proxy proxy = new proxy(new HostImpl());
    proxy.Rent();
    }
    }
  • 运行结果
    在这里插入图片描述
    静态代理:
    优点:在不修改目标对象的功能前提下,能通过代理对象目标功能扩展
    缺点:
    (1)因为代理对象需要 与目标对象实现一样的接口,所有会有很多代理类
    (2)一旦接口增加方法,目标对象与代理对象都要维护

    二、 动态代模式(jdk动态代理):

  • 基本介绍:*
    1)代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
    2)代理对象的生成是利用JDK的API,动态的在内存中构建代理对象
    3)动态代理也叫:JDK代理、接口代理
  • 类图如下:
    在这里插入图片描述
    getProxyInstacne():
    1.根据传入的对象(HostImpl),目标对象
    2.利用反射机制,返回一个代理对象
    3.然后通过代理对象,调用目标对象方法
  • 代码实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    public interface Host {

    void Rent();

    }

    public class HostImpl implements Host {
    @Override
    public void Rent() {
    System.out.println("房东要租房子");
    }
    }

    public class MyInvocationHadler implements InvocationHandler {

    private Object target;

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

    //执行目标方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("JDK代理");
    //利用反射机制调用目标对象的方法
    Object returnVal = method.invoke(target, args);
    System.out.println("JDK代理提交");
    return returnVal;
    }
    }

    public class ProxyFactory {

    private Object target;

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

    //给目标对象,生成一个代理对象
    public Object getProxyInstance(){

    /**
    * public static Object newProxyInstance(ClassLoader loader,
    * Class<?>[] interfaces,
    * InvocationHandler h)
    */
    //1.ClassLoader loader:指定当前目标对象使用的类加载器
    //2.Class<?>[] interfaces:目标对象实现的接口类型,使用泛型确认类型
    //3.InvocationHandler h):事件处理,执行目标对象的方法,会触发事件处理器方法
    return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new MyInvocationHadler(target));
    }
    }
  • 测试结果
    在这里插入图片描述
  • 通过Debug可以观察到
    在这里插入图片描述
  • 类图所示
    在这里插入图片描述

    三、 Cglib动态代理模式:

    基本介绍:

    1)静态代理和JDK动态代理都要求目标对象是实现一个接口,但有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可以使目标对象子类来实现代理,这就是Cglib代理。
    2)Cglib代理也叫子类代理,它是在内存中构建一个子类对象从而实现目标对象功能扩展,有时候也把Cglib代理归属到动态代理中。
    3)Cglib代理是一个强大的高性能的代码生成包,这可以在运行期间扩展java类与实现java接口,它广泛的被许多AOP的框架使用,例如:Spring AOP中实现方法拦截
    4)在AOP编程中如何选择代理模式:
    4.1)目标对象需要实现接口,就用JDK代理
    4.2)目标对象不需要实现接口,就用Cglib代理
    5)Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

    • Cglib使用的jar包:

      cglib-2.2.jar
      asm.jar
      commons.jar
      asm-tree.jar

没有jar包的小伙伴可以下载食用:
链接:https://pan.baidu.com/s/16XEWkAGapxnmT4Li4ocKDA
提取码:sa4k

  • 类图如下
    在这里插入图片描述
  • 代码实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public class HostImpl {

    public void Rent(){
    System.out.println("房东要租房子,cglib方式");
    }
    }
    public class ProxyFactory implements MethodInterceptor {
    //给一个目标对象
    private Object target;

    //构造器,用于初始化被代理的对象
    public ProxyFactory(Object target) {
    this.target = target;
    }
    //返回一个代理对象,是target对象的代理对象
    public Object getProxyInstance(){
    //1.创建一个工具类
    Enhancer enhancer = new Enhancer();
    //2.设置父类
    enhancer.setSuperclass(target.getClass());
    //3.设置回调函数
    enhancer.setCallback(this);
    //4.创建子类对象,即代理对象
    return enhancer.create();
    }

    //重写intercept方法,会调用目标对象的方法
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    System.out.println("cglib代理模式*******开始");
    Object returnVale = method.invoke(target, args);
    System.out.println("cglib代理模式*******提交");
    return returnVale;
    }
    }

    public class Client {
    public static void main(String[] args) {
    //目标对象
    HostImpl target = new HostImpl();
    //获得到代理对象,并且将目标对象传递给代理对象
    HostImpl proxyFactory = (HostImpl)new ProxyFactory(target).getProxyInstance();

    //执行代理对象的方法,触发intercept方法,从而实现对目标对象调用
    proxyFactory.Rent();
    }
    }
  • 运行结果
    在这里插入图片描述
  • 通过Debug可以观察到
    在这里插入图片描述
  • 类图形式
    在这里插入图片描述
    总结:
    cglib生成动态代理时,目标对象target和ProxyFactory 都不需要实现接口!