当前位置:网站首页>Cglib dynamic agent -- example / principle
Cglib dynamic agent -- example / principle
2022-07-06 00:54:00 【It blade out of sheath】
Original website :CGLIB A dynamic proxy -- example / principle _IT A blog with a sharp blade -CSDN Blog
brief introduction
explain
This article introduces with examples CGLIB Use of dynamic agents .
principle
CGLIB It's about classes that implement proxies .
The principle is to generate a subclass of the specified target class , And cover the methods to realize the enhancement , But because it's inheritance , So we can't be right final Decorated class to proxy .
Limit
JDK The dynamic proxy mechanism can only implement the class of the interface , Classes that cannot implement interfaces cannot implement JDK Dynamic proxy for .
example : Count the execution time
rely on
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
The lower version of cglib This dependency may be required :
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.0.1</version>
</dependency>
Code
package org.example.a;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// Surrogate class
class ExecutionTimeReality {
// Print out the task name here , And sleep 500ms The simulation task took a long time
public void dealTask(String taskName) {
System.out.println("Task is running: " + taskName);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Print out the task name here , And sleep 500ms The simulation task took a long time
public void sayHello(String name) {
System.out.println("hello: " + name);
}
}
// proxy class
class ExecutionTimeProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
long startTime = System.currentTimeMillis();
proxy.invokeSuper(obj, args);
long endTime = System.currentTimeMillis();
System.out.println("Elapsed time: " + (endTime - startTime) + " ms");
return null;
}
}
// Factory methods get proxy objects
class DynamicProxyFactory {
public static Object getInstance() {
Object target = new ExecutionTimeReality();
ExecutionTimeProxy proxySubject = new ExecutionTimeProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(proxySubject);
return enhancer.create();
}
}
public class Demo {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\CglibProxyClass");
ExecutionTimeReality subject = (ExecutionTimeReality) DynamicProxyFactory.getInstance();
subject.dealTask("TestTask");
}
}
Execution results
Task is running: TestTask
Elapsed time: 524 ms
principle
Process Overview
This part should be put to the end as a conclusion . But in order to quickly experience the overall process , Put it to the front .
Instantiation Enhancer And set the class and callback to proxy (MethodInterceptor Implementation class object of interface )
Create a proxy class ( Inherited from the proxied class )
Represented by dealTask Method ( Overridden by proxy class , Actually call the proxy class dealTask Method )
Callback intercept Method (ExecutionTimeProxy.intercept) // The callback is passed in the first step
proxy.invokeSuper(obj, args); // Call the parent class invokeSuper The method is MethodProxy Declarative
this.init(); // If it does not exist FastClass, Generate a
this.fastClassInfo.f2.invoke(fci.i2, obj, args);
FastClass Of invoke Method
Compare the first parameter with the number , Execute the corresponding method
Manually generate the proxy class source code
Analysis of the source code , It depends on the generated proxy class source code , Generation method : stay main Write this code at the beginning :
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\CglibProxyClass");
After operation , Will generate E:/CglibProxyClass Folder , Its structure is as follows
E:\CGLIBPROXYCLASS
├─net
│ └─sf
│ └─cglib
│ ├─core
│ │ MethodWrapper$MethodWrapperKey$$KeyFactoryByCGLIB$$d45e49f7.class
│ │
│ └─proxy
│ Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$7fb24d72.class
│
└─org
└─example
└─a
ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386$$FastClassByCGLIB$$bebee033.class
ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386.class
ExecutionTimeReality$$FastClassByCGLIB$$81389d66.class
Because the two one. $ stay CSDN The display in the is abnormal , So use one below $ Two $
ExecutionTimeReality$EnhancerByCGLIB$5a92c386$FastClassByCGLIB$bebee033.class
Quick class 1. This class will not be discussed for the time being . The code does not analyze .
ExecutionTimeReality$EnhancerByCGLIB$5a92c386.class
proxy class , Will be called to ExecutionTimeReality$EnhancerByCGLIB$5a92c386.class
ExecutionTimeReality$FastClassByCGLIB$81389d66.class
Quick class 2. Called by proxy class to .FastClass It is not generated with the proxy class , But in the first execution MethodProxy invoke/invokeSuper And put it in the cache .
ExecutionTimeReality$EnhancerByCGLIB$5a92c386.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.example.a;
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386 extends ExecutionTimeReality implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$dealTask$0$Method;
private static final MethodProxy CGLIB$dealTask$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$sayHello$1$Method;
private static final MethodProxy CGLIB$sayHello$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("org.example.a.ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$2$Method = var10000[0];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[1];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[2];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[3];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
var10000 = ReflectUtils.findMethods(new String[]{"dealTask", "(Ljava/lang/String;)V", "sayHello", "(Ljava/lang/String;)V"}, (var1 = Class.forName("org.example.a.ExecutionTimeReality")).getDeclaredMethods());
CGLIB$dealTask$0$Method = var10000[0];
CGLIB$dealTask$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "dealTask", "CGLIB$dealTask$0");
CGLIB$sayHello$1$Method = var10000[1];
CGLIB$sayHello$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "sayHello", "CGLIB$sayHello$1");
}
final void CGLIB$dealTask$0(String var1) {
super.dealTask(var1);
}
public final void dealTask(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$dealTask$0$Method, new Object[]{var1}, CGLIB$dealTask$0$Proxy);
} else {
super.dealTask(var1);
}
}
final void CGLIB$sayHello$1(String var1) {
super.sayHello(var1);
}
public final void sayHello(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$sayHello$1$Method, new Object[]{var1}, CGLIB$sayHello$1$Proxy);
} else {
super.sayHello(var1);
}
}
final boolean CGLIB$equals$2(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy);
return var2 == null ? false : (Boolean)var2;
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$3() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString();
}
final int CGLIB$hashCode$4() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$5() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1165719922:
if (var10000.equals("dealTask(Ljava/lang/String;)V")) {
return CGLIB$dealTask$0$Proxy;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$5$Proxy;
}
break;
case 771401912:
if (var10000.equals("sayHello(Ljava/lang/String;)V")) {
return CGLIB$sayHello$1$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$2$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$3$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$4$Proxy;
}
}
return null;
}
public ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386 var1 = (ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (var10000 == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386 var10000 = new ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386 var10000 = new ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386 var10000 = new ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
}
You can see through the source code of the proxy class , The proxy class will get all the methods inherited from the parent class , And there will be MethodProxy With the corresponding , such as :
private static final Method CGLIB$dealTask$0$Method;
private static final MethodProxy CGLIB$dealTask$0$Proxy;
private static final Method CGLIB$sayHello$1$Method;
private static final MethodProxy CGLIB$sayHello$1$Proxy;
in addition , From the type of proxy class , It directly inherits the class to be represented , So classes without interfaces that can be proxied . Actually , It has nothing to do with interfaces , If there is an interface, it will not be used .
public class ExecutionTimeReality$$EnhancerByCGLIB$$5a92c386 extends ExecutionTimeReality implements Factory
Method call
final void CGLIB$dealTask$0(String var1) {
super.dealTask(var1);
}
public final void dealTask(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$dealTask$0$Method, new Object[]{var1}, CGLIB$dealTask$0$Proxy);
} else {
super.dealTask(var1);
}
}
FastClass Mechanism
Cglib The reason why the efficiency of dynamic agent execution method is better than JDK Because Cglib Adopted FastClass Mechanism , Its principle is simply : Generate one for the proxy class and one for the proxied class Class, This Class A method of the proxy class or the proxied class is assigned a index(int type ). This index As a parameter ,FastClass You can directly locate the method to be called and call it directly , This saves reflection calls , So call efficiency ratio JDK Dynamic proxy calls high through reflection .
ExecutionTimeReality$FastClassByCGLIB$81389d66
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.example.a;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
public class ExecutionTimeReality$$FastClassByCGLIB$$81389d66 extends FastClass {
public ExecutionTimeReality$$FastClassByCGLIB$$81389d66(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1165719922:
if (var10000.equals("dealTask(Ljava/lang/String;)V")) {
return 0;
}
break;
case 771401912:
if (var10000.equals("sayHello(Ljava/lang/String;)V")) {
return 1;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return 2;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return 3;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 4;
}
}
return -1;
}
public int getIndex(String var1, Class[] var2) {
switch(var1.hashCode()) {
case -2012993625:
if (var1.equals("sayHello")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.String")) {
return 1;
}
}
}
break;
case -1776922004:
if (var1.equals("toString")) {
switch(var2.length) {
case 0:
return 3;
}
}
break;
case -1295482945:
if (var1.equals("equals")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.Object")) {
return 2;
}
}
}
break;
case 147696667:
if (var1.equals("hashCode")) {
switch(var2.length) {
case 0:
return 4;
}
}
break;
case 510300177:
if (var1.equals("dealTask")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.String")) {
return 0;
}
}
}
}
return -1;
}
public int getIndex(Class[] var1) {
switch(var1.length) {
case 0:
return 0;
default:
return -1;
}
}
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
ExecutionTimeReality var10000 = (ExecutionTimeReality)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.dealTask((String)var3[0]);
return null;
case 1:
var10000.sayHello((String)var3[0]);
return null;
case 2:
return new Boolean(var10000.equals(var3[0]));
case 3:
return var10000.toString();
case 4:
return new Integer(var10000.hashCode());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
ExecutionTimeReality var10000 = new ExecutionTimeReality;
ExecutionTimeReality var10001 = var10000;
int var10002 = var1;
try {
switch(var10002) {
case 0:
var10001.<init>();
return var10000;
}
} catch (Throwable var3) {
throw new InvocationTargetException(var3);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
public int getMaxIndex() {
return 4;
}
}
Other websites
Cglib Principle of dynamic agent implementation - Steamed buns with sugar beans - Blog Garden
边栏推荐
- I'm interested in watching Tiktok live beyond concert
- Starting from 1.5, build a micro Service Framework - call chain tracking traceid
- Four commonly used techniques for anti aliasing
- Spark获取DataFrame中列的方式--col,$,column,apply
- Hundreds of lines of code to implement a JSON parser
- Folding and sinking sand -- weekly record of ETF
- Cve-2017-11882 reappearance
- SAP Spartacus home 页面读取 product 数据的请求的 population 逻辑
- ADS-NPU芯片架构设计的五大挑战
- 图解网络:TCP三次握手背后的原理,为啥两次握手不可以?
猜你喜欢
MCU realizes OTA online upgrade process through UART
Cve-2017-11882 reappearance
[groovy] XML serialization (use markupbuilder to generate XML data | create sub tags under tag closures | use markupbuilderhelper to add XML comments)
Mobilenet series (5): use pytorch to build mobilenetv3 and learn and train based on migration
Recursive method to realize the insertion operation in binary search tree
Folding and sinking sand -- weekly record of ETF
2020.2.13
After 95, the CV engineer posted the payroll and made up this. It's really fragrant
2022-02-13 work record -- PHP parsing rich text
ADS-NPU芯片架构设计的五大挑战
随机推荐
RAID disk redundancy queue
Arduino六足机器人
Gartner发布2022-2023年八大网络安全趋势预测,零信任是起点,法规覆盖更广
Ffmpeg captures RTSP images for image analysis
The detailed page returns to the list and retains the original position of the scroll bar
Lone brave man
Convert binary search tree into cumulative tree (reverse middle order traversal)
Synchronized and reentrantlock
How to make your own robot
[Online gadgets] a collection of online gadgets that will be used in the development process
几百行代码实现一个 JSON 解析器
面试必刷算法TOP101之回溯篇 TOP34
Analysis of the combination of small program technology advantages and industrial Internet
Model analysis of establishment time and holding time
Building core knowledge points
KDD 2022 | 脑电AI助力癫痫疾病诊断
KDD 2022 | EEG AI helps diagnose epilepsy
For a deadline, the IT fellow graduated from Tsinghua suddenly died on the toilet
C language programming (Chapter 6 functions)
cf:D. Insert a Progression【关于数组中的插入 + 绝对值的性质 + 贪心一头一尾最值】