From fe56aa6fa47933509c3c5c8300d3e9a06c802dcc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Mar 2019 15:32:24 +0100 Subject: [PATCH] Polishing --- .../cache/interceptor/CacheAspectSupport.java | 12 +- .../interceptor/CacheOperationSource.java | 7 +- .../CompositeCacheOperationSource.java | 9 +- .../support/StandardScriptFactory.java | 5 +- .../org/springframework/lang/NonNull.java | 7 +- .../springframework/lang/NonNullFields.java | 4 +- .../org/springframework/lang/Nullable.java | 8 +- .../springframework/util/ReflectionUtils.java | 666 +++++++++--------- .../CompositeTransactionAttributeSource.java | 12 +- .../TransactionAttributeSource.java | 6 +- .../http/server/ServerHttpRequest.java | 8 +- 11 files changed, 382 insertions(+), 362 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java index f8156ab095..3c7d5fe999 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -313,9 +313,9 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker * @param expectedType type for the bean * @return the bean matching that name * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException if such bean does not exist - * @see CacheOperation#keyGenerator - * @see CacheOperation#cacheManager - * @see CacheOperation#cacheResolver + * @see CacheOperation#getKeyGenerator() + * @see CacheOperation#getCacheManager() + * @see CacheOperation#getCacheResolver() */ protected T getBean(String beanName, Class expectedType) { if (this.beanFactory == null) { @@ -353,8 +353,8 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker /** * Execute the underlying operation (typically in case of cache miss) and return - * the result of the invocation. If an exception occurs it will be wrapped in - * a {@link CacheOperationInvoker.ThrowableWrapper}: the exception can be handled + * the result of the invocation. If an exception occurs it will be wrapped in a + * {@link CacheOperationInvoker.ThrowableWrapper}: the exception can be handled * or modified but it must be wrapped in a * {@link CacheOperationInvoker.ThrowableWrapper} as well. * @param invoker the invoker handling the operation being cached diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java index 0ab1059325..47c8def776 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,13 +27,14 @@ import org.springframework.lang.Nullable; * source level, or elsewhere. * * @author Costin Leau + * @author Juergen Hoeller * @since 3.1 */ public interface CacheOperationSource { /** - * Return the collection of cache operations for this method, or {@code null} - * if the method contains no cacheable annotations. + * Return the collection of cache operations for this method, + * or {@code null} if the method contains no cacheable annotations. * @param method the method to introspect * @param targetClass the target class (may be {@code null}, in which case * the declaring class of the method must be used) diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CompositeCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CompositeCacheOperationSource.java index a5baa2cc43..a342234927 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CompositeCacheOperationSource.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CompositeCacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.springframework.util.Assert; * over a given array of {@code CacheOperationSource} instances. * * @author Costin Leau + * @author Juergen Hoeller * @since 3.1 */ @SuppressWarnings("serial") @@ -42,7 +43,7 @@ public class CompositeCacheOperationSource implements CacheOperationSource, Seri * @param cacheOperationSources the CacheOperationSource instances to combine */ public CompositeCacheOperationSource(CacheOperationSource... cacheOperationSources) { - Assert.notEmpty(cacheOperationSources, "cacheOperationSources array must not be empty"); + Assert.notEmpty(cacheOperationSources, "CacheOperationSource array must not be empty"); this.cacheOperationSources = cacheOperationSources; } @@ -54,21 +55,21 @@ public class CompositeCacheOperationSource implements CacheOperationSource, Seri return this.cacheOperationSources; } + @Override @Nullable public Collection getCacheOperations(Method method, @Nullable Class targetClass) { Collection ops = null; - for (CacheOperationSource source : this.cacheOperationSources) { Collection cacheOperations = source.getCacheOperations(method, targetClass); if (cacheOperations != null) { if (ops == null) { ops = new ArrayList<>(); } - ops.addAll(cacheOperations); } } return ops; } + } diff --git a/spring-context/src/main/java/org/springframework/scripting/support/StandardScriptFactory.java b/spring-context/src/main/java/org/springframework/scripting/support/StandardScriptFactory.java index 4c684b35b6..5420312ec1 100644 --- a/spring-context/src/main/java/org/springframework/scripting/support/StandardScriptFactory.java +++ b/spring-context/src/main/java/org/springframework/scripting/support/StandardScriptFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ import org.springframework.util.StringUtils; /** * {@link org.springframework.scripting.ScriptFactory} implementation based * on the JSR-223 script engine abstraction (as included in Java 6+). - * Supports JavaScript, Groovy, JRuby and other JSR-223 compliant engines. + * Supports JavaScript, Groovy, JRuby, and other JSR-223 compliant engines. * *

Typically used in combination with a * {@link org.springframework.scripting.support.ScriptFactoryPostProcessor}; @@ -151,6 +151,7 @@ public class StandardScriptFactory implements ScriptFactory, BeanClassLoaderAwar if (script instanceof Class ? !requestedIfc.isAssignableFrom((Class) script) : !requestedIfc.isInstance(script)) { adaptationRequired = true; + break; } } if (adaptationRequired) { diff --git a/spring-core/src/main/java/org/springframework/lang/NonNull.java b/spring-core/src/main/java/org/springframework/lang/NonNull.java index 264cb11e8c..20fdcfb80c 100644 --- a/spring-core/src/main/java/org/springframework/lang/NonNull.java +++ b/spring-core/src/main/java/org/springframework/lang/NonNull.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,9 @@ import javax.annotation.meta.TypeQualifierNickname; /** * A common Spring annotation to declare that annotated elements cannot be {@code null}. - * Leverages JSR 305 meta-annotations to indicate nullability in Java to common tools with - * JSR 305 support and used by Kotlin to infer nullability of Spring API. + * + *

Leverages JSR-305 meta-annotations to indicate nullability in Java to common + * tools with JSR-305 support and used by Kotlin to infer nullability of Spring API. * *

Should be used at parameter, return value, and field level. Method overrides should * repeat parent {@code @NonNull} annotations unless they behave differently. diff --git a/spring-core/src/main/java/org/springframework/lang/NonNullFields.java b/spring-core/src/main/java/org/springframework/lang/NonNullFields.java index 3c9d39ddc2..ac431e5f16 100644 --- a/spring-core/src/main/java/org/springframework/lang/NonNullFields.java +++ b/spring-core/src/main/java/org/springframework/lang/NonNullFields.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ import javax.annotation.meta.TypeQualifierDefault; * * @author Sebastien Deleuze * @since 5.0 - * @see NonNullFields + * @see NonNullApi * @see Nullable * @see NonNull */ diff --git a/spring-core/src/main/java/org/springframework/lang/Nullable.java b/spring-core/src/main/java/org/springframework/lang/Nullable.java index 1c343cd4d2..6b5ca26ff8 100644 --- a/spring-core/src/main/java/org/springframework/lang/Nullable.java +++ b/spring-core/src/main/java/org/springframework/lang/Nullable.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,10 @@ import javax.annotation.meta.When; /** * A common Spring annotation to declare that annotated elements can be {@code null} under - * some circumstance. Leverages JSR 305 meta-annotations to indicate nullability in Java - * to common tools with JSR 305 support and used by Kotlin to infer nullability of Spring API. + * some circumstance. + * + *

Leverages JSR-305 meta-annotations to indicate nullability in Java to common + * tools with JSR-305 support and used by Kotlin to infer nullability of Spring API. * *

Should be used at parameter, return value, and field level. Methods override should * repeat parent {@code @Nullable} annotations unless they behave differently. diff --git a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java index e329fecfc1..83e10dc9d0 100644 --- a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,13 +61,13 @@ public abstract class ReflectionUtils { * @since 3.0.5 */ public static final MethodFilter USER_DECLARED_METHODS = - (method -> (!method.isBridge() && !method.isSynthetic() && method.getDeclaringClass() != Object.class)); + (method -> !method.isBridge() && !method.isSynthetic() && method.getDeclaringClass() != Object.class); /** * Pre-built FieldFilter that matches all non-static, non-final fields. */ public static final FieldFilter COPYABLE_FIELDS = - field -> !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())); + (field -> !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()))); /** @@ -76,9 +76,9 @@ public abstract class ReflectionUtils { */ private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$"; - private static final Method[] NO_METHODS = {}; + private static final Method[] EMPTY_METHOD_ARRAY = new Method[0]; - private static final Field[] NO_FIELDS = {}; + private static final Field[] EMPTY_FIELD_ARRAY = new Field[0]; /** @@ -93,209 +93,7 @@ public abstract class ReflectionUtils { private static final Map, Field[]> declaredFieldsCache = new ConcurrentReferenceHashMap<>(256); - /** - * Attempt to find a {@link Field field} on the supplied {@link Class} with the - * supplied {@code name}. Searches all superclasses up to {@link Object}. - * @param clazz the class to introspect - * @param name the name of the field - * @return the corresponding Field object, or {@code null} if not found - */ - @Nullable - public static Field findField(Class clazz, String name) { - return findField(clazz, name, null); - } - - /** - * Attempt to find a {@link Field field} on the supplied {@link Class} with the - * supplied {@code name} and/or {@link Class type}. Searches all superclasses - * up to {@link Object}. - * @param clazz the class to introspect - * @param name the name of the field (may be {@code null} if type is specified) - * @param type the type of the field (may be {@code null} if name is specified) - * @return the corresponding Field object, or {@code null} if not found - */ - @Nullable - public static Field findField(Class clazz, @Nullable String name, @Nullable Class type) { - Assert.notNull(clazz, "Class must not be null"); - Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified"); - 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; - } - - /** - * Set the field represented by the supplied {@link Field field object} on the - * specified {@link Object target object} to the specified {@code value}. - * In accordance with {@link Field#set(Object, Object)} semantics, the new value - * is automatically unwrapped if the underlying field has a primitive type. - *

Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. - * @param field the field to set - * @param target the target object on which to set the field - * @param value the value to set (may be {@code null}) - */ - public static void setField(Field field, @Nullable Object target, @Nullable Object value) { - try { - field.set(target, value); - } - catch (IllegalAccessException ex) { - handleReflectionException(ex); - throw new IllegalStateException( - "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); - } - } - - /** - * Get the field represented by the supplied {@link Field field object} on the - * specified {@link Object target object}. In accordance with {@link Field#get(Object)} - * semantics, the returned value is automatically wrapped if the underlying field - * has a primitive type. - *

Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. - * @param field the field to get - * @param target the target object from which to get the field - * @return the field's current value - */ - @Nullable - public static Object getField(Field field, @Nullable Object target) { - try { - return field.get(target); - } - catch (IllegalAccessException ex) { - handleReflectionException(ex); - throw new IllegalStateException( - "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); - } - } - - /** - * Attempt to find a {@link Method} on the supplied class with the supplied name - * and no parameters. Searches all superclasses up to {@code Object}. - *

Returns {@code null} if no {@link Method} can be found. - * @param clazz the class to introspect - * @param name the name of the method - * @return the Method object, or {@code null} if none found - */ - @Nullable - public static Method findMethod(Class clazz, String name) { - return findMethod(clazz, name, new Class[0]); - } - - /** - * Attempt to find a {@link Method} on the supplied class with the supplied name - * and parameter types. Searches all superclasses up to {@code Object}. - *

Returns {@code null} if no {@link Method} can be found. - * @param clazz the class to introspect - * @param name the name of the method - * @param paramTypes the parameter types of the method - * (may be {@code null} to indicate any signature) - * @return the Method object, or {@code null} if none found - */ - @Nullable - public static Method findMethod(Class clazz, String name, @Nullable Class... paramTypes) { - Assert.notNull(clazz, "Class must not be null"); - Assert.notNull(name, "Method name must not be null"); - 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; - } - - /** - * Invoke the specified {@link Method} against the supplied target object with no arguments. - * The target object can be {@code null} when invoking a static {@link Method}. - *

Thrown exceptions are handled via a call to {@link #handleReflectionException}. - * @param method the method to invoke - * @param target the target object to invoke the method on - * @return the invocation result, if any - * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) - */ - @Nullable - public static Object invokeMethod(Method method, @Nullable Object target) { - return invokeMethod(method, target, new Object[0]); - } - - /** - * Invoke the specified {@link Method} against the supplied target object with the - * supplied arguments. The target object can be {@code null} when invoking a - * static {@link Method}. - *

Thrown exceptions are handled via a call to {@link #handleReflectionException}. - * @param method the method to invoke - * @param target the target object to invoke the method on - * @param args the invocation arguments (may be {@code null}) - * @return the invocation result, if any - */ - @Nullable - public static Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args) { - try { - return method.invoke(target, args); - } - catch (Exception ex) { - handleReflectionException(ex); - } - throw new IllegalStateException("Should never get here"); - } - - /** - * Invoke the specified JDBC API {@link Method} against the supplied target - * object with no arguments. - * @param method the method to invoke - * @param target the target object to invoke the method on - * @return the invocation result, if any - * @throws SQLException the JDBC API SQLException to rethrow (if any) - * @see #invokeJdbcMethod(java.lang.reflect.Method, Object, Object[]) - * @deprecated as of 5.0.11, in favor of custom SQLException handling - */ - @Deprecated - @Nullable - public static Object invokeJdbcMethod(Method method, @Nullable Object target) throws SQLException { - return invokeJdbcMethod(method, target, new Object[0]); - } - - /** - * Invoke the specified JDBC API {@link Method} against the supplied target - * object with the supplied arguments. - * @param method the method to invoke - * @param target the target object to invoke the method on - * @param args the invocation arguments (may be {@code null}) - * @return the invocation result, if any - * @throws SQLException the JDBC API SQLException to rethrow (if any) - * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) - * @deprecated as of 5.0.11, in favor of custom SQLException handling - */ - @Deprecated - @Nullable - public static Object invokeJdbcMethod(Method method, @Nullable Object target, @Nullable Object... args) - throws SQLException { - try { - return method.invoke(target, args); - } - catch (IllegalAccessException ex) { - handleReflectionException(ex); - } - catch (InvocationTargetException ex) { - if (ex.getTargetException() instanceof SQLException) { - throw (SQLException) ex.getTargetException(); - } - handleInvocationTargetException(ex); - } - throw new IllegalStateException("Should never get here"); - } + // Exception handling /** * Handle the given reflection exception. Should only be called if no @@ -375,161 +173,184 @@ public abstract class ReflectionUtils { throw new UndeclaredThrowableException(ex); } - /** - * Determine whether the given method explicitly declares the given - * exception or one of its superclasses, which means that an exception - * of that type can be propagated as-is within a reflective invocation. - * @param method the declaring method - * @param exceptionType the exception to throw - * @return {@code true} if the exception can be thrown as-is; - * {@code false} if it needs to be wrapped - */ - public static boolean declaresException(Method method, Class exceptionType) { - Assert.notNull(method, "Method must not be null"); - Class[] declaredExceptions = method.getExceptionTypes(); - for (Class declaredException : declaredExceptions) { - if (declaredException.isAssignableFrom(exceptionType)) { - return true; - } - } - return false; - } + + // Constructor handling /** - * Determine whether the given field is a "public static final" constant. - * @param field the field to check + * Obtain an accessible constructor for the given class and parameters. + * @param clazz the clazz to check + * @param parameterTypes the parameter types of the desired constructor + * @return the constructor reference + * @throws NoSuchMethodException if no such constructor exists + * @since 5.0 */ - public static boolean isPublicStaticFinal(Field field) { - int modifiers = field.getModifiers(); - return (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)); + public static Constructor accessibleConstructor(Class clazz, Class... parameterTypes) + throws NoSuchMethodException { + + Constructor ctor = clazz.getDeclaredConstructor(parameterTypes); + makeAccessible(ctor); + return ctor; } /** - * Determine whether the given method is an "equals" method. - * @see java.lang.Object#equals(Object) + * Make the given constructor accessible, explicitly setting it accessible + * if necessary. The {@code setAccessible(true)} method is only called + * when actually necessary, to avoid unnecessary conflicts with a JVM + * SecurityManager (if active). + * @param ctor the constructor to make accessible + * @see java.lang.reflect.Constructor#setAccessible */ - public static boolean isEqualsMethod(@Nullable Method method) { - if (method == null || !method.getName().equals("equals")) { - return false; + @SuppressWarnings("deprecation") // on JDK 9 + public static void makeAccessible(Constructor ctor) { + if ((!Modifier.isPublic(ctor.getModifiers()) || + !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) { + ctor.setAccessible(true); } - Class[] paramTypes = method.getParameterTypes(); - return (paramTypes.length == 1 && paramTypes[0] == Object.class); } - /** - * Determine whether the given method is a "hashCode" method. - * @see java.lang.Object#hashCode() - */ - public static boolean isHashCodeMethod(@Nullable Method method) { - return (method != null && method.getName().equals("hashCode") && method.getParameterCount() == 0); - } + + // Method handling /** - * Determine whether the given method is a "toString" method. - * @see java.lang.Object#toString() + * Attempt to find a {@link Method} on the supplied class with the supplied name + * and no parameters. Searches all superclasses up to {@code Object}. + *

Returns {@code null} if no {@link Method} can be found. + * @param clazz the class to introspect + * @param name the name of the method + * @return the Method object, or {@code null} if none found */ - public static boolean isToStringMethod(@Nullable Method method) { - return (method != null && method.getName().equals("toString") && method.getParameterCount() == 0); + @Nullable + public static Method findMethod(Class clazz, String name) { + return findMethod(clazz, name, new Class[0]); } /** - * Determine whether the given method is originally declared by {@link java.lang.Object}. + * Attempt to find a {@link Method} on the supplied class with the supplied name + * and parameter types. Searches all superclasses up to {@code Object}. + *

Returns {@code null} if no {@link Method} can be found. + * @param clazz the class to introspect + * @param name the name of the method + * @param paramTypes the parameter types of the method + * (may be {@code null} to indicate any signature) + * @return the Method object, or {@code null} if none found */ - public static boolean isObjectMethod(@Nullable Method method) { - if (method == null) { - return false; - } - try { - Object.class.getDeclaredMethod(method.getName(), method.getParameterTypes()); - return true; - } - catch (Exception ex) { - return false; + @Nullable + public static Method findMethod(Class clazz, String name, @Nullable Class... paramTypes) { + Assert.notNull(clazz, "Class must not be null"); + Assert.notNull(name, "Method name must not be null"); + 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; } /** - * Determine whether the given method is a CGLIB 'renamed' method, - * following the pattern "CGLIB$methodName$0". - * @param renamedMethod the method to check - * @see org.springframework.cglib.proxy.Enhancer#rename + * Invoke the specified {@link Method} against the supplied target object with no arguments. + * The target object can be {@code null} when invoking a static {@link Method}. + *

Thrown exceptions are handled via a call to {@link #handleReflectionException}. + * @param method the method to invoke + * @param target the target object to invoke the method on + * @return the invocation result, if any + * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) */ - public static boolean isCglibRenamedMethod(Method renamedMethod) { - String name = renamedMethod.getName(); - if (name.startsWith(CGLIB_RENAMED_METHOD_PREFIX)) { - int i = name.length() - 1; - while (i >= 0 && Character.isDigit(name.charAt(i))) { - i--; - } - return ((i > CGLIB_RENAMED_METHOD_PREFIX.length()) && - (i < name.length() - 1) && name.charAt(i) == '$'); - } - return false; + @Nullable + public static Object invokeMethod(Method method, @Nullable Object target) { + return invokeMethod(method, target, new Object[0]); } /** - * Make the given field accessible, explicitly setting it accessible if - * necessary. The {@code setAccessible(true)} method is only called - * when actually necessary, to avoid unnecessary conflicts with a JVM - * SecurityManager (if active). - * @param field the field to make accessible - * @see java.lang.reflect.Field#setAccessible + * Invoke the specified {@link Method} against the supplied target object with the + * supplied arguments. The target object can be {@code null} when invoking a + * static {@link Method}. + *

Thrown exceptions are handled via a call to {@link #handleReflectionException}. + * @param method the method to invoke + * @param target the target object to invoke the method on + * @param args the invocation arguments (may be {@code null}) + * @return the invocation result, if any */ - @SuppressWarnings("deprecation") // on JDK 9 - public static void makeAccessible(Field field) { - if ((!Modifier.isPublic(field.getModifiers()) || - !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || - Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { - field.setAccessible(true); + @Nullable + public static Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args) { + try { + return method.invoke(target, args); } + catch (Exception ex) { + handleReflectionException(ex); + } + throw new IllegalStateException("Should never get here"); } /** - * Make the given method accessible, explicitly setting it accessible if - * necessary. The {@code setAccessible(true)} method is only called - * when actually necessary, to avoid unnecessary conflicts with a JVM - * SecurityManager (if active). - * @param method the method to make accessible - * @see java.lang.reflect.Method#setAccessible + * Invoke the specified JDBC API {@link Method} against the supplied target + * object with no arguments. + * @param method the method to invoke + * @param target the target object to invoke the method on + * @return the invocation result, if any + * @throws SQLException the JDBC API SQLException to rethrow (if any) + * @see #invokeJdbcMethod(java.lang.reflect.Method, Object, Object[]) + * @deprecated as of 5.0.11, in favor of custom SQLException handling */ - @SuppressWarnings("deprecation") // on JDK 9 - public static void makeAccessible(Method method) { - if ((!Modifier.isPublic(method.getModifiers()) || - !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { - method.setAccessible(true); - } + @Deprecated + @Nullable + public static Object invokeJdbcMethod(Method method, @Nullable Object target) throws SQLException { + return invokeJdbcMethod(method, target, new Object[0]); } /** - * Make the given constructor accessible, explicitly setting it accessible - * if necessary. The {@code setAccessible(true)} method is only called - * when actually necessary, to avoid unnecessary conflicts with a JVM - * SecurityManager (if active). - * @param ctor the constructor to make accessible - * @see java.lang.reflect.Constructor#setAccessible + * Invoke the specified JDBC API {@link Method} against the supplied target + * object with the supplied arguments. + * @param method the method to invoke + * @param target the target object to invoke the method on + * @param args the invocation arguments (may be {@code null}) + * @return the invocation result, if any + * @throws SQLException the JDBC API SQLException to rethrow (if any) + * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) + * @deprecated as of 5.0.11, in favor of custom SQLException handling */ - @SuppressWarnings("deprecation") // on JDK 9 - public static void makeAccessible(Constructor ctor) { - if ((!Modifier.isPublic(ctor.getModifiers()) || - !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) { - ctor.setAccessible(true); + @Deprecated + @Nullable + public static Object invokeJdbcMethod(Method method, @Nullable Object target, @Nullable Object... args) + throws SQLException { + try { + return method.invoke(target, args); + } + catch (IllegalAccessException ex) { + handleReflectionException(ex); + } + catch (InvocationTargetException ex) { + if (ex.getTargetException() instanceof SQLException) { + throw (SQLException) ex.getTargetException(); + } + handleInvocationTargetException(ex); } + throw new IllegalStateException("Should never get here"); } /** - * Obtain an accessible constructor for the given class and parameters. - * @param clazz the clazz to check - * @param parameterTypes the parameter types of the desired constructor - * @return the constructor reference - * @throws NoSuchMethodException if no such constructor exists - * @since 5.0 + * Determine whether the given method explicitly declares the given + * exception or one of its superclasses, which means that an exception + * of that type can be propagated as-is within a reflective invocation. + * @param method the declaring method + * @param exceptionType the exception to throw + * @return {@code true} if the exception can be thrown as-is; + * {@code false} if it needs to be wrapped */ - public static Constructor accessibleConstructor(Class clazz, Class... parameterTypes) - throws NoSuchMethodException { - - Constructor ctor = clazz.getDeclaredConstructor(parameterTypes); - makeAccessible(ctor); - return ctor; + public static boolean declaresException(Method method, Class exceptionType) { + Assert.notNull(method, "Method must not be null"); + Class[] declaredExceptions = method.getExceptionTypes(); + for (Class declaredException : declaredExceptions) { + if (declaredException.isAssignableFrom(exceptionType)) { + return true; + } + } + return false; } /** @@ -611,7 +432,7 @@ public abstract class ReflectionUtils { public static Method[] getAllDeclaredMethods(Class leafClass) { final List methods = new ArrayList<>(32); doWithMethods(leafClass, methods::add); - return methods.toArray(new Method[0]); + return methods.toArray(EMPTY_METHOD_ARRAY); } /** @@ -647,7 +468,7 @@ public abstract class ReflectionUtils { methods.add(method); } }); - return methods.toArray(new Method[0]); + return methods.toArray(EMPTY_METHOD_ARRAY); } /** @@ -679,7 +500,7 @@ public abstract class ReflectionUtils { else { result = declaredMethods; } - declaredMethodsCache.put(clazz, (result.length == 0 ? NO_METHODS : result)); + declaredMethodsCache.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result)); } catch (Throwable ex) { throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() + @@ -705,6 +526,168 @@ public abstract class ReflectionUtils { return result; } + /** + * Determine whether the given method is an "equals" method. + * @see java.lang.Object#equals(Object) + */ + public static boolean isEqualsMethod(@Nullable Method method) { + if (method == null || !method.getName().equals("equals")) { + return false; + } + Class[] paramTypes = method.getParameterTypes(); + return (paramTypes.length == 1 && paramTypes[0] == Object.class); + } + + /** + * Determine whether the given method is a "hashCode" method. + * @see java.lang.Object#hashCode() + */ + public static boolean isHashCodeMethod(@Nullable Method method) { + return (method != null && method.getName().equals("hashCode") && method.getParameterCount() == 0); + } + + /** + * Determine whether the given method is a "toString" method. + * @see java.lang.Object#toString() + */ + public static boolean isToStringMethod(@Nullable Method method) { + return (method != null && method.getName().equals("toString") && method.getParameterCount() == 0); + } + + /** + * Determine whether the given method is originally declared by {@link java.lang.Object}. + */ + public static boolean isObjectMethod(@Nullable Method method) { + if (method == null) { + return false; + } + try { + Object.class.getDeclaredMethod(method.getName(), method.getParameterTypes()); + return true; + } + catch (Exception ex) { + return false; + } + } + + /** + * Determine whether the given method is a CGLIB 'renamed' method, + * following the pattern "CGLIB$methodName$0". + * @param renamedMethod the method to check + */ + public static boolean isCglibRenamedMethod(Method renamedMethod) { + String name = renamedMethod.getName(); + if (name.startsWith(CGLIB_RENAMED_METHOD_PREFIX)) { + int i = name.length() - 1; + while (i >= 0 && Character.isDigit(name.charAt(i))) { + i--; + } + return (i > CGLIB_RENAMED_METHOD_PREFIX.length() && (i < name.length() - 1) && name.charAt(i) == '$'); + } + return false; + } + + /** + * Make the given method accessible, explicitly setting it accessible if + * necessary. The {@code setAccessible(true)} method is only called + * when actually necessary, to avoid unnecessary conflicts with a JVM + * SecurityManager (if active). + * @param method the method to make accessible + * @see java.lang.reflect.Method#setAccessible + */ + @SuppressWarnings("deprecation") // on JDK 9 + public static void makeAccessible(Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || + !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { + method.setAccessible(true); + } + } + + + // Field handling + + /** + * Attempt to find a {@link Field field} on the supplied {@link Class} with the + * supplied {@code name}. Searches all superclasses up to {@link Object}. + * @param clazz the class to introspect + * @param name the name of the field + * @return the corresponding Field object, or {@code null} if not found + */ + @Nullable + public static Field findField(Class clazz, String name) { + return findField(clazz, name, null); + } + + /** + * Attempt to find a {@link Field field} on the supplied {@link Class} with the + * supplied {@code name} and/or {@link Class type}. Searches all superclasses + * up to {@link Object}. + * @param clazz the class to introspect + * @param name the name of the field (may be {@code null} if type is specified) + * @param type the type of the field (may be {@code null} if name is specified) + * @return the corresponding Field object, or {@code null} if not found + */ + @Nullable + public static Field findField(Class clazz, @Nullable String name, @Nullable Class type) { + Assert.notNull(clazz, "Class must not be null"); + Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified"); + 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; + } + + /** + * Set the field represented by the supplied {@link Field field object} on the + * specified {@link Object target object} to the specified {@code value}. + * In accordance with {@link Field#set(Object, Object)} semantics, the new value + * is automatically unwrapped if the underlying field has a primitive type. + *

Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. + * @param field the field to set + * @param target the target object on which to set the field + * @param value the value to set (may be {@code null}) + */ + public static void setField(Field field, @Nullable Object target, @Nullable Object value) { + try { + field.set(target, value); + } + catch (IllegalAccessException ex) { + handleReflectionException(ex); + throw new IllegalStateException( + "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); + } + } + + /** + * Get the field represented by the supplied {@link Field field object} on the + * specified {@link Object target object}. In accordance with {@link Field#get(Object)} + * semantics, the returned value is automatically wrapped if the underlying field + * has a primitive type. + *

Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. + * @param field the field to get + * @param target the target object from which to get the field + * @return the field's current value + */ + @Nullable + public static Object getField(Field field, @Nullable Object target) { + try { + return field.get(target); + } + catch (IllegalAccessException ex) { + handleReflectionException(ex); + throw new IllegalStateException( + "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); + } + } + /** * Invoke the given callback on all locally declared fields in the given class. * @param clazz the target class to analyze @@ -778,7 +761,7 @@ public abstract class ReflectionUtils { if (result == null) { try { result = clazz.getDeclaredFields(); - declaredFieldsCache.put(clazz, (result.length == 0 ? NO_FIELDS : result)); + declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result)); } catch (Throwable ex) { throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() + @@ -808,6 +791,35 @@ public abstract class ReflectionUtils { }, COPYABLE_FIELDS); } + /** + * Determine whether the given field is a "public static final" constant. + * @param field the field to check + */ + public static boolean isPublicStaticFinal(Field field) { + int modifiers = field.getModifiers(); + return (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)); + } + + /** + * Make the given field accessible, explicitly setting it accessible if + * necessary. The {@code setAccessible(true)} method is only called + * when actually necessary, to avoid unnecessary conflicts with a JVM + * SecurityManager (if active). + * @param field the field to make accessible + * @see java.lang.reflect.Field#setAccessible + */ + @SuppressWarnings("deprecation") // on JDK 9 + public static void makeAccessible(Field field) { + if ((!Modifier.isPublic(field.getModifiers()) || + !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || + Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { + field.setAccessible(true); + } + } + + + // Cache handling + /** * Clear the internal method/field cache. * @since 4.2.4 diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/CompositeTransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/CompositeTransactionAttributeSource.java index 521b2eca00..84f1fe04c6 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/CompositeTransactionAttributeSource.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/CompositeTransactionAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ public class CompositeTransactionAttributeSource implements TransactionAttribute * Create a new CompositeTransactionAttributeSource for the given sources. * @param transactionAttributeSources the TransactionAttributeSource instances to combine */ - public CompositeTransactionAttributeSource(TransactionAttributeSource[] transactionAttributeSources) { + public CompositeTransactionAttributeSource(TransactionAttributeSource... transactionAttributeSources) { Assert.notNull(transactionAttributeSources, "TransactionAttributeSource array must not be null"); this.transactionAttributeSources = transactionAttributeSources; } @@ -56,10 +56,10 @@ public class CompositeTransactionAttributeSource implements TransactionAttribute @Override @Nullable public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class targetClass) { - for (TransactionAttributeSource tas : this.transactionAttributeSources) { - TransactionAttribute ta = tas.getTransactionAttribute(method, targetClass); - if (ta != null) { - return ta; + for (TransactionAttributeSource source : this.transactionAttributeSources) { + TransactionAttribute attr = source.getTransactionAttribute(method, targetClass); + if (attr != null) { + return attr; } } return null; diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java index 3b384ab6e3..1660223989 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import org.springframework.lang.Nullable; * metadata attributes at source level (such as Java 5 annotations), or anywhere else. * * @author Rod Johnson + * @author Juergen Hoeller * @since 15.04.2003 * @see TransactionInterceptor#setTransactionAttributeSource * @see TransactionProxyFactoryBean#setTransactionAttributeSource @@ -40,8 +41,7 @@ public interface TransactionAttributeSource { * @param method the method to introspect * @param targetClass the target class (may be {@code null}, * in which case the declaring class of the method must be used) - * @return the TransactionAttribute the matching transaction attribute, - * or {@code null} if none found + * @return the matching transaction attribute, or {@code null} if none found */ @Nullable TransactionAttribute getTransactionAttribute(Method method, @Nullable Class targetClass); diff --git a/spring-web/src/main/java/org/springframework/http/server/ServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/ServerHttpRequest.java index 8856452613..3c4dbac78c 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServerHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.security.Principal; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpRequest; +import org.springframework.lang.Nullable; /** * Represents a server-side HTTP request. @@ -33,9 +34,10 @@ public interface ServerHttpRequest extends HttpRequest, HttpInputMessage { /** * Return a {@link java.security.Principal} instance containing the name of the - * authenticated user. If the user has not been authenticated, the method returns - * null. + * authenticated user. + *

If the user has not been authenticated, the method returns null. */ + @Nullable Principal getPrincipal(); /** -- GitLab