Java动态代理、cglib动态代理

简介: 说动态代理,需要先清楚静态代理。所谓静态代理就是程序员提前实现好的代理类,编译后class文件是已经存在的。 实现原理,利用Java代理模式,由一个代理类持有委托类的实例,并实现委托类一样的接口,来实现增强方法的目的。 我们主要用它来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方

说动态代理,需要先清楚静态代理。所谓静态代理就是程序员提前实现好的代理类,编译后class文件是已经存在的。
实现原理,利用Java代理模式,由一个代理类持有委托类的实例,并实现委托类一样的接口,来实现增强方法的目的。

我们主要用它来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情,甚至根本不去执行这个方法。因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象。比如可以添加调用日志,做事务控制,对方法进行缓存等。

Spring容器代替工厂,Spring AOP代替JDK动态代理,让面向切面编程更容易实现。在Spring的帮助下轻松添加,移除动态代理,且对源代码无任何影响。

本文给出静态代理、JDK动态代理、CGLIB动态代理的三种例子。

一、静态代理
在了解代理模式的情况下看下面的代码,没什么可说的。

package com.shanhy.demo.proxy;

public interface Account {

    public void queryAccount();
    
    public void updateAccount();
    
}
package com.shanhy.demo.proxy;

public class AccountImpl implements Account {

    @Override
    public void queryAccount() {
        System.out.println("查看账户");
    }

    @Override
    public void updateAccount() {
        System.out.println("修改账户");
    }

}
package com.shanhy.demo.proxy;

public class AccountProxy implements Account {
    
    private Account account;
    
    public AccountProxy(Account account) {
        super();
        this.account = account;
    }

    @Override
    public void queryAccount() {
        System.out.println("代理before");
        account.queryAccount();
        System.out.println("代理after");
    }

    @Override
    public void updateAccount() {
        System.out.println("代理before");
        account.updateAccount();
        System.out.println("代理after");
    }

}
package com.shanhy.demo.proxy;

public class AccountProxyTest {

    public static void main(String[] args) {
        // AccountProxy为自己实现的代理类,可以发现,一个代理类只能为一个接口服务。
        Account account = new AccountImpl(); 
        AccountProxy proxy = new AccountProxy(account);
        proxy.queryAccount();
        proxy.updateAccount();
    }
}

二、JDK动态代理
使用JDK动态代理使用到一个Proxy类和一个InvocationHandler接口。
Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它仅支持interface代理(也就是代理类必须实现接口),因为它的设计注定了这个遗憾。

package com.shanhy.demo.proxy;

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

public class AccountProxyFactory implements InvocationHandler {
    
    private Object target;
    
    public Object bind(Object target){
        // 这里使用的是Jdk的动态代理,其必须要绑定接口,在我们的业务实现中有可能是没有基于接口是实现的。所以说这个缺陷cglib弥补了。
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        System.out.println(Proxy.isProxyClass(proxy.getClass()));
        boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");
        
        Object result = null;
        if(!objFlag)
            System.out.println("代理before");
        result = method.invoke(this.target, args);
        if(!objFlag)
            System.out.println("代理after");
        return result;
    }

    
}
package com.shanhy.demo.proxy;

public class AccountProxyTest {

    public static void main(String[] args) {
        // 下面使用JDK的代理类,一个代理就可以代理很多接口
        Account account1 = (Account)new AccountProxyFactory().bind(new AccountImpl());
        System.out.println(account1);
        account1.queryAccount();
}

三、CGLIB动态代理
对于上面说到JDK仅支持对实现接口的委托类进行代理的缺陷,这个问题CGLIB给予了很好的补位,解决了这个问题,使其委托类也可是非接口实现类。
CGLIB内部使用到ASM,所以我们下面的例子需要引入asm-3.3.jar、cglib-2.2.2.jar

package com.shanhy.demo.proxy;

import java.lang.reflect.Method;

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

public class AccountCglibProxyFactory implements MethodInterceptor{
    
    private Object target;
    
    public Object getInstance(Object target){
        this.target = target;
        return Enhancer.create(this.target.getClass(), this);
        
//        Enhancer enhancer = new Enhancer();//该类用于生成代理对象
//        enhancer.setSuperclass(this.target.getClass());//设置父类
//        enhancer.setCallback(this);//设置回调用对象为本身
//        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 排除Object类中的toString等方法
        boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object");
        if(!objFlag){
            System.out.println("before");
        }
        Object result = null;
//        我们一般使用proxy.invokeSuper(obj,args)方法。这个很好理解,就是执行原始类的方法。还有一个方法proxy.invoke(obj,args),这是执行生成子类的方法。
//        如果传入的obj就是子类的话,会发生内存溢出,因为子类的方法不挺地进入intercept方法,而这个方法又去调用子类的方法,两个方法直接循环调用了。
        result = methodProxy.invokeSuper(obj, args);
//        result = methodProxy.invoke(obj, args);
        if(!objFlag){
            System.out.println("after");
        }
        return result;
    }

}
package com.shanhy.demo.proxy;

public class Person {

    public void show(){
        System.out.println("showing");
    }
}
package com.shanhy.demo.proxy;

public class AccountProxyTest {

    public static void main(String[] args) {
        // 下面是用cglib的代理
        // 1.支持实现接口的类
        Account account2 = (Account)new AccountCglibProxyFactory().getInstance(new AccountImpl());
        account2.updateAccount();
        
        // 2.支持未实现接口的类
        Person person = (Person)new AccountCglibProxyFactory().getInstance(new Person());
        System.out.println(person);
        person.show();
    }
}
目录
相关文章
|
9月前
|
缓存 监控 Java
java动态代理
本文介绍了Java中的动态代理及其优势,通过增强原有方法或拦截调用实现无侵入式代码扩展,如添加日志、缓存等。文章先讲解了静态代理的基本概念和实现方式,随后引出动态代理解决静态代理在多方法、多类场景下的局限性。通过JDK提供的InvocationHandler接口和Proxy类,展示了如何动态生成代理对象。最后,文章还探讨了代理Hook技术,包括寻找Hook点、选择代理方式以及替换原始对象的具体步骤。
272 0
|
11月前
|
Dubbo Java 应用服务中间件
Java的动态代理
Java动态代理是一种强大的机制,允许在运行时创建接口的代理实例,并拦截方法调用。其核心组件包括`java.lang.reflect.Proxy`和`java.lang.reflect.InvocationHandler`。通过自定义接口、实现接口、编写`InvocationHandler`处理器并生成代理对象,可以灵活地增强方法功能,如日志记录、事务管理等。典型应用场景包括AOP、RPC、延迟加载和Mock测试。与CGLIB相比,JDK动态代理基于接口,性能稍慢但无需第三方库,适用于需要无侵入式增强的场合。
298 2
|
11月前
|
Java API 数据安全/隐私保护
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
387 0
探索Java动态代理的奥秘:JDK vs CGLIB
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
153 0
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
137 0
|
Java
深入理解Java动态代理
深入理解Java动态代理
161 1
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
1236 0
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
686 0
|
Java 数据库连接
Java之jdk和CGLib实现动态代理
Java之jdk和CGLib实现动态代理
180 0