当前位置:网站首页>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
边栏推荐
- The population logic of the request to read product data on the sap Spartacus home page
- Synchronized and reentrantlock
- Uniapp development, packaged as H5 and deployed to the server
- Introduction of motor
- Recursive method converts ordered array into binary search tree
- Folding and sinking sand -- weekly record of ETF
- RAID disk redundancy queue
- Construction plan of Zhuhai food physical and chemical testing laboratory
- Keepalive component cache does not take effect
- MIT doctoral thesis | robust and reliable intelligent system using neural symbol learning
猜你喜欢
[groovy] XML serialization (use markupbuilder to generate XML data | set XML tag content | set XML tag attributes)
Fibonacci number
Building core knowledge points
Comment faire votre propre robot
VSphere implements virtual machine migration
Uniapp development, packaged as H5 and deployed to the server
Basic introduction and source code analysis of webrtc threads
Browser reflow and redraw
[EI conference sharing] the Third International Conference on intelligent manufacturing and automation frontier in 2022 (cfima 2022)
[groovy] JSON string deserialization (use jsonslurper to deserialize JSON strings | construct related classes according to the map set)
随机推荐
Leetcode 44 Wildcard matching (2022.02.13)
RAID disk redundancy queue
cf:C. The Third Problem【关于排列这件事】
Arduino六足机器人
Spark SQL null value, Nan judgment and processing
Analysis of the combination of small program technology advantages and industrial Internet
Differences between standard library functions and operators
KDD 2022 | 脑电AI助力癫痫疾病诊断
孤勇者
面试必刷算法TOP101之回溯篇 TOP34
Basic introduction and source code analysis of webrtc threads
看抖音直播Beyond演唱会有感
详细页返回列表保留原来滚动条所在位置
Dynamic programming -- linear DP
Power query data format conversion, Split Merge extraction, delete duplicates, delete errors, transpose and reverse, perspective and reverse perspective
Exciting, 2022 open atom global open source summit registration is hot
Spark-SQL UDF函数
Meta AI西雅图研究负责人Luke Zettlemoyer | 万亿参数后,大模型会持续增长吗?
Lone brave man
在产业互联网时代,将会凭借大的产业范畴,实现足够多的发展