In this section, let's take a look at the tool class provided by Spring for reflection: ReflectionUtils. Reflection is used very frequently in containers. ReflectionUtils also provides useful methods. Let's take a look.
ReflectionUtils provides some methods for dealing with exceptions in reflection. These methods are generally used within the Spring framework. Of course, for the sake of specification, we can also use these methods when we are involved in the development of reflected exceptions. We look at the code according to the call chain of these methods:
void handleReflectionException(Exception ex)
To handle exceptions in reflection, let's look directly at the code:
public static void handleReflectionException(Exception ex) { if (ex instanceof NoSuchMethodException) { throw new IllegalStateException("Method not found: " + ex.getMessage()); } if (ex instanceof IllegalAccessException) { throw new IllegalStateException("Could not access method: " + ex.getMessage()); } if (ex instanceof InvocationTargetException) { handleInvocationTargetException((InvocationTargetException) ex); } if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } throw new UndeclaredThrowableException(ex); }
It can be seen that the code directly packages the check exceptions in the NoSuchMethodException, IllegalAccessException, InvocationTargetException and other reflections as the corresponding runtime Exception; Here we can first look at the exceptions in reflection:
image.png
You can see that all exceptions in reflection inherit ReflectiveOperationException (reflection operation Exception), and this Exception inherits Exception; There are also specific reflection operation exceptions such as ClassNotFoundException;
In the method, if an InvocationTargetException exception is encountered, it should be handled by the handleInvocationTargetException method:
public static void rethrowRuntimeException(Throwable ex) { if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } if (ex instanceof Error) { throw (Error) ex; } throw new UndeclaredThrowableException(ex); }
It can be seen that this method only judges runtime exceptions and errors; And throw it out as it is; How to understand the calling of this method? The reason is very simple. InvocationTargetException is in method When invoking, the method itself may throw RuntimeException or Error during its execution. Exceptions thrown by other methods are directly packaged as UndeclaredThrowableException (RuntimeException);
Unified, it can be understood that except for errors encountered during reflection execution, all other exceptions are uniformly converted to runtimeexceptions;
Next, enter the normal method
Field findField(Class<?> clazz, String name, Class<?> type)
Query a field by type, field name and field type; This method will traverse to query the fields from the parent class, and all the fields will be queried; We can take a brief look at the implementation:
public static Field findField(Class<?> clazz, String name, Class<?> type) { Class<?> searchType = clazz; while (Object.class != searchType && searchType != null) { Field[] fields = getDeclaredFields(searchType); for (Field field : fields) { if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) { return field; } } searchType = searchType.getSuperclass(); } return null; }
The code implementation is relatively simple. You can query the fields up to the Object type; Notice that a code is called:
Field[] fields = getDeclaredFields(searchType);
Let's take a look at the code implementation:
private static Field[] getDeclaredFields(Class<?> clazz) { Field[] result = declaredFieldsCache.get(clazz); if (result == null) { result = clazz.getDeclaredFields(); declaredFieldsCache.put(clazz, (result.length == 0 ? NO_FIELDS : result)); } return result; }
You can see that in fact, in this tool class, types and fields are cached and saved in declaredFieldsCache. Let's take a look at the declaration of this cache:
private static final Map<Class<?>, Field[]> declaredFieldsCache = new ConcurrentReferenceHashMap<Class<?>, Field[]>(256);
Because it is a tool class, it can be understood that it is declared as ConcurrentReferenceHashMap;
This caching mechanism also exists in method queries;
There is also a simple version of this method:
Field findField(Class<?> clazz, String name)
void setField(Field field, Object target, Object value)
Set the specified value for the specified field in the specified object (target);
*Object getField(Field field, Object target) *
Get the value of the specified field on the specified object (target);
The implementation of the above two methods is very simple, calling field Set and field Get method; The corresponding exceptions are handled; Select an implementation to see:
public static void setField(Field field, Object target, Object value) { try { field.set(target, value); } catch (IllegalAccessException ex) { handleReflectionException(ex); throw new IllegalStateException( "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); } }
handleReflectionException method is used to handle exceptions uniformly;
*Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) *
On the type clazz, query the name method, and the parameter type list is paramTypes;
public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { Class<?> searchType = clazz; while (searchType != null) { Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType)); for (Method method : methods) { if (name.equals(method.getName()) && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { return method; } } searchType = searchType.getSuperclass(); } return null; }
As you can see, the method can still be recursively queried upward, and all the methods are queried.
There is also a simple version of this method:
Method findMethod(Class<?> clazz, String name)
Object invokeMethod(Method method, Object target, Object... args)
On the specified object (target), execute the method using the specified parameter (args);
The implementation of this method is also very simple:
public static Object invokeMethod(Method method, Object target, Object... args) { try { return method.invoke(target, args); } catch (Exception ex) { handleReflectionException(ex); } throw new IllegalStateException("Should never get here"); }
As you can see, only method Invoke method, and uniformly handle the call exceptions;
There is also a simple version of this method:
Object invokeMethod(Method method, Object target)
boolean declaresException(Method method, Class<?> exceptionType)
Judge whether an exception of the specified type is declared on a method;
boolean isPublicStaticFinal(Field field)
Judge whether the field is public static final;
boolean isEqualsMethod(Method method)
Judge whether the method is the equals method;
boolean isHashCodeMethod(Method method)
Determine whether the method is a hashcode method;
boolean isToStringMethod(Method method)
Determine whether the method is a toString method;
Some children may remember that there are these isXXX methods in AopUtils. Yes, in fact, the isXXX methods in AopUtils are the methods of ReflectionUtils that are called;
boolean isObjectMethod(Method method)
Judge whether the method is a method on the Object class;
void makeAccessible(Field field)
Set a field to be readable / writable, mainly for the private field;
void makeAccessible(Method method)
Set a method to be callable, mainly for private methods;
void makeAccessible(Constructor<?> ctor)
Set a constructor to be callable, mainly for private constructors;
void doWithLocalMethods(Class<?> clazz, MethodCallback mc)
Call MethodCallback callback successively for all methods on the specified type;
First, let's take a look at the MethodCallback interface declaration:
public interface MethodCallback { /** * Use the specified method to complete some operations */ void doWith(Method method) throws IllegalArgumentException, IllegalAccessException; }
In fact, it is a normal callback interface; Take a look at the doWithLocalMethods implementation:
public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) { Method[] methods = getDeclaredMethods(clazz); for (Method method : methods) { try { mc.doWith(method); }catch (IllegalAccessException ex) { throw new IllegalStateException("..."); } } }
In fact, the implementation is very simple, that is, get all the methods on the class, and then execute the callback interface; This method is widely used in the label processing of Spring methods for bean s, such as @Init, @Resource, @Autowire and other label preprocessing;
This method has an enhanced version:
void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf)
This version provides a method matching (filter) MethodFilter;
Let's take a look at the interface declaration of MethodFilter:
public interface MethodFilter { /** * Check whether a specified method matches the rule */ boolean matches(Method method); }
This interface declares a matching method for matching rules;
Back to the implementation of the doWithMethods method:
public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) { // Keep backing up the inheritance hierarchy. Method[] methods = getDeclaredMethods(clazz); for (Method method : methods) { if (mf != null && !mf.matches(method)) { continue; } try { mc.doWith(method); }catch (IllegalAccessException ex) { throw new IllegalStateException("..."); } } if (clazz.getSuperclass() != null) { doWithMethods(clazz.getSuperclass(), mc, mf); }else if (clazz.isInterface()) { for (Class<?> superIfc : clazz.getInterfaces()) { doWithMethods(superIfc, mc, mf); } } }
The implementation of this method is very clear. First, get all the methods on the class. For each method, call MethodFilter to check the matching. If it matches, call the MethodCallback callback method. This method will recursively query and process all methods on all parent classes and implemented interfaces;
void doWithLocalFields(Class<?> clazz, FieldCallback fc)
Obviously, this method is the corresponding callback for all fields. The FieldCallback here is similar to the previous MethodCallback:
public interface FieldCallback { /** * Perform operations on the specified fields; */ void doWith(Field field) throws IllegalArgumentException, IllegalAccessException; }
The implementation of this method is similar to that of doWithLocalMethods:
public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) { for (Field field : getDeclaredFields(clazz)) { try { fc.doWith(field); }catch (IllegalAccessException ex) { throw new IllegalStateException("..."); } } }
Get all the fields on the class and execute the callback; Similarly, this method is mainly used in Spring to preprocess the @Autowire or @Resource tags on the fields;
void doWithFields(Class<?> clazz, FieldCallback fc, FieldFilter ff)
Like the enhanced version of doWithMethods, it also provides a functional method with field matching (filtering) for fields; Let's simply look at the FieldFilter implementation:
public interface FieldFilter { /** * Check whether the given fields match; */ boolean matches(Field field); }
Summary
In general, the functions provided by ReflectionUtils are fairly complete. In fact, it is not difficult to implement such a tool class. It is more recommended that you take a look at the implementation of Spring and still gain a lot. Of course, here we are looking at Spring4 X code, I believe that it will be more elegant to completely use Java8 code in Spring5.
Author: Ding Lang Education
Link: https://www.jianshu.com/p/fba68ec120b2
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.