Design pattern [3.3] - Interpretation of cglib dynamic agent source code

Qinhuai grocery store 2021-11-25 18:04:56
design pattern interpretation cglib dynamic

<img src=“ https://markdownpicture.oss-cn-qingdao.aliyuncs.com/blog/20210925233820.png” width = “500” height = “400” alt=“ Image name ” align=center />

cglib A dynamic proxy

cglib Introduce

CGLIB It's an open source project , A powerful high-performance and high-quality code generation library , It can be expanded during operation Java class , Realization Java Interface, etc. . The bottom layer is to use a small and fast bytecode processing framework ASM, To convert bytecode and generate new classes .

In theory, we can also use ASM To directly generate code , But ask us to JVM Inside ,class File format , And the instruction set of bytecode .

This thing is not here JDK Inside the bag , You need to download, import or Maven Coordinate import .

I choose Maven Import , Add to pom.xml file :

<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

Student.java:

public class Student {
public void learn() {
System.out.println(" I am a student , I want to learn. ");
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

MyProxy.java( proxy class )


import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class StudentProxy implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println(" Before the agency -------");
proxy.invokeSuper(obj, args);
System.out.println(" After agency -------");
return null;
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

Test class (Test.java


import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Test {
public static void main(String[] args) {
// proxy class class The file is saved to the local disk for us to decompile and view the source code 
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/aphysia/Desktop");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Student.class);
enhancer.setCallback(new StudentProxy());
Student student = (Student) enhancer.create();
student.learn();
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

The result of running is :

CGLIB debugging enabled, writing to '/Users/xuwenhao/Desktop'
Before the agency -------
I am a student , I want to learn.
After agency -------

  • 1.
  • 2.
  • 3.
  • 4.

In the folder we chose , Generated the code of the proxy class :

 Design patterns 【3.3】-- CGLIB Dynamic agent source code interpretation _ proxy class

Source code analysis

Let's first proxy the class , Need to achieve MethodInterceptor( method interceptors ) Interface , This interface has only one method intercept, The parameters are :

  • obj: Objects that need to be enhanced
  • method: The way to intercept
  • args: Method parameters to be intercepted
  • proxy: Represents the method object to trigger the parent class
package net.sf.cglib.proxy;
import java.lang.reflect.Method;
public interface MethodInterceptor extends Callback {
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

Let's go back to the method we want to create the proxy class enhancer.create(), The meaning of this method : if necessary , Generate a new class , And use the specified callback ( If any ) To create a new object instance . Parameterless constructor using superclass .

 public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

The main method logic we have to look at createHelper(), Besides checking , It's called KEY_FACTORY.newInstance() Method generation EnhancerKey object ,KEY_FACTORY Is static EnhancerKey Interface ,newInstance() Is a method in the interface , A focus on super.create(key) Inside , It's called the method of the parent class :

 private Object createHelper() {
// check 
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

AbstractClassGenerator yes Enhancer Parent class of ,create(key) The main logic of the method is to get the class loader , Cache get class load data , Then reflect the construction object , There are two methods to create instance objects :

  • fistInstance(): This method should not be invoked in the regular stream. . Technically speaking ,{@link #wrapCachedClass(Class)} Use {@link EnhancerFactoryData} As cache value , The latter supports faster instantiation than normal old reflection lookups and calls . For backward compatibility reasons , This method remains unchanged : Just in case it's ever been used .( My understanding is that the current logic will not go to this branch , Because it's busy , But for compatibility , This case Still remain in the ), Internal logic actually uses ReflectUtils.newInstance(type).
  • nextInstance(): Real class to create proxy object
 protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
// The way to actually create an object 
return nextInstance(obj);
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

This method is defined in AbstractClassGenerator, But it's actually calling subclasses Enhancer The implementation of the , Mainly by obtaining parameter types , Parameters , And callback objects , Use these parameters to generate proxy objects .

 protected Object nextInstance(Object instance) {
EnhancerFactoryData data = (EnhancerFactoryData) instance;
if (classOnly) {
return data.generatedClass;
}
Class[] argumentTypes = this.argumentTypes;
Object[] arguments = this.arguments;
if (argumentTypes == null) {
argumentTypes = Constants.EMPTY_CLASS_ARRAY;
arguments = null;
}
// structure 
return data.newInstance(argumentTypes, arguments, callbacks);
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

Internal implementation logic , All the calls are ReflectUtils.newInstance(), The parameter types are different :

 public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
setThreadCallbacks(callbacks);
try {
if (primaryConstructorArgTypes == argumentTypes ||
Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
return ReflectUtils.newInstance(primaryConstructor, arguments);
}
return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
} finally {
setThreadCallbacks(null);
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

Follow in to the end , Is to get the constructor method , Construct proxy objects by reflection , The final call is JDK Methods provided :

 public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {
return newInstance(getConstructor(type, parameterTypes), args);
}
public static Object newInstance(final Constructor cstruct, final Object[] args) {
boolean flag = cstruct.isAccessible();
try {
if (!flag) {
cstruct.setAccessible(true);
}
Object result = cstruct.newInstance(args);
return result;
} catch (InstantiationException e) {
throw new CodeGenerationException(e);
} catch (IllegalAccessException e) {
throw new CodeGenerationException(e);
} catch (InvocationTargetException e) {
throw new CodeGenerationException(e.getTargetException());
} finally {
if (!flag) {
cstruct.setAccessible(flag);
}
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

Open the automatically generated proxy class file and have a look , You will find that it is actually generating those methods , Some enhancement methods are added :

 Design patterns 【3.3】-- CGLIB Dynamic agent source code interpretation _ A dynamic proxy _02

The generated proxy class inherits the original class :

public class Student$$EnhancerByCGLIB$$929cb5fe extends Student implements Factory {
...
}

  • 1.
  • 2.
  • 3.

Look at the generated enhancements , In fact, it calls intercept() Method , This method is implemented by ourselves , Therefore, the enhanced function of proxy object is completed :

 public final void learn() {
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$learn$0$Method, CGLIB$emptyArgs, CGLIB$learn$0$Proxy);
} else {
super.learn();
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

cglib and jdk What's the difference between dynamic agents

  1. jdk Dynamic proxy is an anonymous class that uses interceptors and reflection to generate a proxy interface , Give it to me when you execute the method InvokeHandler Handle .CGLIB Dynamic proxy uses ASM frame , Modify the original bytecode , Then generate new subclasses to handle .
  2. JDK The agent needs to implement the interface , however CGLIB Don't force .
  3. stay JDK1.6 Before ,cglib Because bytecode generation technology is used , Higher than reflection efficiency , But after that, jdk There are also some optimizations , Efficiency has improved .

【 Author's brief introduction 】
Qin Huai , official account 【 Qin Huai grocery store 】 author , The road to technology is not in the moment , one's nobility lasts forever , Even slowly , Go on and on . Personal writing direction :Java The source code parsing ,JDBC,Mybatis,Spring,redis, Distributed , The finger of the sword Offer,LeetCode etc. , Write every article carefully , I don't like the title party , I don't like fancy things , Most of them write series , There's no guarantee that everything I write is right , But I promise that what I have written has been put into practice or searched for information . Omission or error , I hope you can correct me. .

  The finger of the sword Offer All the answers PDF

 2020 What did I write in 2000 ?

  Open source programming notes

版权声明
本文为[Qinhuai grocery store]所创,转载请带上原文链接,感谢
https://javamana.com/2021/11/20211109095357350f.html

  1. 6年老猿带你掌握Spring Boot实现定时任务的动态增删启停
  2. disruptor笔记之六:常见场景,java教程从入门到精通pdf百度云
  3. Pourquoi InnoDB n'utilise - t - il pas un cache LRU naïf?
  4. Java Reflection (2): quelques opérations de base de reflection
  5. 6年老猿帶你掌握Spring Boot實現定時任務的動態增删啟停
  6. Les singes âgés vous permettent de maîtriser le démarrage et l'arrêt dynamiques des tâches programmées par Spring boot
  7. Docker From Beginning to Practice Series IV - docker Container chorégraphe Clean docker Composition
  8. 编写 java 程序,为家用电脑 ipv6 自动更新 goddy dns 记录(ddns)
  9. java jvm-old gc耗时几十s,导致系统告警
  10. Disruptor note 6: scénario commun, tutoriel Java de l'introduction à la maîtrise du PDF Baidu Cloud
  11. 编写Java程序启动脚本最佳实践
  12. How to get the correct Linux user's documents, music videos and other directories?
  13. Java JVM Old GC prend des dizaines de s, ce qui provoque une alarme système
  14. Écrivez un programme Java pour mettre à jour automatiquement les enregistrements DNS goddy (ddns) pour l'ordinateur domestique IPv6
  15. 編寫Java程序啟動脚本最佳實踐
  16. Meilleures pratiques pour écrire des scripts de démarrage de programmes Java
  17. Notes sur springcloud Eureka
  18. Ajout, suppression et modification simples de mybatis
  19. MySQL Learning - Logging System Redo log and Bin log
  20. Springboot Common comments | @ configuration
  21. Mécanisme d'expiration du cache redis et d'élimination de la mémoire
  22. Analyse concise du code source redis 01 - configuration de l'environnement
  23. Java - carte mémoire de l'objet
  24. Redis source Concise Analysis 02 - SDS String
  25. Why did docker lose to kubernetes? Docker employee readme!
  26. Spring cloud gateway practice 2: more routing configuration methods
  27. Principe de mise en œuvre ultime du mécanisme de concurrence Java sous - jacent
  28. [démarrer avec Java 100 exemples] 13. Modifier l’extension de fichier - remplacement de chaîne
  29. Java期末作业——王者荣耀的洛克王国版游戏
  30. Elasticsearch聚合学习之五:排序结果不准的问题分析,阿里巴巴java性能调优实战
  31. Java期末作業——王者榮耀的洛克王國版遊戲
  32. Java final work - King's Glory Rock Kingdom Game
  33. 【网络编程】TCP 网络应用程序开发
  34. 【网络编程入门】什么是 IP、端口、TCP、Socket?
  35. 【網絡編程入門】什麼是 IP、端口、TCP、Socket?
  36. [Introduction à la programmation réseau] qu'est - ce que IP, port, TCP et socket?
  37. [programmation réseau] développement d'applications réseau TCP
  38. [Java Basics] comprendre les génériques
  39. Dix outils open source que les architectes de logiciels Java devraient maîtriser!!
  40. Java经典面试题详解,突围金九银十面试季(附详细答案,mysql集群架构部署方案
  41. java架构之路(多线程)synchronized详解以及锁的膨胀升级过程,mysql数据库实用教程pdf
  42. java整理,java高级特性编程及实战第一章
  43. java教程——反射,mongodb下载教程
  44. Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day12,zookeeper原理作用
  45. Java后端互联网500道中高级面试题(含答案),linux钩子技术
  46. java8 Stream API及常用方法,java初级程序员面试
  47. java-集合-Map(双列)——迪迦重制版,2021Java开发社招面试解答之性能优化
  48. Flink处理函数实战之二:ProcessFunction类,java线程面试题目
  49. flex 布局详解,【Java面试题
  50. Linux basic command learning
  51. Why did docker lose to kubernetes? Docker employee readme!
  52. MySQL安装
  53. Elastic Search Aggregate Learning five: Problem Analysis of Uncertainty of sequencing results, Alibaba Java Performance Tuning Practical
  54. Installing, configuring, starting and accessing rabbitmq under Linux
  55. Oracle SQL injection summary
  56. Installation MySQL
  57. L'exposition à la photo d'essai sur la route i7 du nouveau vaisseau amiral de BMW Pure Electric a également été comparée à celle de Xiaopeng p7.
  58. spring JTA 关于异常处理的时机问题
  59. Le problème du temps de traitement des exceptions dans la JTA printanière
  60. Do you really know MySQL order by