代理的三种方式

代理的三种方式(静态,动态和cglib)

静态代理

​ 静态代理是java中的一种设计模式,通过创建一个代理类来代替原始类,从而控制对原始类的访问.代理类和原始类之间都实现了相同的接口.使得客户端在使用时无需关心具体的实现细节.代理的概念已经解释了,那为什么叫做静态代理呢?是因为该代理在编译阶段就已经确定了代理类和原始类之间的关系.

静态代理的主要特点:

  1. 编译时确定:

    • 在静态代理中,代理类和原始类的关系是在编译时确定的。这意味着在程序运行之前,代理类的代码就已经生成,并且编译器检查了代理类和原始类之间的关系。
  2. 接口实现:

    • 静态代理通常要求代理类和原始类都实现相同的接口。这是为了确保代理类能够代替原始类,同时客户端代码无需关心具体的实现。
    1. 相同的接口:
      • 原始类(RealSubject)和代理类(ProxySubject)通常都会实现相同的接口,比如上面例子中的 Subject 接口。这确保了代理类具有与原始类相同的方法签名,即相同的公共接口。
    2. 代替原始类:
      • 由于代理类和原始类实现了相同的接口,客户端代码可以通过接口类型引用对象,而不需要关心具体是原始类还是代理类。客户端代码可以将对象视为实现了某个接口的对象,而不关心具体是哪个类的实例。
    3. 客户端无需关心具体的实现:
      • 客户端代码只需要与接口打交道,而不需要关心具体的实现类。客户端代码通过接口引用对象,这使得代理类和原始类可以在不影响客户端的情况下进行替换。这样的设计使得客户端代码更加灵活,可以轻松切换不同的实现类,而不需要修改客户端代码。
  3. 代理控制:

    • 通过代理类,可以在原始类的方法执行前后插入额外的逻辑,实现对原始类的访问控制和功能增强。这使得代理在某些情况下可以用于实现日志记录、性能监控、事务管理等功能。

在这里插入图片描述

​ 代码示例:

假设有一个接口 Subject

javaCopy codepublic interface Subject {
    void request();
}

和一个原始类 RealSubject

javaCopy codepublic class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

然后,创建一个代理类 ProxySubject

javaCopy codepublic class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        // 额外逻辑(在原始方法执行前后插入)
        System.out.println("ProxySubject: Before request.");
        realSubject.request();
        System.out.println("ProxySubject: After request.");
    }
}

在这个示例中,ProxySubjectSubject 接口的实现,通过包含一个 RealSubject 对象,它能够在原始方法执行前后插入额外的逻辑。

使用示例:

javaCopy codepublic class Client {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxy = new ProxySubject(realSubject);

        // 客户端无需关心具体的实现
        proxy.request();
    }
}

在这个例子中,客户端通过代理类 ProxySubject 访问原始类 RealSubject 的方法,而不直接调用原始类的方法。

动态代理

​ java动态代理是一种在运行时创建代理类的机制,它允许在不提前知道代理类的就提类型的情况下,动态的创建一个代理对象来代替原始类.与静态代理相较,动态代理更加灵活,可以代理任意的接口类型,不需要为每个被代理类编写专门的代理类,而是通过java的反射机制在运行时动态的生成代理类.动态代理类主要使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现.

​ 动态代理又称作jdk代理或者接口代理.Proxy 类用于创建动态代理类的实例,而 InvocationHandler 接口则负责处理代理类的方法调用。

  1. 在运行时创建代理类:
    • 动态代理机制允许在程序运行时创建代理类,而不是在编译时确定。这使得我们可以在不提前知道具体代理类型的情况下,动态地创建代理对象来代替原始类。
  2. 反射机制的应用:
    • 动态代理主要使用了Java的反射机制。具体而言,使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。Proxy 类用于创建动态代理类的实例,而 InvocationHandler 接口则负责处理代理类的方法调用。
  3. 代理任意接口类型:
    • 与静态代理相比,动态代理更加灵活,因为它可以代理任意的接口类型。在动态代理中,代理类实现了一个或多个接口,并且在运行时生成的代理类会实现这些接口的方法。
  4. 无需为每个被代理类编写专门的代理类:
    • 在动态代理中,我们不需要为每个具体的被代理类编写专门的代理类。相反,我们可以通过一个通用的代理类处理多个接口类型的代理。

在这里插入图片描述

代码示例:

假设有一个接口 Subject 和一个实现该接口的原始类 RealSubject

javaCopy codepublic interface Subject {
    void request();
}

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

使用动态代理创建代理对象:

javaCopy codeimport java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyExample {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                Subject.class.getClassLoader(),
                new Class[]{Subject.class},
                new MyInvocationHandler(realSubject)
        );

        // 通过代理对象调用方法
        proxySubject.request();
    }
}

class MyInvocationHandler implements InvocationHandler {
    private final Subject realSubject;

    public MyInvocationHandler(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 额外逻辑(在原始方法执行前后插入)
        System.out.println("Dynamic Proxy: Before request.");
        Object result = method.invoke(realSubject, args);
        System.out.println("Dynamic Proxy: After request.");
        return result;
    }
}

在这个例子中,Proxy.newProxyInstance 方法会在运行时动态地创建代理对象,代理对象会实现 Subject 接口,并且使用 MyInvocationHandler 处理方法调用。这种方式可以灵活地处理多个接口类型的代理。

cglib代理

​ cglib代理是一个第三方开源库,用户在java运行时生成字节码并创建代理类.与基于接口的java动态代理不同,cglib代理可以代理普通类(即使他们没有实现任何接口).cglib使用ASM库来生成字节码,并通过继承方式创建代理类 ,因此又被称作子类代理.

在这里插入图片描述

  1. 代理类的生成:
    • CGLIB通过继承目标类来创建代理类。它创建一个目标类的子类,并重写其中的方法,以在方法调用前后插入横切逻辑。
  2. 无需实现接口:
    • 与JDK动态代理不同,CGLIB代理不要求目标类实现接口。它可以代理没有实现接口的类。
  3. 性能:
    • CGLIB代理的性能通常比JDK动态代理稍差,因为它涉及到类的继承。但在实际应用中,通常不会明显影响性能。
  4. Final类和方法:
    • CGLIB无法代理final类和final方法,因为它无法重写这些final方法。
  5. 可见性:
    • CGLIB代理只能代理可见的非final类的可见的非final方法。

下面是一个简单的示例,演示了CGLIB代理的使用:

javaCopy codeimport net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBExample {
    public static void main(String[] args) {
        // 创建目标类的Enhancer
        Enhancer enhancer = new Enhancer();
        // 设置目标类的父类(被代理类)
        enhancer.setSuperclass(MyService.class);
        // 设置方法拦截器
        enhancer.setCallback(new MyMethodInterceptor());

        // 创建代理类
        MyService proxy = (MyService) enhancer.create();
        // 调用代理类的方法
        proxy.doSomething();
    }
}

class MyService {
    public void doSomething() {
        System.out.println("MyService: Doing something.");
    }
}

class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 横切逻辑(在原始方法执行前后插入)
        System.out.println("CGLIB Proxy: Before " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("CGLIB Proxy: After " + method.getName());
        return result;
    }
}

在这个例子中,MyService 是目标类,MyMethodInterceptor 是方法拦截器,使用CGLIB的Enhancer来创建代理类。生成的代理类继承了MyService,并在方法调用前后插入了横切逻辑。

java动态代理和cglib代理的区别

  1. jdk动态代理只能基于接口,代理生成的对象只能赋值给接口变量,而Cglib就不存在这个问题,Cglib是通过生成子类来实现的,代理对象既可以赋值给实现类,又可以赋值给接口。
  2. CgLib 创建的动态代理对象性能比 JDK 创建的动态代理对象的性能高不少,但是 CGLib 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib 合适,反之,使用 JDK 方式要更为合适一些。同时,由于 CGLib 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理.

文章参考:

搞懂Java三种代理模式:静态代理、动态代理和cglib代理 - 掘金 (juejin.cn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值