java的静态代理和动态代理

概念

代理我最先接触这个概念应该是设计模式,但是之前没怎么在意,了解的也不深,后来想把spring事务这块研究下又涉及到了spring aop,然后又对spring aop有了新的认识,aop的原理就是动态代理,想要了解动态代理就要了解静态代理。

这件事也使我认识一些道理:

  • 学习是不可以偷懒的,没学的迟早要补上。
  • 想要了解框架就要了解它背后的原理。

代理顾名思义就是代替执行任务,java中代理的实现是代理类和被代理类同时实现同一接口,这样在直接使用被代理类的地方可以使用代理类来代替。代理是一种设计模式,这种设计模式spring aop里面也用到了,我们能在切点之前执行一些操作,切点之后执行一些操作。执行的操作一般不是核心操作,如一些消息、日志等。

静态代理

接口

package test;

public interface Person {
    //上交班费
    void giveMoney();
}

被代理类

package test;

public class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void giveMoney() {
        System.out.println(name + "交了50元班费");
    }
}

代理类

package test;

public class StudentMonitor implements Person{
    private Student student;

    public StudentMonitor(Student student){
        this.student=student;
    }

    @Override
    public void giveMoney() {
        System.out.println("我是班长负责统一收缴班费");
        student.giveMoney();
        System.out.println("该同学近期表现良好");
    }
}

大家都看到静态代理很不方便,想要搞代理每个被代理类都要新建个代理类,维护起来很不方便。

那么动态代理就来了。

动态代理

动态代理也是逐步的发展,jdk自带的jdk代理只是解决了每次都要新建代理类的麻烦,但是被代理类还是要实现某个接口。想要更加灵活可以使用CGLIB,spring会根据是否实现了接口动态切换动态代理实现方式。

下面给出例子,使用一个通用的代理类来代理上面的StudentMonitor。

package test;

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

public class StudentInvocationHandler<T> implements InvocationHandler {
    T target;

    public StudentInvocationHandler(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" + method.getName() + "方法");
        Object result = method.invoke(target, args);
        return result;
    }
}

具体执行操作

package test;

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

public class ProxyTest {
    public static void main(String[] args) {
        //创建一个实例对象,这个对象是被代理的对象
        Person zhangsan = new Student("张三");

        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StudentInvocationHandler<Person>(zhangsan);

        //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

        //代理执行上交班费的方法
        stuProxy.giveMoney();
    }
}

大家都发现jdk动态代理的实现有两步关键步骤:实现InvocationHandler接口和通过Proxy类来在运行时动态生成代理类并执行。下面我们将重点研究这个两个步骤。

jdk动态代理核心

Interface InvocationHandler

InvocationHandler是由一个代理实例的调用处理器实现。
每一个代理实例有一个相关联的调用处理器。当一个方法在代理实例被调用时,是被调用处理器的反射方法编码和分派。

也就是说被代理类执行的方法是被代理实例的调用处理器在调用时动态生成的,利用了反射机制。

这个接口就一个方法Object invoke(Object proxy, Method method, Object[] args)

方法描述
该方法在一个代理实例上处理一个方法调用并且返回结果。当一个方法是在一个被关联该方法的代理实例上被反射,这个方法是在一个调用处理器上被调用。

方法参数
proxy:该方法被调用的代理实例,就是代理实例。
method:被代理需要执行的方法
args:被代理需要执行方法的参数

Class Proxy

这里主要先了解这个类是干嘛的,和newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法。

Proxy提供静态方法来创造动态代理类和实例并且它也是所有动态实体类的超类。如下:这个是临时的,想要生成文件需要在生成代理实例的前面加

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

cglib临时为文件生成开启

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import test.Person;

public final class $Proxy0 extends Proxy implements Person {
    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 boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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 giveMoney() 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)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("test.Person").getMethod("giveMoney", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法

最终生成实例是需要临时生成$Proxy0.class,然后继承Proxy实现了被代理的接口(Person)
描述

返回指定接口的代理类,给指定调用处理器分派方法调用。

参数

loader:类载入器去定义代理方法
interfaces:接口列表给代理类去实现
h:分派方法调用的调用处理器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值