动态代理详解(原理+代码+代码解析)

动态代理

1.什么是动态代理?

动态代理是一种在运行的时候动态的生成代理对象的技术。它在不改变原始类的情况下,对原始类的方法进行拦截或者增强。

2.动态代理可以实现的功能?

使用动态代理可以实现如下常用功能:
1.AOP(面向切面编程) :动态代理可以用于实现横切逻辑,例如日志记录,性能监控,事务管理等。通过在方法前后插入代理逻辑,可以实现对目标方法的增强。
2.远程方法调用(RPC) :动态代理可以用于远程方法调用的框架,例如使用代理对象作为客户端的远程服务代理,将调用转发给实际的远程服务,并且可以将客户端或服务器的业务逻辑进行增强。
3.动态权限校验:动态代理可以用于动态权限校验,例如在访问某个受限资源的时候(我们假设是个方法),使用这个方法的对应对象的代理对象来调用这个方法的时候,在调用方法前可以增加权限判断的逻辑,如果权限对,就可以进入。

3.动态代理和静态代理的区别?

动态代理和静态代理最大的区别就是:静态代理是编译器确定的代理类,动态代理是运行期确定的代理类。

也就是说:

静态代理其实就是事先写好代理类,可以手动编写也可以使用工具生成,但是它的缺点就是每个业务类都需要一个代理类,因此维护性差,很不灵活也很不方便。

动态代理就是在运行期间,动态的创建目标对象的代理对象。它可以在不修改原始类的情况下

因此,他们两个最终的效果是一样的,都是可以在不修改原始类的代码的情况下,对代理对象的方法进行增强。只不过生成时间不同,和方式不同,静态代理需要给每个类手动创建代理对象,麻烦点罢了。

4.静态代理代码

代码比较简单,不过多解释,在动态代理的代码会详细解释

java 复制代码
interface User{
    int age();
    void sayHi(String name);
}
class UserImpl implements User{
    @Override
    public int age() {
        return 18;
    }

    @Override
    public void sayHi(String name) {
        System.out.println("hello"+name);
    }
}
class UserProxy implements User{
    public User user;
    public UserProxy(User user){
        this.user=user;
    }

    @Override
    public int age() {
        return 18;
    }

    @Override
    public void sayHi(String name) {
        System.out.println("before");
        System.out.println("hello:"+name+",你的年龄是:"+user.age());
        System.out.println("after");
    }
}
public class Test10 {
    public static void main(String[] args) {
        User user=new UserImpl();
        UserProxy userProxy=new UserProxy(user);
        userProxy.sayHi("小白");
    }
}

5.静态代理代码

解释在代码后面,请耐心看完

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface School{
    void location();
}
class SchoolImpl implements School{

    @Override
    public void location() {
        System.out.println("学校的位置");
    }
}
class SchoolInvocationHandler implements InvocationHandler{
    public Object target;
    public SchoolInvocationHandler(Object target){
        this.target=target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result=method.invoke(target,args);
        System.out.println("after");
        return result;
    }
}
public class Test13 {
    public static void main(String[] args) {
        School school=new SchoolImpl();
        SchoolInvocationHandler handler=new SchoolInvocationHandler(school);
        School schoolProxy=(School) Proxy.newProxyInstance(School.class.getClassLoader(),
                new Class[]{School.class},handler);
        schoolProxy.location();
    }

}

首先,被代理的对象需要实现接口,如下图

为什么被代理对象要实现接口呢?

因为动态代理要求被代理对象实现接口主要是由Java动态代理机制的设计决定的。在Java中,动态代理是基于接口的,这意味着它使用接口来定义代理对象可以调用的方法集合。

然后,就创建一个调用器,这个所谓的调用器呢,你就可以当他是个工具,当代理对象去调用被代理对象的方法的时候,人家代理对象需要拿着这个调用器去调用方法,要不它调用不了,调用器的代码如下图,这个handler就是调用器

然后就创建代理对象,如下图红色圈,下图的绿色圈就是你需要被代理的类,这个类叫啥,你就把这里替换成相应的类名就行了

之后,当走了下图黄色圈的时候,也就是代理对象调用被代理类的方法的时候

这时候就相当于代理对象拿着调用器(handler),然后去找代理类,然后调用代理方法,然后走的是invoke,如下图,也就是说,当代理对象开始调用被代理类的方法的时候,就走调用器的invoke的方法

下面我来解释这三个参数

Object proxy:就代理对象自己,也就是schoolProxy,

Method method:是代理对象调用的方法,也就是location()方法

Object[] args:相当于location()方法里面的参数,我这个代码没有参数,如果有参数的话,比如location(int age,String name),就把这两个参数放到args这个数组,并且如果是基础数据类型,放到args这个数组就会变成包装类,也就是int变成Integer age

下面来解释下图红圈部分

method.invoke表示代理对象拿着调用器真正的去调用location方法了

target是被代理的对象

Object result代表的就是location()方法的返回值,如果没有返回值,那么object就是null

6.动态代理的底层

JDK动态代理的底层是通过java的反射机制实现的。

java的反射机制允许程序在运行时动态的获取类的信息(如成员变量,方法,构造函数等),并在运行时调用对象的方法或创建对象,如下源码所示

但是,动态代理除了可以依靠反射来实现之外,还可以使用字节码生成库来实现,字节码生成库允许我们在允许时生成字节码,从而创建代理对象,例如CGLib,它是一个开源的字节码生成库,它可以在运行时扩展java类并生成子类,从而实现动态代理的功能。

相关推荐
工业互联网专业15 分钟前
基于springboot+vue的高校社团管理系统的设计与实现
java·vue.js·spring boot·毕业设计·源码·课程设计
九圣残炎17 分钟前
【ElasticSearch】 Java API Client 7.17文档
java·elasticsearch·搜索引擎
随心Coding19 分钟前
【零基础入门Go语言】错误处理:如何更优雅地处理程序异常和错误
开发语言·后端·golang
m0_7482345220 分钟前
【Spring Boot】Spring AOP动态代理,以及静态代理
spring boot·后端·spring
m0_748251521 小时前
Ubuntu介绍、与centos的区别、基于VMware安装Ubuntu Server 22.04、配置远程连接、安装jdk+Tomcat
java·ubuntu·centos
咸甜适中1 小时前
go语言gui窗口应用之fyne框架-动态添加、删除一行控件(逐行注释)
开发语言·后端·golang
Bro_cat1 小时前
深入浅出JSON:数据交换的轻量级解决方案
java·ajax·java-ee·json
梁雨珈1 小时前
Groovy语言的安全开发
开发语言·后端·golang
等一场春雨2 小时前
Java设计模式 五 建造者模式 (Builder Pattern)
java·设计模式·建造者模式
hunzi_12 小时前
Java和PHP开发的商城系统区别
java·php