Java Callback Function + Use Case

Preface

When I saw this paragraph code in Handler, I was confused

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);
		}
    });

Knowing at your fingertips that this is a callback function, add it now~

What is a callback function

A callback function is a function that is called through a function pointer. If you pass a function's pointer (address) as an argument to another function, when the pointer is used to call the function it points to, we call it a callback function.
In Java, a pointer is a reference. A callback function is not called directly by the implementer of the function, but by another party in response to a particular event or condition when it occurs.
The first time I looked at this knowledge point, I was really confused. Here's how I understand it step by step after I disassemble it.

Version 0

Initial Version
I called you, and in the way I called you, you called me again (callback)

public class ClassA {
    public void a() {
        System.out.println("Executed a Method");
        ClassB b = new ClassB();
        b.b();
    }
    public void backs(){
        System.out.println("A:I am A Callback function!");
    }
}
public class ClassB {
    public void b() {
        System.out.println("I executed b");
        System.out.println("B:I started calling A Callback-->");
        ClassA a = new ClassA();
        a.backs();
        System.out.println("B: <--I complete the call A Callback");
    }
}
public class Main {
    public static void main(String[] args) {
        ClassA a = new ClassA();
        a.a();
    }
}

results of enforcement

Executed a Method
 I executed b
B:I started calling A Callback-->
A:I am A Callback function!
B: <--I complete the call A Callback

Version 1

Evolution takes A created in B and called in A as an object.
Write an interface to call back

public class ClassA {
    public void a() {
        System.out.println("Executed a Method");
        ClassB b = new ClassB();
        b.b(this);
    }
    public void backs(){
        System.out.println("A:I am A Callback function!");
    }
}
public class ClassB {
    public void b(ClassA a) {
        System.out.println("I executed b");
        System.out.println("B:I started calling A Callback-->");
        a.backs();
        System.out.println("B: <--I complete the call A Callback");
    }
}

Main method does not need to be changed
Execution result, execution result unchanged

Executed a Method
 I executed b
B:I started calling A Callback-->
A:I am A Callback function!
B: <--I complete the call A Callback

Version 2

Replace this class in version 1 with an Interface interface Interface
Create an interface

public interface Interface {
    public void backs();
}
public class ClassA {
    public void a() {
        System.out.println("Executed a Method");
        ClassB b = new ClassB();
        b.b(new Interface() {
            @Override
            public void backs() {
                System.out.println("A:I am A Callback function!");
            }
        });
    }
}
public class ClassB {
    public void b(Interface in) {
        System.out.println("I executed b");
        System.out.println("B:I started calling A Callback-->");
        in.backs();
        System.out.println("B: <--I complete the call A Callback");
    }
}

Main remains unchanged
Execution results will not change

Executed a Method
 I executed b
B:I started calling A Callback-->
A:I am A Callback function!
B: <--I complete the call A Callback

Version 3

Add a parameter to the interface so that the callback method can pass parameters

public interface Interface {
    public void backs(String n);
}
public class ClassA {
    public void a() {
        System.out.println("Executed a Method");
        ClassB b = new ClassB();
        b.b(new Interface() {
            @Override
            public void backs(String n) {
                System.out.println("A:I am A Callback function! I print:" + n);
            }
        });
    }
}
public class ClassB {
    public void b(Interface in) {
        System.out.println("I executed b");
        System.out.println("B:I started calling A Callback-->");
        in.backs("<I am B Biographical Parameters");
        System.out.println("B: <--I complete the call A Callback");
    }
}

results of enforcement

Executed a Method
 I executed b
B:I started calling A Callback-->
A:I am A Callback function! I print: I am B Biographical Parameters
B: <--I complete the call A Callback

Version 4

Add a return parameter to the interface

public interface Interface {
    public String backs(String n);
}
public class ClassA {
    public void a() {
        System.out.println("Executed a Method");
        ClassB b = new ClassB();
        b.b(new Interface() {
            @Override
            public String backs(String n) {
                System.out.println("A:I am A Callback function! I print:" + n);
                return "A:I am A Callback function! I print:" + n + "Return of.";
            }
        });
    }
}
public class ClassB {
    public void b(Interface in) {
        System.out.println("I executed b");
        System.out.println("B:I started calling A Callback-->");
        String backs = in.backs("<I am B Biographical Parameters");
        System.out.println("B:I received the result of the callback:"+backs);
        System.out.println("B: <--I complete the call A Callback");
    }
}

results of enforcement

Executed a Method
 I executed b
B:I started calling A Callback-->
A:I am A Callback function! I print: I am B Biographical Parameters
B:I received the result of the callback:A:I am A Callback function! I print: I am B Return of passed parameter.
B: <--I complete the call A Callback

Version 5

public interface Interface {
    public String backs(String n);
}
public class ClassA {
    public void a() {
        System.out.println("Executed a Method");
        ClassB b = new ClassB();
        String b1 = b.b(new Interface() {
            @Override
            public String backs(String n) {
                System.out.println("A:I am A Callback function! I print:" + n);
                return "A:I am A Callback function! I print:" + n + "Return of.";
            }
        });
        System.out.println("A:Result after execution:" + b1);
    }
}
public class ClassB {
    public String b(Interface in) {
        System.out.println("I executed b");
        System.out.println("B:I started calling A Callback-->");
        String backs = in.backs("<I am B Biographical Parameters");
        System.out.println("B:I received the result of the callback:"+backs + "<--I complete the call A Callback");
        return "<I am B Return of";
    }
}

Execution results:

Executed a Method
 I executed b
B:I started calling A Callback-->
A:I am A Callback function! I print: I am B Biographical Parameters
B:I received the result of the callback:A:I am A Callback function! I print: I am B Return of passed parameter.<--I complete the call A Callback
A:Result after execution:<I am B Return of

Version 6

Declare the callback function before using it

public class ClassA {
    public void a() {
        System.out.println("Executed a Method");
        Interface in = (n -> {
            System.out.println("A: I use the callback interface directly, and the parameters I receive are:" + n);
            return "I am the data returned by the callback";
        });

        String backs = in.backs("I A,I'm " in>Users");
        System.out.println("backes:" + backs);
    }
}

call

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

results of enforcement

Executed a Method
A: I use the callback interface directly, and the parameters I receive are: A,I'm " in>Users
backes:I am the data returned by the callback

Return to the problem described in the preamble

1. MethodIntrospector.selectMethods()

Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
    // The interface requires a MetadataLookup type to do a type conversion.
    // T:Set<Scheduled>, this parameter is passed from the called class.
    // Look at the calling code, point method selectMethods in
    // The code inside is a callback metadataLookup.inspect (specific method);
    // 
	(MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
        // Create RequestMappingInfo using the method and type level RequestMapping annotations.
		Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
			method, Scheduled.class, Schedules.class);
		return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
	});

2. Abstract Class MethodIntrospector

Before that, look at the interface for this callback, which is an internal class in the MethodIntrospector class

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

3. Method 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;

    // Is it a proxy class
    if (!Proxy.isProxyClass(targetType)) {
        // Get this class from this class, check if it is a subclass of CGLIB, and return to the original class.
        specificHandlerType = ClassUtils.getUserClass(targetType);
        // Add to set hash table
        handlerTypes.add(specificHandlerType);
    }
    // Add all interface classes of this class to the set hash table
    handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

    for (Class<?> currentHandlerType : handlerTypes) {
        // This one doesn't know what to do. It doesn't use classes in loops. That is, to use the actual implementation class instead of the interface?
        final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

        // Huo, just before that callback function is finished, there's another one.
        // 3 parameters, 1-interface or implementation class 2-tuned function passed in 
        // 3-The pre-built MethodFilter matches all non-bridge non-composite methods not declared in java.lang.Object.
        ReflectionUtils.doWithMethods(currentHandlerType, method -> {
            // Find this method from this class
            Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
            // Call back up to the class that called this method to execute the corresponding method with it. The object/admin/test returned is RequestMappingInfo
            T result = metadataLookup.inspect(specificMethod);
            if (result != null) {
                // Find the original method of the bridge Method provided.
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                    methodMap.put(specificMethod, result);
                }
            }
        }, ReflectionUtils.USER_DECLARED_METHODS);
    }

    // <Method, MappingInfo>Map
    return methodMap;
	}

4. Member variable USER_DECLARED_METHODS

// Here is another use of callback functions, member variables in ReflectionUtils
public static final MethodFilter USER_DECLARED_METHODS =
    (method -> !method.isBridge() && !method.isSynthetic());

// Look at the callback interface, which is also an internal class
public abstract class ReflectionUtils {
    public interface MethodFilter {
        boolean matches(Method method);
    }
}
// Understanding: This is just that the callback inside is directly used without writing a call above to generate an object with only one interface inside, which is already implemented inside the interface
// Calling this is equivalent to calling this method.

5. Method doWithMethods()

Call doWithMethods on selectMethods. Performs a given callback operation on all matching methods of a given class and superclass (or given interface and superinterface).

public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
    // Keep backing up the inheritance hierarchy.
    // Cached, and all methods in this class
    Method[] methods = getDeclaredMethods(clazz, false);
    for (Method method : methods) {
        // The interface object obtained by using the callback function. Determine if it is not a bridge and not an executable is a composite structure and is not skipped
        if (mf != null && !mf.matches(method)) {
            // Is it a bridge or an executable skip
            continue;
        }
        try {
            // This is the callback that calls doWithMethods executed in the class that calls it, passing over the methods in this class
            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. The getMappingForMethod() method called in the doWithMethods() method

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // Provide an appropriate custom RequestCondition based on whether the provided annotatedElement is a class or a method.
    // Received is an AnnotatedElement type,
    // Method inherits Executable, Executable implements GenericDeclaration
    // GenericDeclaration inherits AnnotatedElement, as you can see
    // RequestMappingInfo on Create Method
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // RequestMappingInfo on the class where the method was created
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            // Combine two RequestMappingInfo
            info = typeInfo.combine(info);
        }
        String prefix = getPathPrefix(handlerType);
        if (prefix != null) {
            info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
        }
    }
    return info;
}

Look deeper

combination

RequestMappingInfo after combining

7. createRequestMappingInfo() called in getMappingForMethod() method

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    // Find the first @RequestMapping of this method
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    // Both methods are returned nulls and appear to be overridden. However, this startup does not return null directly, so condition is null
    RequestCondition<?> condition = (element instanceof Class ?
                                     getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    // Generate RequestMappingInfo object
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

Curious, what's find like

Actually how do I mark this method?

Call the construct to create 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();
}

Create long like this

afterword
So the final result is to put all the methods under this class labeling the RequestMapping annotation in Map with <Method, RequestMappingInfo>

Full text complete~

Literary blogs, learn and practice. Welcome to Correct~

Tags: Java Spring

Posted on Sat, 27 Nov 2021 13:31:18 -0500 by BrettCarr