深入理解 Java 代理:静态代理与动态代理剖析

在 Java 编程领域,代理模式是一种常见且重要的设计模式。它通过引入代理对象,实现对目标对象的访问控制和功能增强。Java 中的代理主要分为静态代理和动态代理,它们在实现方式和应用场景上各有特点。本文将深入探讨 Java 代理的相关知识,重点分析静态代理与动态代理的区别,并详细讲解动态代理的原理与实现。

一、代理模式概述

代理模式的核心思想是为目标对象提供一个代理对象,由代理对象控制对目标对象的引用。其主要目的有两个:一是控制访问,通过代理对象间接访问目标对象,避免直接访问目标对象给系统带来不必要的复杂性;二是功能增强,借助代理对象对原有业务进行功能扩展。

例如,在一个转账业务场景中,目标对象负责具体的转账操作,而代理对象可以在转账前后增加日志记录、权限验证等功能,从而对转账业务进行增强。

二、静态代理

(一)静态代理的实现方式

静态代理通常需要定义一个目标类的接口,目标类和代理类都实现该接口。代理类持有目标类的引用,在代理类的方法中调用目标类的相应方法,并可以在调用前后添加额外的逻辑。

以下是一个简单的示例:

java

// 目标类接口
interface TransAction {
    void transfer();
}

// 目标类
class TransActionImpl implements TransAction {
    @Override
    public void transfer() {
        System.out.println("执行转账操作");
    }
}

// 代理类
class TransActionProxy implements TransAction {
    private TransAction target;

    public TransActionProxy(TransAction target) {
        this.target = target;
    }

    @Override
    public void transfer() {
        System.out.println("权限验证");
        target.transfer();
        System.out.println("记录转账日志");
    }
}

在上述代码中,TransAction是目标类接口,TransActionImpl是目标类,实现了转账操作。TransActionProxy是代理类,持有目标类的引用,并在transfer方法中添加了权限验证和日志记录的功能。

(二)静态代理的缺点

静态代理虽然实现简单,但存在明显的缺点。当目标类增多时,需要为每个目标类创建对应的代理类,这会导致代理类数量急剧增加,造成代理关系混乱,代码维护成本升高。

三、动态代理

(一)动态代理的概念与优势

动态代理是在运行时动态地生成代理类,而不是像静态代理那样在编译时就确定代理类。它可以更灵活地处理多个目标类,避免了静态代理中代理类过多的问题。

(二)JDK 动态代理的实现

JDK 提供了动态代理的支持,主要涉及java.lang.reflect.Proxy类和InvocationHandler接口。

以下是一个使用 JDK 动态代理的示例:

java

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

// 目标类接口
interface Service {
    void execute();
}

// 目标类
class ServiceImpl implements Service {
    @Override
    public void execute() {
        System.out.println("执行服务操作");
    }
}

// 调用处理器
class MyInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置处理");
        Object result = method.invoke(target, args);
        System.out.println("后置处理");
        return result;
    }
}

public class DynamicProxyExample {
    public static void main(String[] args) {
        Service target = new ServiceImpl();
        MyInvocationHandler handler = new MyInvocationHandler(target);
        Service proxy = (Service) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler
        );
        proxy.execute();
    }
}

在这个示例中:

  1. 定义了Service接口和实现该接口的ServiceImpl目标类。
  2. 创建了MyInvocationHandler类实现InvocationHandler接口,在invoke方法中添加了前置处理和后置处理的逻辑,并通过method.invoke方法调用目标对象的方法。
  3. 使用Proxy.newProxyInstance方法动态生成代理对象,该方法接受三个参数:目标对象的类加载器、目标对象实现的接口数组以及调用处理器。

(三)CGLIB 动态代理

除了 JDK 动态代理,CGLIB 也是一种常用的动态代理实现方式。CGLIB 通过继承目标类来生成代理类,因此不需要目标类实现接口。

以下是一个简单的 CGLIB 动态代理示例:

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

import java.lang.reflect.Method;

class TargetClass {
    public void operation() {
        System.out.println("执行目标操作");
    }
}

class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB前置处理");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("CGLIB后置处理");
        return result;
    }
}

public class CglibProxyExample {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TargetClass.class);
        enhancer.setCallback(new MyMethodInterceptor());
        TargetClass proxy = (TargetClass) enhancer.create();
        proxy.operation();
    }
}

在 CGLIB 动态代理中,通过Enhancer类设置目标类和回调函数(实现MethodInterceptor接口),然后生成代理对象。

(四)JDK 动态代理与 CGLIB 动态代理的比较

  • JDK 动态代理:要求目标对象实现接口,代理类实现相同接口,通过反射机制调用目标方法,适用于有接口的情况。
  • CGLIB 动态代理:通过继承目标类生成代理类,无需目标类实现接口,性能相对较好,但无法代理final类和final方法。

四、静态代理与动态代理的区别

(一)实现方式

静态代理在编译时就确定了代理类,代理类与目标类实现相同的接口;动态代理则在运行时动态生成代理类,JDK 动态代理基于接口实现,CGLIB 动态代理基于类的继承。

(二)灵活性

静态代理的代理类是固定的,每个目标类都需要一个对应的代理类;动态代理更加灵活,一个代理类可以处理多个目标类,适用于目标类数量较多或变化频繁的场景。

(三)代码维护

静态代理随着目标类的增加,代理类也会增多,导致代码维护困难;动态代理通过统一的代理生成逻辑,减少了代码量,便于维护。

五、代理模式的应用场景

(一)AOP 编程

在面向切面编程(AOP)中,代理模式被广泛应用于实现横切关注点,如日志记录、事务管理、权限控制等。通过代理对象在目标方法调用前后插入相应的逻辑,实现功能的增强和分离。

(二)远程调用

在远程方法调用(RPC)中,代理模式可以用于创建远程对象的本地代理,隐藏远程调用的细节,使调用者感觉像是在调用本地对象。

(三)缓存代理

代理对象可以实现缓存功能,当目标对象的方法被频繁调用时,代理对象可以缓存方法的返回结果,避免重复调用目标对象,提高系统性能。

六、总结

Java 代理模式为我们提供了一种强大的编程手段,通过静态代理和动态代理的不同实现方式,可以满足不同场景下的需求。静态代理简单直观,适用于目标类较少的情况;动态代理则更加灵活,能够应对复杂多变的业务场景。深入理解代理模式的原理和应用,有助于我们编写出更加灵活、可维护和高性能的 Java 程序。在实际开发中,根据具体需求选择合适的代理方式,能够提升代码的质量和可扩展性。希望本文能帮助你更好地掌握 Java 代理的相关知识。如果你在学习和实践过程中有任何疑问,欢迎在评论区留言交流。

public interface ByClothes {

    public abstract void clothes(String size);

}
/**
 * 卖衣服的工厂
 *      目标类
 */
public class ClothesFactory implements ByClothes{

    public void clothes(String size){
        System.out.println("已经给您定制好了一件大小为"+size+"的衣服");
    }

    public void 机器处理(){

    }

    public void 原材料(){}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DyProxy implements InvocationHandler {

    //被代理的对象
    //代理对象如何得知自己代理的是哪个目标类
    //这里这样写其实是让用户告诉我,我要代理谁
    private Object o ;
    public DyProxy(Object o){
        this.o = o;
    }


    //2.获取目标类的接口,要知道自己代理的核心方法是啥?
    public Object getProxyInterFace(){
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(),this);
    }



    //知道了接口----》变相得知了自己要代理的核心方法:
    //invoke方法是利用反射的方式获取到了要代理的核心方法
    //1.Object:jdk创建的代理类,无需赋值
    //2.Method:目标类当中的方法,jdk提供,无需赋值
    //3.Object[]:目标类当中的方法的参数,jdk提供,无需赋值
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        FrontService();

        method.invoke(o,args);

        endService();
        return null;
    }

    //前置服务
    public void FrontService() {
        System.out.println("根据您的需求进行市场调研");
    }

    //前置服务
    public void endService() {
        System.out.println("为您提供一条龙的包办服务");
    }

}
public class TestProxy {
    public static void main(String[] args) {
         ClothesFactory clothesFactory = new ClothesFactory();
         ByClothes clothes = (ByClothes) new DyProxy(clothesFactory).getProxyInterFace();
         clothes.clothes("XXXL");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值