当前位置:网站首页>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 msprinciple
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.classBecause 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 FactoryMethod 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
边栏推荐
- After Luke zettlemoyer, head of meta AI Seattle research | trillion parameters, will the large model continue to grow?
- DD's command
- 【EI会议分享】2022年第三届智能制造与自动化前沿国际会议(CFIMA 2022)
- curlpost-php
- XML Configuration File
- curlpost-php
- Spark获取DataFrame中列的方式--col,$,column,apply
- Dynamic programming -- linear DP
- Hundreds of lines of code to implement a JSON parser
- NLP generation model 2017: Why are those in transformer
猜你喜欢

从 1.5 开始搭建一个微服务框架——调用链追踪 traceId

Idea remotely submits spark tasks to the yarn cluster

Model analysis of establishment time and holding time

VSphere implements virtual machine migration

MCU realizes OTA online upgrade process through UART

Folding and sinking sand -- weekly record of ETF

Anconda download + add Tsinghua +tensorflow installation +no module named 'tensorflow' +kernelrestart: restart failed, kernel restart failed

Browser reflow and redraw

MySQL storage engine

Analysis of the combination of small program technology advantages and industrial Internet
随机推荐
Finding the nearest common ancestor of binary search tree by recursion
Analysis of the combination of small program technology advantages and industrial Internet
[Online gadgets] a collection of online gadgets that will be used in the development process
RAID disk redundancy queue
Cf:d. insert a progression [about the insert in the array + the nature of absolute value + greedy top-down]
golang mqtt/stomp/nats/amqp
2022-02-13 work record -- PHP parsing rich text
程序员搞开源,读什么书最合适?
Live broadcast system code, custom soft keyboard style: three kinds of switching: letters, numbers and punctuation
STM32 key chattering elimination - entry state machine thinking
KDD 2022 | 脑电AI助力癫痫疾病诊断
95后CV工程师晒出工资单,狠补了这个,真香...
Spark AQE
Hundreds of lines of code to implement a JSON parser
The detailed page returns to the list and retains the original position of the scroll bar
golang mqtt/stomp/nats/amqp
Folding and sinking sand -- weekly record of ETF
Curlpost PHP
激动人心,2022开放原子全球开源峰会报名火热开启
小程序容器可以发挥的价值