CGLib之Enhancer

article/2025/10/31 13:19:31

Enhancer允许为非接口类型创建一个Java代理。Enhancer动态创建了给定类型的子类但是拦截了所有的方法。和Proxy不一样的是,不管是接口还是类他都能正常工作。

来个场景模拟一下AOP

package cglib.enhancer;public class Hello {public String sayHello(boolean throwException) throws Exception {System.out.println("hello everyone!");if(throwException)throw new Exception("test exception");return "123";}
}

ProxyFactory用于创建增强代理实现了方法拦截

package cglib.enhancer;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 ProxyFactory implements MethodInterceptor {//要代理的原始对象private Object obj;public Object createProxy(Object target) {this.obj = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.obj.getClass());// 设置代理目标enhancer.setCallback(this);// 设置回调enhancer.setClassLoader(target.getClass().getClassLoader());return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {Object result = null;try {// 前置通知before();result = proxy.invokeSuper(obj, args);// 后置通知after();} catch (Exception e) {exception();}finally{beforeReturning();}return result;}private void before() {System.out.println("before method invoke");}private void after() {System.out.println("after method invoke");}private void exception() {System.out.println("method invoke exception");}private void beforeReturning() {System.out.println("before returning");}
}
场景类:

package cglib.enhancer;public class EnhancerTest {public static void main(String[] args) throws Exception {Hello hello = new Hello();ProxyFactory cglibProxy = new ProxyFactory();Hello proxy = (Hello) cglibProxy.createProxy(hello);String result=proxy.sayHello(true);System.out.println(result);}
}

运行结果:

before method invoke
hello everyone!
method invoke exception
before returning
null
当然把true改为false之后结果变为:

before method invoke
hello everyone!
after method invoke
before returning
123

接下来我们了解下 Enhancer可以使用的Callback接口。

package net.sf.cglib.proxy;/*** All callback interfaces used by {@link Enhancer} extend this interface.* @see MethodInterceptor* @see NoOp* @see LazyLoader* @see Dispatcher* @see InvocationHandler* @see FixedValue*/
public interface Callback
{
}
Callback接口是一个空接口,没有任何契约方法,它只表示这是一个回调。接下来看它的子接口:


接口都很简单。

先来一个基础POJO类:

public class SampleClass {public String test(String input) {return "Hello world!";}
}

FixedValue 

使用FixedValue可以很容易的替换掉方法的返回值。

@Test
public void testFixedValue() throws Exception {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new FixedValue() {@Overridepublic Object loadObject() throws Exception {return "Hello cglib!";}});SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null));
}
在上面的例子中, enhancer返回一个SampleClass的子类实例,当所有的方法被调用的时候返回匿名FixedValue回调返回的值。当然了如果方法返回的不是String类型例如,hashcode()将抛出一个类型转换异常,因为"Hello cglib!"不能转换为java.lang.Number类型。

InvocationHandler

@Test
public void testInvocationHandler() throws Exception {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {return "Hello cglib!";} else {throw new RuntimeException("Do not know what to do.");}}});SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null));assertNotEquals("Hello cglib!", proxy.toString());
}
这个回调允许你去回答调用的方法。但是你要小心使用这个回调,因为这个代理对象的所有的方法调用都会使用 InvocationHandler#invoke方法,这样的话可能导致死循环。为了避免这种情况你可以使用MethodInterceptor。

MethodInterceptor

@Test
public void testMethodInterceptor() throws Exception {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)throws Throwable {if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {return "Hello cglib!";} else {return proxy.invokeSuper(obj, args);}}});SampleClass proxy = (SampleClass) enhancer.create();assertEquals("Hello cglib!", proxy.test(null));assertNotEquals("Hello cglib!", proxy.toString());proxy.hashCode(); // Does not throw an exception or result in an endless loop.
}

唯一需要注意的就是proxy.invokeSuper和proxy.invoke的区别。invokeSuper是退出当前interceptor的处理,进入下一个callback处理,invoke则会继续回调该方法,如果传递给invoke的obj参数出错容易造成递归调用。

LazyLoader

尽管LazyLoader唯一的方法和FixedValue的一模一样,但是他们还是有一个根本上的区别。
相比于Dispatcher,lazyLoader在第一次获取了loadObject后,会进行缓存,后续的请求调用都会直接调用该缓存的属性。
这个是有道理的,如果你的对象是昂贵的在其创建,不知道对象什么时候被使用。请注意,一些增强的类的构造函数必须被代理对象和延迟加载的对象。因此,确保还有另一个廉价的(可能保护)构造函数获得或使用一个接口类型的代理。你可以选择提供参数的调用Enhancer#create(Object...)。

Dispatcher

DispatcherLazyLoader很想但是每次调用方法的时候不会存储加载的对象。这允许改变一个类的实现但不改变原来的对象。
再者说一些构造函数必须被代理和生成的对象调用。

ProxyRefDispatcher

这个类携带了代理对象的引用,通过它的签名调用。例如这允许委托方法调用代理的另一个方法。要清楚这很容易造成死循环,并且总是导致死循环如果同样的方法被ProxyRefDispatcher#loadObject(Object)调用。

NoOp

像名字所说的一样,这个类不做任何操作。相反地,它委托每个方法的调用给被增强类的方法实现。

http://chatgpt.dhexx.cn/article/Crq10upb.shtml

相关文章

cglib源码学习交流

背景 前段时间在工作中,包括一些代码阅读过程中,spring aop经常性的会看到cglib中的相关内容,包括BeanCopier,BulkBean,Enancher等内容,以前虽大致知道一些内容,原理是通过bytecode,但没具体深入代码研究&a…

CGLib浅析

CGLib浅析 什么是CGLib CGLIB实现动态代理,并不要求被代理类必须实现接口,底层采用asm字节码生成框架生成代理类字节码(该代理类继承了被代理类)。 所以被代理类一定不能定义为final class并且对于final 方法不能被代理。 实现需要 //MethodIntercept…

CGLIB

1、CGLIB 官网:http://cglib.sourceforge.net CGLIB是一个强大的高性能的代码生成包。它被许多AOP的框架(例如Spring AOP)使用,为他们提供方法的interception(拦截)。 Hibernate也使用CGLIB来代理单端si…

CGLIB代理到底是个什么东西?这是一篇最全的CGLIB大全

目录 0 概述 0.1 CGLIB包结构 1 使用CGLIB实现动态代理 1.1 CGLIB代理相关的类 1.2 CGLIB动态代理的基本原理 1.3 使用MethodInterceptor接口实现方法回调 1.3.1 实现MethodInterceptor接口 1.4 使用CGLIB代理最核心类Enhancer生成代理对象 1.5 使用CGLIB继进行动态代理…

什么是CGLIB,CGLIB使用简介

什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)…

cglib动态代理 | 如何生成代理类、代理类内容解析

文章目录 简介一、cglib动态代理有什么特点CgLib动态代理:优点:缺点: 二、Cglib如何生成代理类生成代理类的具体代码:生成代理对象的具体代码: 三、代理类内容解析持久化代理类:代理类内容: Cgl…

CGLib是什么

什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)…

什么是CGLIB,CGLIB使用简介,cglib

什么是CGLIB,CGLIB使用简介 2018年08月20日 10:41:31 axiaositong 阅读数:348 什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为…

(转帖)Cglib和jdk动态代理的区别及运行性能比较

动态代理解决了方法之间的紧耦合,IOC解决了类与类之间的紧耦合! Cglib和jdk动态代理的区别? 1、Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在…

CGLib动态代理原理

CGLib动态代理原理 CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法; 看一下CGLib的基本结构,下图所示,代理类去继承目标类,每次调用代理类的方…

CGLib介绍

1. CGLIB介绍 CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库, 它可以在运行期扩展Java类与实现Java接口。CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用&#…

cglib的简单使用

一、前言 最近在看Spring的源码,其中有牵扯到cglib的相关内容,遂简单记录下cglib是如何使用的 二、原理(节选自网络) CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。…

cglib动态代理

前面介绍了代理模式和JAVA动态代理,这片文章主要解析cglib动态代理实现。 基本介绍 CGLIB(Code Generation Library),是一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类…

动态代理之 cglib 实现

(尊重劳动成果,转载请注明出处:https://blog.csdn.net/qq_25827845/article/details/87513102冷血之心的博客) 目录 前言: 正文: AOP(面向切面编程) JDK动态代理 cglib实现动态…

CGLIB介绍与原理

一、什么是 CGLIB? CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个…

CGLIB(Code Generation Library)详解

什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)…

【动态代理】CGLIB 动态代理的使用及原理

1. CGLIB 动态代理介绍 什么是 CGLIB? CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。 通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更…

CGLIB详解(最详细)

转载地址:https://blog.csdn.net/danchu/article/details/70238002 什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架&#xff0c…

GPG error解决方案

问题: sudo apt-get update时报错GPG error 解决方案: // F42ED6FBAB17C654是根据你报错那一行确定的 sudo gpg --keyserver keyserver.ubuntu.com --recv F42ED6FBAB17C654 sudo gpg --export --armor F42ED6FBAB17C654 | sudo apt-key add -然后: sudo apt-get update

GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

阿里云CentOS 7.9 64位 搭建网站踩坑实录 问题1.GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql Failing package is: mysql-community-libs-compat-5.7.37-1.el7.x86_64 GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql …