Java回调函数 + 使用案例

article/2025/10/5 16:46:04

文章目录

  • 前言
  • 什么是回调函数
  • 第0个版本
  • 第1个版本
  • 第2个版本
  • 第3个版本
  • 第4个版本
  • 第5个版本
  • 第6个版本
  • 回头解析前言描述的问题
    • 1. MethodIntrospector.selectMethods()
    • 2. 抽象类MethodIntrospector
    • 3. 方法selectMethods()
    • 4. 成员变量USER_DECLARED_METHODS
    • 5. 方法doWithMethods()
    • 6. doWithMethods()方法里调用的getMappingForMethod()方法
    • 7. getMappingForMethod()方法里调用的createRequestMappingInfo()

前言

在看Handler时看到了这么语段代码顿时就蒙了

Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {try {return getMappingForMethod(method, userType);}catch (Throwable ex) {throw new IllegalStateException("Invalid mapping on handler class [" +userType.getName() + "]: " + method, ex);}});

经过指点知道这是回调函数,马上来补充一下~

什么是回调函数

回调函数,是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
在Java中,指针即所谓的引用。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
第一次看这知识点,是真的懵。下面看我拆解后逐步理解它的思路。

第0个版本

初代版本
我调用了你,在我调用你的方法里你又调用了我(回调)

public class ClassA {public void a() {System.out.println("执行了a方法");ClassB b = new ClassB();b.b();}public void backs(){System.out.println("A:我就是A的回调函数!");}
}
public class ClassB {public void b() {System.out.println("我执行了b");System.out.println("B:我开始调用A的回调-->");ClassA a = new ClassA();a.backs();System.out.println("B: <--我完成调用A的回调");}
}
public class Main {public static void main(String[] args) {ClassA a = new ClassA();a.a();}
}

执行结果

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数!
B: <--我完成调用A的回调

第1个版本

演变一下把在B里创建的A,用对象的形式在A里调用时就带过去。
写一个回调用的接口

public class ClassA {public void a() {System.out.println("执行了a方法");ClassB b = new ClassB();b.b(this);}public void backs(){System.out.println("A:我就是A的回调函数!");}
}

public class ClassB {public void b(ClassA a) {System.out.println("我执行了b");System.out.println("B:我开始调用A的回调-->");a.backs();System.out.println("B: <--我完成调用A的回调");}
}

Main方法不用变
执行结果,执行结果不变

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数!
B: <--我完成调用A的回调

第2个版本

把第1个版本中的这个类换成接口Interface
创建一个接口

public interface Interface {public void backs();
}
public class ClassA {public void a() {System.out.println("执行了a方法");ClassB b = new ClassB();b.b(new Interface() {@Overridepublic void backs() {System.out.println("A:我就是A的回调函数!");}});}
}
public class ClassB {public void b(Interface in) {System.out.println("我执行了b");System.out.println("B:我开始调用A的回调-->");in.backs();System.out.println("B: <--我完成调用A的回调");}
}

Main依然不变
执行结果也不变

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数!
B: <--我完成调用A的回调

第3个版本

给接口加一个入参,让回调方法可以传参

public interface Interface {public void backs(String n);
}
public class ClassA {public void a() {System.out.println("执行了a方法");ClassB b = new ClassB();b.b(new Interface() {@Overridepublic void backs(String n) {System.out.println("A:我就是A的回调函数!我打印:" + n);}});}
}
public class ClassB {public void b(Interface in) {System.out.println("我执行了b");System.out.println("B:我开始调用A的回调-->");in.backs("《我是B传的参数》");System.out.println("B: <--我完成调用A的回调");}
}

执行结果

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数!我打印:《我是B传的参数》
B: <--我完成调用A的回调

第4个版本

给接口加个返回的参数

public interface Interface {public String backs(String n);
}
public class ClassA {public void a() {System.out.println("执行了a方法");ClassB b = new ClassB();b.b(new Interface() {@Overridepublic String backs(String n) {System.out.println("A:我就是A的回调函数!我打印:" + n);return "A:我就是A的回调函数!我打印:" + n + "的返回。";}});}
}
public class ClassB {public void b(Interface in) {System.out.println("我执行了b");System.out.println("B:我开始调用A的回调-->");String backs = in.backs("《我是B传的参数》");System.out.println("B:我收到了回调的结果:"+backs);System.out.println("B: <--我完成调用A的回调");}
}

执行结果

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数!我打印:《我是B传的参数》
B:我收到了回调的结果:A:我就是A的回调函数!我打印:《我是B传的参数》的返回。
B: <--我完成调用A的回调

第5个版本

public interface Interface {public String backs(String n);
}
public class ClassA {public void a() {System.out.println("执行了a方法");ClassB b = new ClassB();String b1 = b.b(new Interface() {@Overridepublic String backs(String n) {System.out.println("A:我就是A的回调函数!我打印:" + n);return "A:我就是A的回调函数!我打印:" + n + "的返回。";}});System.out.println("A:执行完得到的结果:" + b1);}
}
public class ClassB {public String b(Interface in) {System.out.println("我执行了b");System.out.println("B:我开始调用A的回调-->");String backs = in.backs("《我是B传的参数》");System.out.println("B:我收到了回调的结果:"+backs + "<--我完成调用A的回调");return "《我是B的返回》";}
}

执行结果:

执行了a方法
我执行了b
B:我开始调用A的回调-->
A:我就是A的回调函数!我打印:《我是B传的参数》
B:我收到了回调的结果:A:我就是A的回调函数!我打印:《我是B传的参数》的返回。<--我完成调用A的回调
A:执行完得到的结果:《我是B的返回》

第6个版本

先声明回调函数,再使用

public class ClassA {public void a() {System.out.println("执行了a方法");Interface in = (n -> {System.out.println("A:我是直接使用回调接口,我接收的参数是:" + n);return "我是回调的返回数据";});String backs = in.backs("我A,我是《in》的使用者");System.out.println("backes:" + backs);}
}

调用

public class Main {public static void main(String[] args) {ClassA a = new ClassA();a.a();}
}

执行结果

执行了a方法
A:我是直接使用回调接口,我接收的参数是:我A,我是《in》的使用者
backes:我是回调的返回数据

回头解析前言描述的问题

1. MethodIntrospector.selectMethods()

Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,// 接口需要MetadataLookup类型,做一个类型转换。// T:Set<Scheduled>,这个参数是被调用类传过来的。// 看调用代码,点方法selectMethods进去// 里面这句代码为回调 metadataLookup.inspect(specificMethod);// (MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {// 使用方法和类型级别的RequestMapping注解来创建RequestMappingInfo。Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);return (!scheduledMethods.isEmpty() ? scheduledMethods : null);});

2. 抽象类MethodIntrospector

在这之前先看下这个回调的接口,这是MethodIntrospector类里的内部类

public abstract class MethodIntrospector {public interface MetadataLookup<T> {T inspect(Method method);}
}

3. 方法selectMethods()

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {final Map<Method, T> methodMap = new LinkedHashMap<>();Set<Class<?>> handlerTypes = new LinkedHashSet<>();Class<?> specificHandlerType = null;// 是否是代理类if (!Proxy.isProxyClass(targetType)) {// 通过这个类取到这个类,检测是否是CGLIB 生成的子类,是则返回原始类。specificHandlerType = ClassUtils.getUserClass(targetType);// 添加到set哈希表中handlerTypes.add(specificHandlerType);}// 把这个类的所有接口类添加到set哈希表中handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));for (Class<?> currentHandlerType : handlerTypes) {// 这个不知道为啥,不使用循环里的类。也就是不使用接口,要使用实际的实现类,是这个意思?final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);// 霍,刚那个回调函数还没完成,又来一个。// 3个参数,1-接口或实现类 2-被调函数传过来的 // 3-预构建的 MethodFilter 匹配所有未在java.lang.Object声明的非桥非合成方法。ReflectionUtils.doWithMethods(currentHandlerType, method -> {// 从这个类中找到这个方法Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);// 再向上回调到调用此方法的类中执行对应的方法,带着这个类。返回的是RequestMappingInfo的对象 /admin/testT result = metadataLookup.inspect(specificMethod);if (result != null) {// 找到提供的bridge Method的原始方法。Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {methodMap.put(specificMethod, result);}}}, ReflectionUtils.USER_DECLARED_METHODS);}// <Method,RequestMappingInfo>的Mapreturn methodMap;}

4. 成员变量USER_DECLARED_METHODS

// 这里又是一个回调函数的使用,在ReflectionUtils里的成员变量
public static final MethodFilter USER_DECLARED_METHODS =(method -> !method.isBridge() && !method.isSynthetic());// 看下回调接口,这也是个内部类
public abstract class ReflectionUtils {public interface MethodFilter {boolean matches(Method method);}
}
// 理解:这个就是上面没写调用直接使用里面的回调了生成了一个里面只有一个接口的对象,接口里面已经有实现
// 调用这个就是相当于调用了这个方法。

5. 方法doWithMethods()

接selectMethods调用doWithMethods。对给定类和超类(或给定接口和超接口)的所有匹配方法执行给定的回调操作。

public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {// Keep backing up the inheritance hierarchy.// 缓存下,并该类中的所有方法Method[] methods = getDeclaredMethods(clazz, false);for (Method method : methods) {// 使用回调函数得到的接口对象。判断是否不是桥且不是可执行文件是合成结构  不是跳过if (mf != null && !mf.matches(method)) {// 是桥或是可执行文件跳过continue;}try {// 这个就是调用doWithMethods的回调在调用它的类里执行,传过去的是这个类中的方法mc.doWith(method);}catch (IllegalAccessException ex) {throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);}}if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {doWithMethods(clazz.getSuperclass(), mc, mf);}else if (clazz.isInterface()) {for (Class<?> superIfc : clazz.getInterfaces()) {doWithMethods(superIfc, mc, mf);}}
}

6. doWithMethods()方法里调用的getMappingForMethod()方法

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {// 根据提供的annotatedElement是类还是方法提供适当的自定义RequestCondition 。// 接收的是一个 AnnotatedElement 类型,// Method 继承 Executable,Executable 实现 GenericDeclaration// GenericDeclaration 继承 AnnotatedElement,可以看下// 创建方法上的 RequestMappingInfoRequestMappingInfo info = createRequestMappingInfo(method);if (info != null) {// 创建方法的类上的 RequestMappingInfoRequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);if (typeInfo != null) {// 把两个 RequestMappingInfo 组合在一起info = typeInfo.combine(info);}String prefix = getPathPrefix(handlerType);if (prefix != null) {info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);}}return info;
}

看下效果更深刻
在这里插入图片描述

组合
在这里插入图片描述

组合完后的RequestMappingInfo
在这里插入图片描述

7. getMappingForMethod()方法里调用的createRequestMappingInfo()

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {// 找到这个方法第一个@RequestMappingRequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);// 两个方法都是返回的null,看上去能被覆盖。不过这个启动没有直接返回的null,所以condition是nullRequestCondition<?> condition = (element instanceof Class ?getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));// 生成RequestMappingInfo对象return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

好奇,find什么样
在这里插入图片描述

实际我这个方法我怎么标记的?
在这里插入图片描述

调用创建RequestMappingInfo的构造

protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {RequestMappingInfo.Builder builder = RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes()).produces(requestMapping.produces()).mappingName(requestMapping.name());if (customCondition != null) {builder.customCondition(customCondition);}return builder.options(this.config).build();
}

创建完长这样
在这里插入图片描述

结言
所以最后的结果就是把这个类下所有方法标RequestMapping注解的方法以<Method,RequestMappingInfo>放入Map

全文完~

文文的博客,博学躬行。欢迎指正~


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

相关文章

Java-回调函数

什么是回调 函数调用可以分为三种模式&#xff0c;串行调用、异步调用、回调。这三种都是日常开发中常见到的方式。 一个方法执行完&#xff0c;再执行下一个&#xff0c;串行逻辑会阻塞线程执行流程&#xff0c;等到所有逻辑执行结束&#xff0c;线程才会结束。 异步执行是…

java回调函数(callBack)

最近有个新同事给我写了个接口&#xff0c;说是用到了回调&#xff0c;我等了半天发现结果才返回回来&#xff0c;把我都整急了。最后我看了他的代码&#xff0c;目瞪口呆。他还信誓旦旦的说没错&#xff0c;按网上的例子来写的。 我一搜&#xff0c;网上例子真一大堆&#xff…

java回调函数的作用以及运用

模块之间总是存在这一定的接口&#xff0c;从调用方式上看&#xff0c;可以分为三类&#xff1a;同步调用、回调和异步调用。同步调用是一种阻塞式调用&#xff0c;也是我们在写程序中经常使用的&#xff1b;回调是一种双向的调用模式&#xff0c;也就是说&#xff0c;被调用的…

Java回调函数理解和应用

#Java回调函数理解和应用 所谓回调&#xff1a;就是A类中调用B类中的某个方法C&#xff0c;然后B类中反过来调用A类中的方法D&#xff0c;D这个方法就叫回调方法&#xff0c;这样子说你是不是有点晕晕的。 在未理解之前&#xff0c;我也是一脸懵逼&#xff0c;等我理解之后&…

java回调函数(全干货)

产生接口回调的场景 产生接口回调的场景很简单,比如A叫B帮忙做一件事,交代完后A去忙别的事&#xff0c;然后B做完这件事之后会通知A, 通知A的这个动作就是接口回调的动作。接口回调 接口回调的意义是通过接口来实现解耦的的前提下调用另一个类的方法&#xff0c;也就是B为A准…

深入理解Java回调函数

废话不多说&#xff0c;像许多网上介绍回调机制的文章一样&#xff0c;我这里也以一个现实的例子开头&#xff1a;假设你公司的总经理出差前需要你帮他办件事情&#xff0c;这件事情你需要花些时间去做&#xff0c;这时候总经理肯定不能守着你做完再出差吧&#xff0c;于是就他…

java 回调函数解读

模块间调用 在一个应用系统中&#xff0c;无论使用何种语言开发&#xff0c;必然存在模块之间的调用&#xff0c;调用的方式分为几种&#xff1a; &#xff08;1&#xff09;同步调用 同步调用是最基本并且最简单的一种调用方式&#xff0c;类A的方法a()调用类B的方法b()&…

java中的回调函数

CALLBACK&#xff0c;即回调函数&#xff0c;是一个通过函数指针调用的函数。如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用为调用它所指向的函数时&#xff0c;我们就说这是回调函数。回调函数不是由该函数的实现方直接…

Java回调函数详解

什么是回调函数&#xff08;CallBack&#xff09; 在编写程序时&#xff0c;有时候会调用许多API中实现实现的函数&#xff0c;但某些方法需要我们传入一个方法&#xff0c;以便在需要的时候调用我们传入进去的函数。这个被传入的函数称为回调函数&#xff08;Callback functi…

numpy 数据类型转换

参考NumPy 数据类型 - 云社区 - 腾讯云 首先需要导入numpy模块 import numpy as np 首先生成一个浮点数组 a np.random.random(4) dtype的用法 看看结果信息&#xff0c;左侧是结果信息&#xff0c;右侧是对应的python语句 我们发现这个数组的type是float64&#xff0c;…

javascript学习之数据类型转换

⭐️⭐️⭐️ 作者&#xff1a;船长在船上 &#x1f6a9;&#x1f6a9;&#x1f6a9; 主页&#xff1a;来访地址船长在船上的博客 &#x1f528;&#x1f528;&#x1f528; 简介&#xff1a;资深前端开发工程师&#xff0c;专注前端开发&#xff0c;欢迎咨询交流&#xff0…

【Python入门篇】——Python基础语法(数据类型与数据类型转换)

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; Python入门&#xff0c;本专栏主要内容为Python的基础语法&#xff0c;Python中的选择循环语句…

Java八种基本数据类型转换

转换规则&#xff1a; 1、八种基本数据类型当中除布尔类型之外剩下的7种类型之间都可以相互转换。 2、小容量向大容量转换&#xff0c;称为自动类型转换&#xff0c;容量从小到大排序(此处没有布尔类型)&#xff1a; byte < short < int < long < float < doub…

Java数据类型转换

前言 我将在这篇文章中介绍关于Java数据类型转换的知识 一、Java基本数据类型的转换 Java的数据类型转换分两种&#xff0c;一种是由低精度向高精度转换&#xff0c;另一种是由高精度由低精度转换 double float long int short byte 以上数据类型的精度由高到低 1.由低精度向…

C#中的数据类型转换

一、定义 C# 是一门强类型语言&#xff0c;对类型要求比较严格&#xff0c;但是在一定的条件下也是可以相互转换的&#xff0c; 如将 int 型数据转换成 double 型数据。 C#中的数据类型转换分为两种&#xff1a;隐式类型转换以及显式类型转换&#xff1b; 二、数据类型转换方式…

Pandas数据类型转换

Pandas数据类型转换 一、Pandas中的数据类型&#xff1a; 不管是Series还是DataFrame的每一列&#xff0c;都有对应的数据类型。在Pandas中存在以下数据类型。 Pandas dtypePython 类型Numpy类型描述objectstr或者mixed&#xff08;混合类型&#xff09;string_, unicode_, …

C++ 数据类型转换

C 数据类型转换 概述不同类型数据间的转换隐式类型转换强制类型转换自己声明的类型转换转换构造函数 类型转换函数案例应用 概述 在日常的开发中, 我们经常会用到数据类型转换, 所以我们要对数据类型转换有一定的了解. 不同类型数据间的转换 在 C 中, 某些标准类型的数据之间…

C语言数据类型转换详解

数据类型转换就是将数据&#xff08;变量、数值、表达式的结果等&#xff09;从一种类型转换为另一种类型。 自动类型转换 自动类型转换就是编译器默默地、隐式地、偷偷地进行的数据类型转换&#xff0c;这种转换不需要程序员干预&#xff0c;会自动发生。 1 . 将一种类型的…

Java的数据类型转换

Java的数据类型转换分为以下的两种&#xff1a; 一、自动类型转换 首先我们要了解自动类型转换是什么&#xff0c;为什么要进行自动类型转换呢&#xff1f; 1.自动类型转换就是类型范围小的变量&#xff0c;可以直接赋值给类型范围大的变量。比如以下代码&#xff1a; 其中int类…

Python 中数据类型转换

前两篇回顾&#xff1a; 第一篇&#xff1a;环境搭建以及运行 Python 的 3 种方式 第二篇&#xff1a;变量及数据类型 今天是 Python 基础系列第三篇&#xff1a;三种数据类型转换形式&#xff08;布尔值转换、字符串转换、数值转换&#xff09;。 一、转为布尔值 一&#xf…