由反射引出的Java动态代理与静态代理

简介: 由反射引出的Java动态代理与静态代理

写在开头

《深入剖析Java中的反射,由浅入深,层层剥离!》这篇文章中我们讲反射时,曾提到过Java的动态代理中使用了反射技术,那么好,今天我们要就着反射的索引,来学习一下Java中的代理!

代理模式

在Java中有多达23种的设计模式(后面Java基础更新完后,会找个时间详细的去写写这些设计模式),恰当的设计模式的使用能够提升代码的效率,简化代码的复杂性。

而今天我们要说的代理模式就是其中之一,所谓代理是为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

大白话:买房的(客户方),房产销售(代理方),卖房的(委托方)

image.png

在Java中有静态代理和动态代理两种实现方式,继续放下看!!!

静态代理

所谓静态代理,一般是针对编译期就已经完成了接口,实现类,代理类的定义,我们对目标对象的增强需要手工去完成,一个目标对象就要有个代理类,非常不灵活。

静态代理的实现步骤

1,因为代理类与被目标对象有相似的行为(共同),所以我们先创建一个接口。

public interface SaleHouse {
   
   
     String saleHouse();
}

2,提供接口的实现类,当做目标对象

public class SaleHouseImpl implements SaleHouse{
   
   
    @Override
    public String saleHouse() {
   
   
        return "我要卖房子啦!!!";
    }
}

3,代理类同样也要实现接口,并在目标方法前后做一些控制操作

public class SaleHouseProxy implements SaleHouse{
   
   

    private SaleHouse saleHouse;
    //提供一个包含目标对象的有参构造
    public SaleHouseProxy(SaleHouse saleHouse) {
   
   
        this.saleHouse = saleHouse;
    }

    @Override
    public String saleHouse() {
   
   
        //调用方法前,我们可以加一些自己的控制
        System.out.println("我要收代理费!!!");
        System.out.println("--------------------");
        String s = saleHouse.saleHouse();
        System.out.println(s);
        System.out.println("--------------------");
        //调用方法后,我们依旧可以操作
        System.out.println("我要拿提成!!!");
        return "这就是静态代理";
    }
}

4,客户端调用代理类,并传入目标对象

public class Test {
   
   
    public static void main(String[] args) throws FileNotFoundException {
   
   
        //客户端调用静态代理
        SaleHouse saleHouse = new SaleHouseImpl();
        SaleHouseProxy saleHouseProxy = new SaleHouseProxy(saleHouse);
        saleHouseProxy.saleHouse();
    }
}

5,控制台查看打印结果

我要收代理费!!!
--------------------
我要卖房子啦!!!
--------------------
我要拿提成!!!

动态代理

其实无论是静态代理还是静态代理,在我们的日常开发中,使用的都是很多,但对于SpringAop、RPC等框架来说,动态代理发挥着相当大的作用,动态代理具有:运行时控制,灵活性更好的特点。
那怎么实现动态代理呢?
如下三种方式:

JDK 动态代理
CGLib 动态代理
使用 Spring aop 模块完成动态代理功能 //今天先不说这个

JDK动态代理

实现步骤:
1,定义一个接口及其实现类;
代码同静态代理中步骤1,步骤2;

2,自定义 InvocationHandler (调用处理器)并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;

public class JdkDynamicInvocationHandler implements InvocationHandler {
   
   

    //代理类中的真实对象
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
   
        //调用方法前,我们可以加一些自己的控制
        System.out.println("我要收代理费!!!");
        Object invoke = method.invoke(target, args);
        //调用方法后,我们依旧可以操作
        System.out.println("我要拿提成!!!");
        return invoke;
    }
}

3,通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;其实,这一步也可以写在第2步的代码里,不过为了代码的可读性,我们进行解耦实现!
3.1,定义一个工厂类,在工厂类中通过Proxy.newProxyInstance()方法获取某个类的代理对象

public class JdkDynamicProxyFactory {
   
   
    public static Object getProxy(Object target) {
   
   
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 目标类的类加载器
            target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
            new JdkDynamicInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
        );
    }

3.2,客户端传入目标对象,实现代理扩展

  //客户端调用静态代理
SaleHouse proxySaleHouse = (SaleHouse) JdkDynamicProxyFactory.getProxy(new SaleHouseImpl());
proxySaleHouse.saleHouse();

4,控制台输出

我要收代理费!!!
我要卖房子啦!!!
我要拿提成!!!

【扩展】
关于Proxy类的静态工厂方法newProxyInstance()如何创建代理实例的过程,感兴趣的可以去读源码。

CGLIB 动态代理

其实在JDK动态代理中有一个弊端,那就是只能代理接口或接口的实现类,那么未实现任何接口的类就不能代理了吗?答案是否定的,因为咱们有CGLIB!

CGLIB (Code Generation Library) 是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,CGLIB 通过继承方式实现代理。

实现步骤:
1,引入cglib依赖
因为是第三方实现的动态代理,所以在使用前先引入依赖包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

2,定义一个类;

public class Person {
   
   
    public void eat(){
   
   
        System.out.println("我在吃饭!!!");
    }
}

3,自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;

public class CglibMethodInterceptor implements MethodInterceptor {
   
   

    /**
     * @param o           被代理的对象(需要增强的对象)
     * @param method      被拦截的方法(需要增强的方法)
     * @param args        方法入参
     * @param methodProxy 用于调用原始方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   
   
        //调用方法前,我们可以加一些自己的控制
        System.out.println("饭前先洗手");
        Object object = methodProxy.invokeSuper(o, args);
        //调用方法前,我们可以加一些自己的控制
        System.out.println("饭后要擦嘴");
        return object;
    }
}

4,创建一个工厂类,用来构建代理对象,通过 Enhancer 类的 create()方法实现;

public class CglibProxyFactory {
   
   

    public static Object getProxy(Class<?> clazz) {
   
   
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new CglibMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}

5、客户端调用,通过反射传入Person类信息

public static void main(String[] args) throws FileNotFoundException {
   
   
        //客户端调用静态代理
        Person person = (Person) CglibProxyFactory.getProxy(Person.class);
        person.eat();
    }

6、输出

饭前先洗手
我在吃饭!!!
饭后要擦嘴

OK,终于码完了动态代理,自己还去看了很久的源码,头昏脑涨!

目录
相关文章
|
8月前
|
缓存 监控 Java
java动态代理
本文介绍了Java中的动态代理及其优势,通过增强原有方法或拦截调用实现无侵入式代码扩展,如添加日志、缓存等。文章先讲解了静态代理的基本概念和实现方式,随后引出动态代理解决静态代理在多方法、多类场景下的局限性。通过JDK提供的InvocationHandler接口和Proxy类,展示了如何动态生成代理对象。最后,文章还探讨了代理Hook技术,包括寻找Hook点、选择代理方式以及替换原始对象的具体步骤。
258 0
|
10月前
|
Dubbo Java 应用服务中间件
Java的动态代理
Java动态代理是一种强大的机制,允许在运行时创建接口的代理实例,并拦截方法调用。其核心组件包括`java.lang.reflect.Proxy`和`java.lang.reflect.InvocationHandler`。通过自定义接口、实现接口、编写`InvocationHandler`处理器并生成代理对象,可以灵活地增强方法功能,如日志记录、事务管理等。典型应用场景包括AOP、RPC、延迟加载和Mock测试。与CGLIB相比,JDK动态代理基于接口,性能稍慢但无需第三方库,适用于需要无侵入式增强的场合。
281 2
|
10月前
|
Java API 数据安全/隐私保护
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
361 0
探索Java动态代理的奥秘:JDK vs CGLIB
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
监控 Java
Java基础——反射
本文介绍了Java反射机制的基本概念和使用方法,包括`Class`类的使用、动态加载类、获取方法和成员变量信息、方法反射操作、以及通过反射了解集合泛型的本质。同时,文章还探讨了动态代理的概念及其应用,通过实例展示了如何利用动态代理实现面向切面编程(AOP),例如为方法执行添加性能监控。
170 5
|
Java
Java的反射
Java的反射。
139 2
|
Java
Java访问外网图片地址时,如何添加代理?
【10月更文挑战第14天】Java访问外网图片地址时,如何添加代理?
368 2
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
147 0
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
IDE Java 编译器
java的反射与注解
java的反射与注解
116 0