java静态代理和动态代理

本文详细介绍了静态代理和动态代理的概念及其应用场景。静态代理通过程序员手动编写代理类或使用工具生成,而动态代理则是在程序运行时通过反射机制动态创建。文章还对比了两者之间的优缺点,并展示了JDK和CGLIB动态代理的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

按照代理的创建时期,代理类可以分为两种:
静态代理:由程序员创建代理类或特定工具自动生成源代码,再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态代理:在程序运行时用反射机制动态创建。

静态代理

代理对象(Proxy)和目标对象(RealSubject)实现了相同的接口(Subject),目标对象作为代理对象的一个属性,具体接口实现中,代理对象可以在调用目标对象相应方法前后加上其他业务处理逻辑。
(1)Subject接口:

public interface Subject {

    public abstract void dealTask();

}

(2)RealSubject类:继承Subject接口,并实现其中的方法

public class RealSubject implements Subject {
    @Override
    public void dealTask() {
        System.out.println("我要吃饭");
    }
}

(3)代理类Proxy:继承Subject类,并把Subject作为参数通过构造参数传递。
Subject是个接口,因此在调用的时候传递的应该是他的实现类。

public class Proxy implements Subject {

    // 目标对象
    private Subject subject;

    // 通过构造方法传入目标对象
    public Proxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void dealTask() {
        System.out.println("代理来帮你做饭");

        subject.dealTask();

        System.out.println("代理来帮你刷碗");
    }

}

(4)Client客户端调用:

public class Client {
    public static void main(String[] args) {
        // Proxy类中构造方法传递的是一个接口,在调用的时候就必须传递这个接口的实现类,因此是RealSubject
        Subject subject = new Proxy(new RealSubject());

        subject.dealTask();
    }

}

控制台输出:

代理来帮你做饭
我要吃饭
代理来帮你刷碗


动态代理之JDK

为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。

代理模式:
这里写图片描述

(1)接口

public interface SubjectService {
    public void getName(String name);

    public void getAge(Integer age);
}

(2)实现类

public class SubjectServiceImpl implements SubjectService{
    @Override
    public void getName(String name) {
        System.out.println("getName方法的实现类:" + name);
    }

    @Override
    public void getAge(Integer age) {
        System.out.println("getage方法的实现类"+age);
    }
}

(3)代理类:继承InvocationHandler接口并重写invole方法,并把目标类作为参数传递进去

public class MyInvocationHandler implements InvocationHandler {
    // 目标方法
    private Object target;

    public MyInvocationHandler() {
    }

    // 把目标方法作为参数传递
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 是getName()方法
        if ("getName".equals(method.getName())) {
            System.out.println("before----" + method.getName());
            Object result = method.invoke(target, args);
            System.out.println("after----" + method.getName());
            return result;
            // 是getAge()方法
        } else if ("getAge".equals(method.getName())) {
            System.out.println("before----" + method.getName());
            Object result = method.invoke(target, args);
            System.out.println("after----" + method.getName());
            return result;
        } else {
            Object result = method.invoke(target, args);
            return result;
        }
    }
}

(4)客户端

public class Client {
    public static void main(String[] args) {
        //多态的方式
        SubjectService subjectService = new SubjectServiceImpl();

        //参数是接口,真正调用传递的就应该是实现类
        InvocationHandler invocationHandler = new MyInvocationHandler(subjectService);

        //创建代理类
        SubjectService serviceproxy = (SubjectService) Proxy.newProxyInstance(
                subjectService.getClass().getClassLoader(), subjectService.getClass().getInterfaces(),
                invocationHandler);

        serviceproxy.getName("小红");
        serviceproxy.getAge(20);
    }
}

控制台打印:

before----getName
getName方法的实现类:小红
after----getName
before----getAge
getage方法的实现类20
after----getAge


动态代理之CGLIB

cglib需要的jar包有asm.jar、cglib.jar
cglib,code generate library,代理类可对类进行代理,使用第三方cglib库来实现,其内部使用asm框架生成代理类的字节码,其字节码文件更加复杂,不能代理final方法,因为代理类是委托类的子类。

代理对象的生成过程由Enhancer类(CGLib的字节码增强器)实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。

(1)定义业务逻辑

public class Subject {
    public void say(String name) {
        System.out.println("hello " + name);
    }
}

(2)实现MethodInterceptor接口,定义方法的拦截器

public class MyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("before----------" + method.getName() + "()");

        // 调用MyInterceptor父类的方法,因为在Client类中设置了MyInterceptor的父类是Subject
        Object returnObj = methodProxy.invokeSuper(object, args);

        System.out.println("after----------" + method.getName() + "()");

        return returnObj;
    }
}

(3)使用Enhancer类生成代理类

public class Client {

    public static void main(String[] args) {
        // 利用Enhancer类生成代理类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Subject.class);              // 继承被代理类
        enhancer.setCallback(new MyInterceptor());          // 设置回调

        //采用多态的形式,因为上面通过setSuperclass设置了继承关系
        Subject subject = (Subject) enhancer.create();      // 生成代理对象
        subject.say("yes");
    }
}

控制台输出:

before----------say
hello yes
after----------say

静态代理和动态代理的比较

静态代理的优点:
(1)代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)

静态代理的缺点:
(1)一个代理类只能代理一个业务类。如果业务类增加方法时,相应的代理类也要增加方法。。
(2)如果接口增加一个方法,除了实现类需要实现这个方法外,代理类也需要实现此方法。增加了代码维护的复杂度。


jdk和cglib动态代理实现的区别
1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值