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~