Java中静态代理和动态代理介绍和使用

前言

在Java中,代理模式是一种常用的设计模式,用于为其他对象提供一种代理以控制对这个对象的访问。代理模式主要有两种实现方式:静态代理和动态代理。

一、静态代理

静态代理是由程序员手动创建或指定代理类,代理类在程序运行前就已经存在。静态代理通常用于业务层之间的解耦,使得委托类(被代理的类)和代理类之间不存在直接的依赖关系,而是通过共同的接口进行交互。

优点

  • 可以在不修改真实对象的前提下,增加额外的功能。
  • 灵活性高,可以根据需要创建多个代理类。

缺点

  • 代理类需要手动编写,增加了代码的复杂性。
  • 如果接口增加方法,所有实现接口的类都需要进行修改。

示例

假设有一个接口Subject和两个实现类RealSubject(真实对象)和ProxySubject(代理对象)。

Subject接口:

package edu.etime.proxy.demo;

/**
 * @Date 2024/7/21 9:51
 * @Author liukang
 **/
public interface Subject {
    void request();
}

RealSubject(真实对象)

package edu.etime.proxy.demo;

/**
 * @Date 2024/7/21 9:52
 * @Author liukang
 **/
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("处理请求");
    }
}

 ProxySubject(代理对象):

package edu.etime.proxy.demo;

/**
 * @Date 2024/7/21 9:52
 * @Author liukang
 **/
public class ProxySubject implements Subject {
    private RealSubject realSubject;

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

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

    @Override
    public void request() {
        preRequest();
        realSubject.request();
        postRequest();
    }

    private void preRequest() {
        System.out.println("请求前的处理");
    }

    private void postRequest() {
        System.out.println("请求后的处理");
    }
}

测试调用:

package edu.etime.proxy.demo;

/**
 * @Date 2024/7/21 9:54
 * @Author liukang
 **/
public class MyTest {
    public static void main(String[] args) {
        // 创建代理对象
        ProxySubject proxySubject = new ProxySubject();
        // 调用代理对象中的request方法
        proxySubject.request();
    }
}

二、动态代理 

动态代理是在运行时动态生成代理类。动态代理主要依赖于Java的反射机制。Java提供了两种动态代理的实现方式:基于接口的JDK动态代理和基于类的CGLIB动态代理。

1.JDK动态代理

JDK动态代理

JDK动态代理是面向接口的代理模式,它要求被代理对象必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

优点

  • 动态生成代理类,减少代码量。
  • 代理类实现简单,只需实现InvocationHandler接口。

缺点

  • 只能代理实现了接口的类。

示例

Subject接口:

package edu.etime.proxy.demo2;

/**
 * @Date 2024/7/21 9:51
 * @Author liukang
 **/
public interface Subject {
    void request();
}

RealSubject(真实对象)

package edu.etime.proxy.demo2;

/**
 * @Date 2024/7/21 10:09
 * @Author liukang
 **/
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("处理请求");
    }
}

DynamicProxyHandler(代理对象):

package edu.etime.proxy.demo2;

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

/**
 * @Date 2024/7/21 10:01
 * @Author liukang
 **/
public class DynamicProxyHandler implements InvocationHandler {
    private Object target;

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

    // 实现InvocationHandler接口重写的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /*方法参数解释
        * Object proxy:代理的对象
        * Method method:要运行的方法
        * Object[] args:调用方法时传递的参数
        * */
        System.out.println("请求前的处理");
        Object result = method.invoke(target, args);
        System.out.println("请求后的处理");
        return result;
    }

    // 创建代理对象的方法
    public static <T> T getProxyInstance(T target, Class<T> interfaceClass) {
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),// 指定类加载器 此处就使用传入的接口的类加载器,也可以使用其他的
                new Class<?>[]{interfaceClass},// 指定接口,这些接口用于指定生成的代理长什么样子,也就是有哪些方法
                new DynamicProxyHandler(target)// 动态代理对象 DynamicProxyHandler里面重写invoke方法,添加逻辑,用于指定生成的代理对象要干什么事情
        );
    }
}

测试调用:

package edu.etime.proxy.demo2;



/**
 * @Date 2024/7/21 10:06
 * @Author liukang
 **/
public class MyTest {
    public static void main(String[] args) {
        // 使用
        Subject subject = (Subject) DynamicProxyHandler.getProxyInstance(new RealSubject(), Subject.class);
        subject.request();
    }
}

 

 

2.CGLIB动态代理

CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现接口。CGLIB通过继承被代理类来创建代理对象,因此它不要求被代理类实现接口。

优点

  • 可以代理没有实现接口的类。

缺点

  • 性能上比JDK动态代理稍差。
  • 使用复杂度较高。

CGLIB动态代理的关键类:

  • Enhancer:CGLIB中最常用的类,用于创建代理对象。它允许你设置被代理类、回调接口等。
  • MethodInterceptor:这是CGLIB中用于拦截方法调用的接口。你需要实现这个接口,并在其intercept方法中编写你的代理逻辑。

CGLIB动态代理的关键步骤:

  1. 创建MethodInterceptor实现类:在这个类中,你编写方法拦截逻辑。
  2. 使用Enhancer设置被代理类和回调:配置Enhancer实例,指定被代理的类和MethodInterceptor实现类。
  3. 创建代理对象:调用Enhancer的create()方法创建代理对象。
  4. 通过代理对象调用方法:代理对象会拦截所有调用,并将它们转发到你的MethodInterceptor实现类。

示例

RealSubject(真实对象)

package com.etime.proxy;

/**
 * @Date 2024/7/21 10:32
 * @Author liukang
 **/
public class RealSubject {
    public void request() {
        System.out.println("处理请求");
    }
}

CglibProxy(代理对象):

package com.etime.proxy;

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

import java.lang.reflect.Method;

/**
 * @Date 2024/7/21 10:32
 * @Author liukang
 **/
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用父类(即被代理类)中的方法
        System.out.println("After method " + method.getName());
        return result;
    }

    public static <T> T getProxyInstance(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz); // 设置被代理类
        enhancer.setCallback(new CglibProxy()); // 设置回调
        return (T) enhancer.create(); // 创建代理对象
    }
}

测试调用:

package com.etime.proxy;

/**
 * @Date 2024/7/21 10:39
 * @Author liukang
 **/
public class MyTest {
    public static void main(String[] args) {
        RealSubject realSubject = CglibProxy.getProxyInstance(RealSubject.class);
        realSubject.request(); // 这将调用代理对象的request方法,该方法会拦截并增强原始方法
    }
}

注意:cglib动态代理需要导入对应的依赖或者jar包

maven依赖:

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

在这个示例中,RealSubject是一个普通的Java类,没有实现任何接口。我们使用CGLIB的Enhancer类来创建RealSubject的代理对象。CglibProxy类实现了MethodInterceptor接口,并在其intercept方法中编写了代理逻辑。当通过代理对象调用request方法时,intercept方法会被调用,并且我们可以在调用原始方法之前和之后执行额外的逻辑。

注意:由于CGLIB是通过继承来创建代理对象的,因此它不能代理final类,也不能代理final方法,因为final类和方法不能被继承。此外,由于CGLIB依赖于Java的反射机制来生成代理类,因此在性能上可能略逊于JDK动态代理(特别是在处理大量代理对象时)。然而,对于没有实现接口的类,CGLIB是一个很好的选择。

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值