mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-18 00:49:02 +00:00
Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
445e8ea84a | ||
|
|
df60919bb0 | ||
|
|
2f1f36eeda | ||
|
|
1c0a4f8d78 | ||
|
|
cc376ac5ed | ||
|
|
10c9d2fcec | ||
|
|
439ffe2e8a | ||
|
|
4e55262521 | ||
|
|
ebd2ec57fc | ||
|
|
f536819c5d | ||
|
|
af75c6db2f | ||
|
|
d12619cf1f | ||
|
|
4c1d227776 | ||
|
|
da02b7a141 | ||
|
|
17117bd21a | ||
|
|
0d7494ac52 | ||
|
|
a59a33846e | ||
|
|
51b8ba334a | ||
|
|
abc2269d97 | ||
|
|
ff385aaf7b | ||
|
|
1b50ca6bd3 | ||
|
|
6bae759349 | ||
|
|
e1e8c165db | ||
|
|
6699833121 | ||
|
|
095acefd7e | ||
|
|
b62e066b7d |
+7
-7
@@ -30,21 +30,21 @@ ext {
|
||||
|
||||
aspectjVersion = "1.9.5"
|
||||
freemarkerVersion = "2.3.28"
|
||||
groovyVersion = "2.5.8"
|
||||
groovyVersion = "2.5.9"
|
||||
hsqldbVersion = "2.4.1"
|
||||
jackson2Version = "2.9.9"
|
||||
jettyVersion = "9.4.25.v20191220"
|
||||
jettyVersion = "9.4.26.v20200117"
|
||||
junit5Version = "5.3.2"
|
||||
kotlinVersion = "1.2.71"
|
||||
log4jVersion = "2.11.2"
|
||||
nettyVersion = "4.1.44.Final"
|
||||
reactorVersion = "Californium-SR15"
|
||||
nettyVersion = "4.1.45.Final"
|
||||
reactorVersion = "Californium-SR16"
|
||||
rxjavaVersion = "1.3.8"
|
||||
rxjavaAdapterVersion = "1.2.1"
|
||||
rxjava2Version = "2.2.17"
|
||||
rxjava2Version = "2.2.18"
|
||||
slf4jVersion = "1.7.30" // spring-jcl + consistent 3rd party deps
|
||||
tiles3Version = "3.0.8"
|
||||
tomcatVersion = "9.0.30"
|
||||
tomcatVersion = "9.0.31"
|
||||
undertowVersion = "2.0.29.Final"
|
||||
|
||||
gradleScriptDir = "${rootProject.projectDir}/gradle"
|
||||
@@ -142,7 +142,7 @@ configure(allprojects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "8.28"
|
||||
toolVersion = "8.29"
|
||||
configDir = rootProject.file("src/checkstyle")
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
version=5.1.13.BUILD-SNAPSHOT
|
||||
version=5.1.14.RELEASE
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -37,6 +37,7 @@ import org.springframework.scheduling.config.TaskManagementConfigUtils;
|
||||
* @see org.springframework.scheduling.annotation.ProxyAsyncConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class AspectJAsyncConfiguration extends AbstractAsyncConfiguration {
|
||||
|
||||
@Bean(name = TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME)
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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,6 +36,7 @@ import org.springframework.transaction.config.TransactionManagementConfigUtils;
|
||||
* @see TransactionManagementConfigurationSelector
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class AspectJJtaTransactionManagementConfiguration extends AspectJTransactionManagementConfiguration {
|
||||
|
||||
@Bean(name = TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_BEAN_NAME)
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.transaction.config.TransactionManagementConfigUtils;
|
||||
* @see AspectJJtaTransactionManagementConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
|
||||
|
||||
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -373,7 +373,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
||||
|
||||
/**
|
||||
* Determine the name of the wrapped parameter/field.
|
||||
* @return the declared name (never {@code null})
|
||||
* @return the declared name (may be {@code null} if unresolvable)
|
||||
*/
|
||||
@Nullable
|
||||
public String getDependencyName() {
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -146,8 +146,9 @@ public class ArgumentConvertingMethodInvoker extends MethodInvoker {
|
||||
for (Method candidate : candidates) {
|
||||
if (candidate.getName().equals(targetMethod)) {
|
||||
// Check if the inspected method has the correct number of parameters.
|
||||
Class<?>[] paramTypes = candidate.getParameterTypes();
|
||||
if (paramTypes.length == argCount) {
|
||||
int parameterCount = candidate.getParameterCount();
|
||||
if (parameterCount == argCount) {
|
||||
Class<?>[] paramTypes = candidate.getParameterTypes();
|
||||
Object[] convertedArguments = new Object[argCount];
|
||||
boolean match = true;
|
||||
for (int j = 0; j < argCount && match; j++) {
|
||||
|
||||
+5
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -748,8 +748,7 @@ class ConfigurationClassParser {
|
||||
* @param importSelector the selector to handle
|
||||
*/
|
||||
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
|
||||
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
|
||||
configClass, importSelector);
|
||||
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
|
||||
if (this.deferredImportSelectors == null) {
|
||||
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
|
||||
handler.register(holder);
|
||||
@@ -775,7 +774,6 @@ class ConfigurationClassParser {
|
||||
this.deferredImportSelectors = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -786,8 +784,7 @@ class ConfigurationClassParser {
|
||||
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
|
||||
|
||||
public void register(DeferredImportSelectorHolder deferredImport) {
|
||||
Class<? extends Group> group = deferredImport.getImportSelector()
|
||||
.getImportGroup();
|
||||
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
|
||||
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
|
||||
(group != null ? group : deferredImport),
|
||||
key -> new DeferredImportSelectorGrouping(createGroup(group)));
|
||||
@@ -799,8 +796,7 @@ class ConfigurationClassParser {
|
||||
public void processGroupImports() {
|
||||
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
|
||||
grouping.getImports().forEach(entry -> {
|
||||
ConfigurationClass configurationClass = this.configurationClasses.get(
|
||||
entry.getMetadata());
|
||||
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
|
||||
try {
|
||||
processImports(configurationClass, asSourceClass(configurationClass),
|
||||
asSourceClasses(entry.getImportClassName()), false);
|
||||
@@ -818,8 +814,7 @@ class ConfigurationClassParser {
|
||||
}
|
||||
|
||||
private Group createGroup(@Nullable Class<? extends Group> type) {
|
||||
Class<? extends Group> effectiveType = (type != null ? type
|
||||
: DefaultDeferredImportSelectorGroup.class);
|
||||
Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
|
||||
Group group = BeanUtils.instantiateClass(effectiveType);
|
||||
ParserStrategyUtils.invokeAwareMethods(group,
|
||||
ConfigurationClassParser.this.environment,
|
||||
@@ -827,7 +822,6 @@ class ConfigurationClassParser {
|
||||
ConfigurationClassParser.this.registry);
|
||||
return group;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -51,6 +51,7 @@ public interface DeferredImportSelector extends ImportSelector {
|
||||
|
||||
/**
|
||||
* Interface used to group results from different import selectors.
|
||||
* @since 5.0
|
||||
*/
|
||||
interface Group {
|
||||
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -50,6 +50,7 @@ public interface ImportSelector {
|
||||
/**
|
||||
* Select and return the names of which class(es) should be imported based on
|
||||
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
|
||||
* @return the class names, or an empty array if none
|
||||
*/
|
||||
String[] selectImports(AnnotationMetadata importingClassMetadata);
|
||||
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -42,6 +42,7 @@ import org.springframework.util.Assert;
|
||||
* @see ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoaderAware {
|
||||
|
||||
@Nullable
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -50,6 +50,7 @@ import org.springframework.util.StringUtils;
|
||||
* @see EnableMBeanExport
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class MBeanExportConfiguration implements ImportAware, EnvironmentAware, BeanFactoryAware {
|
||||
|
||||
private static final String MBEAN_EXPORTER_BEAN_NAME = "mbeanExporter";
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -53,6 +53,6 @@ public enum ScopedProxyMode {
|
||||
/**
|
||||
* Create a class-based proxy (uses CGLIB).
|
||||
*/
|
||||
TARGET_CLASS;
|
||||
TARGET_CLASS
|
||||
|
||||
}
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -160,8 +160,9 @@ public abstract class AbstractResourceBasedMessageSource extends AbstractMessage
|
||||
/**
|
||||
* Set the number of seconds to cache loaded properties files.
|
||||
* <ul>
|
||||
* <li>Default is "-1", indicating to cache forever (just like
|
||||
* {@code java.util.ResourceBundle}).
|
||||
* <li>Default is "-1", indicating to cache forever (matching the default behavior
|
||||
* of {@code java.util.ResourceBundle}). Note that this constant follows Spring
|
||||
* conventions, not {@link java.util.ResourceBundle.Control#getTimeToLive}.
|
||||
* <li>A positive number will cache loaded properties files for the given
|
||||
* number of seconds. This is essentially the interval between refresh checks.
|
||||
* Note that a refresh attempt will first check the last-modified timestamp
|
||||
@@ -184,8 +185,9 @@ public abstract class AbstractResourceBasedMessageSource extends AbstractMessage
|
||||
* Set the number of milliseconds to cache loaded properties files.
|
||||
* Note that it is common to set seconds instead: {@link #setCacheSeconds}.
|
||||
* <ul>
|
||||
* <li>Default is "-1", indicating to cache forever (just like
|
||||
* {@code java.util.ResourceBundle}).
|
||||
* <li>Default is "-1", indicating to cache forever (matching the default behavior
|
||||
* of {@code java.util.ResourceBundle}). Note that this constant follows Spring
|
||||
* conventions, not {@link java.util.ResourceBundle.Control#getTimeToLive}.
|
||||
* <li>A positive number will cache loaded properties files for the given
|
||||
* number of milliseconds. This is essentially the interval between refresh checks.
|
||||
* Note that a refresh attempt will first check the last-modified timestamp
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -42,6 +42,6 @@ public enum RegistrationPolicy {
|
||||
* Registration should replace the affected MBean when attempting to register an MBean
|
||||
* under a name that already exists.
|
||||
*/
|
||||
REPLACE_EXISTING;
|
||||
REPLACE_EXISTING
|
||||
|
||||
}
|
||||
|
||||
+11
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -42,6 +42,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.componentscan.gh24375.MyComponent;
|
||||
import org.springframework.context.index.CandidateComponentsTestClassLoader;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
@@ -501,6 +502,15 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gh24375() {
|
||||
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
|
||||
Set<BeanDefinition> components = provider.findCandidateComponents(MyComponent.class.getPackage().getName());
|
||||
assertEquals(1, components.size());
|
||||
assertEquals(MyComponent.class.getName(), components.iterator().next().getBeanClassName());
|
||||
}
|
||||
|
||||
|
||||
private boolean containsBeanClass(Set<BeanDefinition> candidates, Class<?> beanClass) {
|
||||
for (BeanDefinition candidate : candidates) {
|
||||
if (beanClass.getName().equals(candidate.getBeanClassName())) {
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -530,9 +530,10 @@ public class ImportSelectorTests {
|
||||
static Map<String, List<String>> allImports() {
|
||||
return TestImportGroup.imports.entrySet()
|
||||
.stream()
|
||||
.collect(Collectors.toMap((entry) -> entry.getKey().getClassName(),
|
||||
.collect(Collectors.toMap(entry -> entry.getKey().getClassName(),
|
||||
Map.Entry::getValue));
|
||||
}
|
||||
|
||||
private final List<Entry> instanceImports = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation.componentscan.gh24375;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface A {
|
||||
|
||||
@AliasFor("value")
|
||||
B other() default @B;
|
||||
|
||||
@AliasFor("other")
|
||||
B value() default @B;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation.componentscan.gh24375;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface B {
|
||||
|
||||
String name() default "";
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation.componentscan.gh24375;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@A(other = @B)
|
||||
public class MyComponent {
|
||||
}
|
||||
@@ -336,7 +336,15 @@ public class ReflectUtils {
|
||||
public static Constructor getConstructor(Class type, Class[] parameterTypes) {
|
||||
try {
|
||||
Constructor constructor = type.getDeclaredConstructor(parameterTypes);
|
||||
constructor.setAccessible(true);
|
||||
if (System.getSecurityManager() != null) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||
constructor.setAccessible(true);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
else {
|
||||
constructor.setAccessible(true);
|
||||
}
|
||||
return constructor;
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -169,11 +169,12 @@ public final class GenericTypeResolver {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) genericType;
|
||||
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length];
|
||||
Type[] typeArguments = parameterizedType.getActualTypeArguments();
|
||||
ResolvableType contextType = ResolvableType.forClass(contextClass);
|
||||
for (int i = 0; i < typeArguments.length; i++) {
|
||||
Type typeArgument = typeArguments[i];
|
||||
if (typeArgument instanceof TypeVariable) {
|
||||
ResolvableType resolvedTypeArgument = resolveVariable(
|
||||
(TypeVariable<?>) typeArgument, ResolvableType.forClass(contextClass));
|
||||
(TypeVariable<?>) typeArgument, contextType);
|
||||
if (resolvedTypeArgument != ResolvableType.NONE) {
|
||||
generics[i] = resolvedTypeArgument.resolve();
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -149,9 +149,10 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
|
||||
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<>();
|
||||
List<AnnotationAttributes> attributesList = this.attributesMap.get(annotationName);
|
||||
if (attributesList != null) {
|
||||
String annotatedElement = "method '" + getMethodName() + '\'';
|
||||
for (AnnotationAttributes annotationAttributes : attributesList) {
|
||||
AnnotationAttributes convertedAttributes = AnnotationReadingVisitorUtils.convertClassValues(
|
||||
"method '" + getMethodName() + "'", this.classLoader, annotationAttributes, classValuesAsString);
|
||||
annotatedElement, this.classLoader, annotationAttributes, classValuesAsString);
|
||||
convertedAttributes.forEach(allAttributes::add);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -55,6 +55,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
* @see StandardEvaluationContext
|
||||
* @see SimpleEvaluationContext
|
||||
@@ -765,8 +766,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
}
|
||||
|
||||
if (this.member instanceof Method) {
|
||||
mv.visitMethodInsn((isStatic ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, this.member.getName(),
|
||||
CodeFlow.createSignatureDescriptor((Method) this.member), false);
|
||||
Method method = (Method) this.member;
|
||||
boolean isInterface = method.getDeclaringClass().isInterface();
|
||||
int opcode = (isStatic ? INVOKESTATIC : isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL);
|
||||
mv.visitMethodInsn(opcode, classDesc, method.getName(),
|
||||
CodeFlow.createSignatureDescriptor(method), isInterface);
|
||||
}
|
||||
else {
|
||||
mv.visitFieldInsn((isStatic ? GETSTATIC : GETFIELD), classDesc, this.member.getName(),
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.standard;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.SpelCompilerMode;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for the {@link SpelCompiler}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.1.14
|
||||
*/
|
||||
public class SpelCompilerTests {
|
||||
|
||||
@Test // gh-24357
|
||||
public void expressionCompilesWhenMethodComesFromPublicInterface() {
|
||||
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
|
||||
SpelExpressionParser parser = new SpelExpressionParser(config);
|
||||
|
||||
OrderedComponent component = new OrderedComponent();
|
||||
Expression expression = parser.parseExpression("order");
|
||||
|
||||
// Evaluate the expression multiple times to ensure that it gets compiled.
|
||||
IntStream.rangeClosed(1, 5).forEach(i -> assertEquals(42, expression.getValue(component)));
|
||||
}
|
||||
|
||||
|
||||
static class OrderedComponent implements Ordered {
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -157,7 +157,12 @@ final class LogAdapter {
|
||||
private final ExtendedLogger logger;
|
||||
|
||||
public Log4jLog(String name) {
|
||||
this.logger = loggerContext.getLogger(name);
|
||||
LoggerContext context = loggerContext;
|
||||
if (context == null) {
|
||||
// Circular call in early-init scenario -> static field not initialized yet
|
||||
context = LogManager.getContext(Log4jLog.class.getClassLoader(), false);
|
||||
}
|
||||
this.logger = context.getLogger(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -280,92 +285,110 @@ final class LogAdapter {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFatalEnabled() {
|
||||
return isErrorEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled() {
|
||||
return this.logger.isErrorEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled() {
|
||||
return this.logger.isWarnEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled() {
|
||||
return this.logger.isInfoEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled() {
|
||||
return this.logger.isDebugEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled() {
|
||||
return this.logger.isTraceEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatal(Object message) {
|
||||
error(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatal(Object message, Throwable exception) {
|
||||
error(message, exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Object message) {
|
||||
if (message instanceof String || this.logger.isErrorEnabled()) {
|
||||
this.logger.error(String.valueOf(message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Object message, Throwable exception) {
|
||||
if (message instanceof String || this.logger.isErrorEnabled()) {
|
||||
this.logger.error(String.valueOf(message), exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Object message) {
|
||||
if (message instanceof String || this.logger.isWarnEnabled()) {
|
||||
this.logger.warn(String.valueOf(message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Object message, Throwable exception) {
|
||||
if (message instanceof String || this.logger.isWarnEnabled()) {
|
||||
this.logger.warn(String.valueOf(message), exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Object message) {
|
||||
if (message instanceof String || this.logger.isInfoEnabled()) {
|
||||
this.logger.info(String.valueOf(message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Object message, Throwable exception) {
|
||||
if (message instanceof String || this.logger.isInfoEnabled()) {
|
||||
this.logger.info(String.valueOf(message), exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Object message) {
|
||||
if (message instanceof String || this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(String.valueOf(message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Object message, Throwable exception) {
|
||||
if (message instanceof String || this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(String.valueOf(message), exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Object message) {
|
||||
if (message instanceof String || this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(String.valueOf(message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Object message, Throwable exception) {
|
||||
if (message instanceof String || this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(String.valueOf(message), exception);
|
||||
@@ -486,74 +509,92 @@ final class LogAdapter {
|
||||
this.logger = java.util.logging.Logger.getLogger(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFatalEnabled() {
|
||||
return isErrorEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled() {
|
||||
return this.logger.isLoggable(java.util.logging.Level.SEVERE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled() {
|
||||
return this.logger.isLoggable(java.util.logging.Level.WARNING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled() {
|
||||
return this.logger.isLoggable(java.util.logging.Level.INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled() {
|
||||
return this.logger.isLoggable(java.util.logging.Level.FINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled() {
|
||||
return this.logger.isLoggable(java.util.logging.Level.FINEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatal(Object message) {
|
||||
error(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatal(Object message, Throwable exception) {
|
||||
error(message, exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Object message) {
|
||||
log(java.util.logging.Level.SEVERE, message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Object message, Throwable exception) {
|
||||
log(java.util.logging.Level.SEVERE, message, exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Object message) {
|
||||
log(java.util.logging.Level.WARNING, message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Object message, Throwable exception) {
|
||||
log(java.util.logging.Level.WARNING, message, exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Object message) {
|
||||
log(java.util.logging.Level.INFO, message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Object message, Throwable exception) {
|
||||
log(java.util.logging.Level.INFO, message, exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Object message) {
|
||||
log(java.util.logging.Level.FINE, message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Object message, Throwable exception) {
|
||||
log(java.util.logging.Level.FINE, message, exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Object message) {
|
||||
log(java.util.logging.Level.FINEST, message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Object message, Throwable exception) {
|
||||
log(java.util.logging.Level.FINEST, message, exception);
|
||||
}
|
||||
@@ -567,8 +608,8 @@ final class LogAdapter {
|
||||
else {
|
||||
rec = new LocationResolvingLogRecord(level, String.valueOf(message));
|
||||
rec.setLoggerName(this.name);
|
||||
rec.setResourceBundleName(logger.getResourceBundleName());
|
||||
rec.setResourceBundle(logger.getResourceBundle());
|
||||
rec.setResourceBundleName(this.logger.getResourceBundleName());
|
||||
rec.setResourceBundle(this.logger.getResourceBundle());
|
||||
rec.setThrown(exception);
|
||||
}
|
||||
logger.log(rec);
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -42,8 +42,9 @@ public final class CallMetaDataProviderFactory {
|
||||
public static final List<String> supportedDatabaseProductsForProcedures = Arrays.asList(
|
||||
"Apache Derby",
|
||||
"DB2",
|
||||
"MySQL",
|
||||
"Informix Dynamic Server",
|
||||
"Microsoft SQL Server",
|
||||
"MySQL",
|
||||
"Oracle",
|
||||
"PostgreSQL",
|
||||
"Sybase"
|
||||
@@ -51,8 +52,8 @@ public final class CallMetaDataProviderFactory {
|
||||
|
||||
/** List of supported database products for function calls. */
|
||||
public static final List<String> supportedDatabaseProductsForFunctions = Arrays.asList(
|
||||
"MySQL",
|
||||
"Microsoft SQL Server",
|
||||
"MySQL",
|
||||
"Oracle",
|
||||
"PostgreSQL"
|
||||
);
|
||||
|
||||
+5
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -57,9 +57,12 @@ abstract class AbstractEmbeddedDatabaseConfigurer implements EmbeddedDatabaseCon
|
||||
try {
|
||||
con.close();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
catch (SQLException ex) {
|
||||
logger.debug("Could not close JDBC Connection on shutdown", ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Unexpected exception on closing JDBC Connection", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -52,10 +52,10 @@ public abstract class DatabasePopulatorUtils {
|
||||
DataSourceUtils.releaseConnection(connection, dataSource);
|
||||
}
|
||||
}
|
||||
catch (ScriptException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (ex instanceof ScriptException) {
|
||||
throw (ScriptException) ex;
|
||||
}
|
||||
throw new UncategorizedScriptException("Failed to execute database script", ex);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -505,7 +505,7 @@ public class SingleConnectionFactory implements ConnectionFactory, QueueConnecti
|
||||
logger.debug("Ignoring Connection state exception - assuming already closed: " + ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Could not close shared JMS Connection", ex);
|
||||
logger.warn("Could not close shared JMS Connection", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -41,6 +41,6 @@ public enum SimpMessageType {
|
||||
|
||||
DISCONNECT_ACK,
|
||||
|
||||
OTHER;
|
||||
OTHER
|
||||
|
||||
}
|
||||
|
||||
+2
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -170,11 +170,8 @@ public abstract class SessionFactoryUtils {
|
||||
try {
|
||||
session.close();
|
||||
}
|
||||
catch (HibernateException ex) {
|
||||
logger.debug("Could not close Hibernate Session", ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Unexpected exception on closing Hibernate Session", ex);
|
||||
logger.error("Failed to release Hibernate Session", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -427,11 +427,8 @@ public abstract class EntityManagerFactoryUtils {
|
||||
em.close();
|
||||
}
|
||||
}
|
||||
catch (PersistenceException ex) {
|
||||
logger.debug("Could not close JPA EntityManager", ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Unexpected exception on closing JPA EntityManager", ex);
|
||||
logger.error("Failed to release JPA EntityManager", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -603,9 +603,9 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
|
||||
getJpaDialect().releaseJdbcConnection(conHandle,
|
||||
txObject.getEntityManagerHolder().getEntityManager());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
catch (Throwable ex) {
|
||||
// Just log it, to keep a transaction-related exception.
|
||||
logger.error("Could not close JDBC connection after transaction", ex);
|
||||
logger.error("Failed to release JDBC connection after transaction", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -18,5 +18,5 @@ package org.springframework.orm.jpa.hibernate.beans;
|
||||
|
||||
public enum BeanSource {
|
||||
SPRING,
|
||||
FALLBACK;
|
||||
FALLBACK
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -35,6 +35,7 @@ import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
* @see TransactionManagementConfigurationSelector
|
||||
*/
|
||||
@Configuration
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
|
||||
|
||||
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
|
||||
|
||||
+39
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -18,11 +18,13 @@ package org.springframework.http.codec.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
@@ -113,17 +115,46 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
|
||||
public Flux<Object> decode(Publisher<DataBuffer> input, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
|
||||
Flux.from(input), this.jsonFactory, getObjectMapper(), true, getMaxInMemorySize());
|
||||
boolean forceUseOfBigDecimal = getObjectMapper().isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
|
||||
if (BigDecimal.class.equals(elementType.getType())) {
|
||||
forceUseOfBigDecimal = true;
|
||||
}
|
||||
|
||||
Flux<DataBuffer> processed = processInput(input, elementType, mimeType, hints);
|
||||
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(processed, this.jsonFactory, getObjectMapper(),
|
||||
true, forceUseOfBigDecimal, getMaxInMemorySize());
|
||||
return decodeInternal(tokens, elementType, mimeType, hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the input publisher into a flux. Default implementation returns
|
||||
* {@link Flux#from(Publisher)}, but subclasses can choose to to customize
|
||||
* this behaviour.
|
||||
* @param input the {@code DataBuffer} input stream to process
|
||||
* @param elementType the expected type of elements in the output stream
|
||||
* @param mimeType the MIME type associated with the input stream (optional)
|
||||
* @param hints additional information about how to do encode
|
||||
* @return the processed flux
|
||||
* @since 5.1.14
|
||||
*/
|
||||
protected Flux<DataBuffer> processInput(Publisher<DataBuffer> input, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
return Flux.from(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
|
||||
Flux.from(input), this.jsonFactory, getObjectMapper(), false, getMaxInMemorySize());
|
||||
boolean forceUseOfBigDecimal = getObjectMapper().isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
|
||||
if (BigDecimal.class.equals(elementType.getType())) {
|
||||
forceUseOfBigDecimal = true;
|
||||
}
|
||||
|
||||
Flux<DataBuffer> processed = processInput(input, elementType, mimeType, hints);
|
||||
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(processed, this.jsonFactory, getObjectMapper(),
|
||||
false, forceUseOfBigDecimal, getMaxInMemorySize());
|
||||
return decodeInternal(tokens, elementType, mimeType, hints).singleOrEmpty();
|
||||
}
|
||||
|
||||
@@ -166,7 +197,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
|
||||
}
|
||||
|
||||
|
||||
// HttpMessageDecoder...
|
||||
// HttpMessageDecoder
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getDecodeHints(ResolvableType actualType, ResolvableType elementType,
|
||||
@@ -180,7 +211,8 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
|
||||
return getMimeTypes();
|
||||
}
|
||||
|
||||
// Jackson2CodecSupport ...
|
||||
|
||||
// Jackson2CodecSupport
|
||||
|
||||
@Override
|
||||
protected <A extends Annotation> A getAnnotation(MethodParameter parameter, Class<A> annotType) {
|
||||
|
||||
+120
-59
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.http.codec.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
@@ -29,9 +28,11 @@ import java.util.Map;
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.SequenceWriter;
|
||||
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -44,7 +45,6 @@ import org.springframework.core.codec.EncodingException;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.HttpMessageEncoder;
|
||||
@@ -115,65 +115,53 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
|
||||
Assert.notNull(elementType, "'elementType' must not be null");
|
||||
|
||||
JsonEncoding encoding = getJsonEncoding(mimeType);
|
||||
|
||||
if (inputStream instanceof Mono) {
|
||||
return Mono.from(inputStream).map(value ->
|
||||
encodeValue(value, mimeType, bufferFactory, elementType, hints, encoding)).flux();
|
||||
return Mono.from(inputStream)
|
||||
.map(value -> encodeValue(value, bufferFactory, elementType, mimeType, hints))
|
||||
.flux();
|
||||
}
|
||||
else {
|
||||
return this.streamingMediaTypes.stream()
|
||||
.filter(mediaType -> mediaType.isCompatibleWith(mimeType))
|
||||
.findFirst()
|
||||
.map(mediaType -> {
|
||||
byte[] separator = STREAM_SEPARATORS.getOrDefault(mediaType, NEWLINE_SEPARATOR);
|
||||
return Flux.from(inputStream).map(value -> {
|
||||
DataBuffer buffer = encodeValue(
|
||||
value, mimeType, bufferFactory, elementType, hints, encoding);
|
||||
if (separator != null) {
|
||||
buffer.write(separator);
|
||||
}
|
||||
return buffer;
|
||||
});
|
||||
})
|
||||
.orElseGet(() -> {
|
||||
ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
|
||||
return Flux.from(inputStream).collectList().map(list ->
|
||||
encodeValue(list, mimeType, bufferFactory, listType, hints, encoding)).flux();
|
||||
});
|
||||
byte[] separator = streamSeparator(mimeType);
|
||||
if (separator != null) { // streaming
|
||||
try {
|
||||
ObjectWriter writer = createObjectWriter(elementType, mimeType, hints);
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler());
|
||||
JsonEncoding encoding = getJsonEncoding(mimeType);
|
||||
JsonGenerator generator = getObjectMapper().getFactory().createGenerator(byteBuilder, encoding);
|
||||
SequenceWriter sequenceWriter = writer.writeValues(generator);
|
||||
|
||||
return Flux.from(inputStream)
|
||||
.map(value -> encodeStreamingValue(value, bufferFactory, hints, sequenceWriter, byteBuilder,
|
||||
separator));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return Flux.error(ex);
|
||||
}
|
||||
}
|
||||
else { // non-streaming
|
||||
ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
|
||||
return Flux.from(inputStream)
|
||||
.collectList()
|
||||
.map(list -> encodeValue(list, bufferFactory, listType, mimeType, hints))
|
||||
.flux();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private DataBuffer encodeValue(Object value, @Nullable MimeType mimeType, DataBufferFactory bufferFactory,
|
||||
ResolvableType elementType, @Nullable Map<String, Object> hints, JsonEncoding encoding) {
|
||||
public DataBuffer encodeValue(Object value, DataBufferFactory bufferFactory,
|
||||
ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
if (!Hints.isLoggingSuppressed(hints)) {
|
||||
LogFormatUtils.traceDebug(logger, traceOn -> {
|
||||
String formatted = LogFormatUtils.formatValue(value, !traceOn);
|
||||
return Hints.getLogPrefix(hints) + "Encoding [" + formatted + "]";
|
||||
});
|
||||
}
|
||||
ObjectWriter writer = createObjectWriter(valueType, mimeType, hints);
|
||||
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler());
|
||||
JsonEncoding encoding = getJsonEncoding(mimeType);
|
||||
|
||||
JavaType javaType = getJavaType(elementType.getType(), null);
|
||||
Class<?> jsonView = (hints != null ? (Class<?>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
|
||||
ObjectWriter writer = (jsonView != null ?
|
||||
getObjectMapper().writerWithView(jsonView) : getObjectMapper().writer());
|
||||
|
||||
if (javaType.isContainerType()) {
|
||||
writer = writer.forType(javaType);
|
||||
}
|
||||
|
||||
writer = customizeWriter(writer, mimeType, elementType, hints);
|
||||
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer();
|
||||
boolean release = true;
|
||||
OutputStream outputStream = buffer.asOutputStream();
|
||||
logValue(hints, value);
|
||||
|
||||
try {
|
||||
JsonGenerator generator = getObjectMapper().getFactory().createGenerator(outputStream, encoding);
|
||||
JsonGenerator generator = getObjectMapper().getFactory().createGenerator(byteBuilder, encoding);
|
||||
writer.writeValue(generator, value);
|
||||
generator.flush();
|
||||
release = false;
|
||||
}
|
||||
catch (InvalidDefinitionException ex) {
|
||||
throw new CodecException("Type definition error: " + ex.getType(), ex);
|
||||
@@ -182,24 +170,96 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
throw new EncodingException("JSON encoding error: " + ex.getOriginalMessage(), ex);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unexpected I/O error while writing to data buffer",
|
||||
ex);
|
||||
}
|
||||
finally {
|
||||
if (release) {
|
||||
DataBufferUtils.release(buffer);
|
||||
}
|
||||
throw new IllegalStateException("Unexpected I/O error while writing to byte array builder", ex);
|
||||
}
|
||||
|
||||
byte[] bytes = byteBuilder.toByteArray();
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private DataBuffer encodeStreamingValue(Object value, DataBufferFactory bufferFactory, @Nullable Map<String, Object> hints,
|
||||
SequenceWriter sequenceWriter, ByteArrayBuilder byteArrayBuilder, byte[] separator) {
|
||||
|
||||
logValue(hints, value);
|
||||
|
||||
try {
|
||||
sequenceWriter.write(value);
|
||||
sequenceWriter.flush();
|
||||
}
|
||||
catch (InvalidDefinitionException ex) {
|
||||
throw new CodecException("Type definition error: " + ex.getType(), ex);
|
||||
}
|
||||
catch (JsonProcessingException ex) {
|
||||
throw new EncodingException("JSON encoding error: " + ex.getOriginalMessage(), ex);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unexpected I/O error while writing to byte array builder", ex);
|
||||
}
|
||||
|
||||
byte[] bytes = byteArrayBuilder.toByteArray();
|
||||
byteArrayBuilder.reset();
|
||||
|
||||
int offset;
|
||||
int length;
|
||||
if (bytes.length > 0 && bytes[0] == ' ') {
|
||||
// SequenceWriter writes an unnecessary space in between values
|
||||
offset = 1;
|
||||
length = bytes.length - 1;
|
||||
}
|
||||
else {
|
||||
offset = 0;
|
||||
length = bytes.length;
|
||||
}
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer(length + separator.length);
|
||||
buffer.write(bytes, offset, length);
|
||||
buffer.write(separator);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private void logValue(@Nullable Map<String, Object> hints, Object value) {
|
||||
if (!Hints.isLoggingSuppressed(hints)) {
|
||||
LogFormatUtils.traceDebug(logger, traceOn -> {
|
||||
String formatted = LogFormatUtils.formatValue(value, !traceOn);
|
||||
return Hints.getLogPrefix(hints) + "Encoding [" + formatted + "]";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectWriter createObjectWriter(ResolvableType valueType, @Nullable MimeType mimeType,
|
||||
@Nullable Map<String, Object> hints) {
|
||||
|
||||
JavaType javaType = getJavaType(valueType.getType(), null);
|
||||
Class<?> jsonView = (hints != null ? (Class<?>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
|
||||
ObjectWriter writer = (jsonView != null ?
|
||||
getObjectMapper().writerWithView(jsonView) : getObjectMapper().writer());
|
||||
|
||||
if (javaType.isContainerType()) {
|
||||
writer = writer.forType(javaType);
|
||||
}
|
||||
|
||||
return customizeWriter(writer, mimeType, valueType, hints);
|
||||
}
|
||||
|
||||
protected ObjectWriter customizeWriter(ObjectWriter writer, @Nullable MimeType mimeType,
|
||||
ResolvableType elementType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private byte[] streamSeparator(@Nullable MimeType mimeType) {
|
||||
for (MediaType streamingMediaType : this.streamingMediaTypes) {
|
||||
if (streamingMediaType.isCompatibleWith(mimeType)) {
|
||||
return STREAM_SEPARATORS.getOrDefault(streamingMediaType, NEWLINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the JSON encoding to use for the given mime type.
|
||||
* @param mimeType the mime type as requested by the caller
|
||||
@@ -219,7 +279,7 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
}
|
||||
|
||||
|
||||
// HttpMessageEncoder...
|
||||
// HttpMessageEncoder
|
||||
|
||||
@Override
|
||||
public List<MimeType> getEncodableMimeTypes() {
|
||||
@@ -238,7 +298,8 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
return (actualType != null ? getHints(actualType) : Hints.none());
|
||||
}
|
||||
|
||||
// Jackson2CodecSupport ...
|
||||
|
||||
// Jackson2CodecSupport
|
||||
|
||||
@Override
|
||||
protected <A extends Annotation> A getAnnotation(MethodParameter parameter, Class<A> annotType) {
|
||||
|
||||
+45
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -16,10 +16,24 @@
|
||||
|
||||
package org.springframework.http.codec.json;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
/**
|
||||
* Decode a byte stream into JSON and convert to Object's with Jackson 2.9,
|
||||
@@ -32,6 +46,11 @@ import org.springframework.util.MimeType;
|
||||
*/
|
||||
public class Jackson2JsonDecoder extends AbstractJackson2Decoder {
|
||||
|
||||
private static final StringDecoder STRING_DECODER = StringDecoder.textPlainOnly(Arrays.asList(",", "\n"), false);
|
||||
|
||||
private static final ResolvableType STRING_TYPE = ResolvableType.forClass(String.class);
|
||||
|
||||
|
||||
public Jackson2JsonDecoder() {
|
||||
super(Jackson2ObjectMapperBuilder.json().build());
|
||||
}
|
||||
@@ -40,4 +59,28 @@ public class Jackson2JsonDecoder extends AbstractJackson2Decoder {
|
||||
super(mapper, mimeTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Flux<DataBuffer> processInput(Publisher<DataBuffer> input, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<DataBuffer> flux = Flux.from(input);
|
||||
if (mimeType == null) {
|
||||
return flux;
|
||||
}
|
||||
|
||||
// Jackson asynchronous parser only supports UTF-8
|
||||
Charset charset = mimeType.getCharset();
|
||||
if (charset == null || StandardCharsets.UTF_8.equals(charset) || StandardCharsets.US_ASCII.equals(charset)) {
|
||||
return flux;
|
||||
}
|
||||
|
||||
// Potentially, the memory consumption of this conversion could be improved by using CharBuffers instead
|
||||
// of allocating Strings, but that would require refactoring the buffer tokenization code from StringDecoder
|
||||
|
||||
MimeType textMimeType = new MimeType(MimeTypeUtils.TEXT_PLAIN, charset);
|
||||
Flux<String> decoded = STRING_DECODER.decode(input, STRING_TYPE, textMimeType, null);
|
||||
DataBufferFactory factory = new DefaultDataBufferFactory();
|
||||
return decoded.map(s -> factory.wrap(s.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+27
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -55,16 +55,18 @@ final class Jackson2Tokenizer {
|
||||
|
||||
private final boolean tokenizeArrayElements;
|
||||
|
||||
private TokenBuffer tokenBuffer;
|
||||
private final boolean forceUseOfBigDecimal;
|
||||
|
||||
private final int maxInMemorySize;
|
||||
|
||||
private int objectDepth;
|
||||
|
||||
private int arrayDepth;
|
||||
|
||||
private final int maxInMemorySize;
|
||||
|
||||
private int byteCount;
|
||||
|
||||
private TokenBuffer tokenBuffer;
|
||||
|
||||
|
||||
// TODO: change to ByteBufferFeeder when supported by Jackson
|
||||
// See https://github.com/FasterXML/jackson-core/issues/478
|
||||
@@ -72,17 +74,19 @@ final class Jackson2Tokenizer {
|
||||
|
||||
|
||||
private Jackson2Tokenizer(JsonParser parser, DeserializationContext deserializationContext,
|
||||
boolean tokenizeArrayElements, int maxInMemorySize) {
|
||||
boolean tokenizeArrayElements, boolean forceUseOfBigDecimal, int maxInMemorySize) {
|
||||
|
||||
this.parser = parser;
|
||||
this.deserializationContext = deserializationContext;
|
||||
this.tokenizeArrayElements = tokenizeArrayElements;
|
||||
this.tokenBuffer = new TokenBuffer(parser, deserializationContext);
|
||||
this.forceUseOfBigDecimal = forceUseOfBigDecimal;
|
||||
this.inputFeeder = (ByteArrayFeeder) this.parser.getNonBlockingInputFeeder();
|
||||
this.maxInMemorySize = maxInMemorySize;
|
||||
this.tokenBuffer = createToken();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Flux<TokenBuffer> tokenize(DataBuffer dataBuffer) {
|
||||
int bufferSize = dataBuffer.readableByteCount();
|
||||
byte[] bytes = new byte[dataBuffer.readableByteCount()];
|
||||
@@ -132,6 +136,9 @@ final class Jackson2Tokenizer {
|
||||
previousNull = true;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
previousNull = false;
|
||||
}
|
||||
updateDepth(token);
|
||||
if (!this.tokenizeArrayElements) {
|
||||
processTokenNormal(token, result);
|
||||
@@ -165,9 +172,8 @@ final class Jackson2Tokenizer {
|
||||
|
||||
if ((token.isStructEnd() || token.isScalarValue()) && this.objectDepth == 0 && this.arrayDepth == 0) {
|
||||
result.add(this.tokenBuffer);
|
||||
this.tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
|
||||
this.tokenBuffer = createToken();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void processTokenArray(JsonToken token, List<TokenBuffer> result) throws IOException {
|
||||
@@ -178,10 +184,16 @@ final class Jackson2Tokenizer {
|
||||
if (this.objectDepth == 0 && (this.arrayDepth == 0 || this.arrayDepth == 1) &&
|
||||
(token == JsonToken.END_OBJECT || token.isScalarValue())) {
|
||||
result.add(this.tokenBuffer);
|
||||
this.tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
|
||||
this.tokenBuffer = createToken();
|
||||
}
|
||||
}
|
||||
|
||||
private TokenBuffer createToken() {
|
||||
TokenBuffer tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
|
||||
tokenBuffer.forceUseOfBigDecimal(this.forceUseOfBigDecimal);
|
||||
return tokenBuffer;
|
||||
}
|
||||
|
||||
private boolean isTopLevelArrayToken(JsonToken token) {
|
||||
return this.objectDepth == 0 && ((token == JsonToken.START_ARRAY && this.arrayDepth == 1) ||
|
||||
(token == JsonToken.END_ARRAY && this.arrayDepth == 0));
|
||||
@@ -217,10 +229,13 @@ final class Jackson2Tokenizer {
|
||||
* @param objectMapper the current mapper instance
|
||||
* @param tokenizeArrays if {@code true} and the "top level" JSON object is
|
||||
* an array, each element is returned individually immediately after it is received
|
||||
* @param forceUseOfBigDecimal if {@code true}, any floating point values encountered
|
||||
* in source will use {@link java.math.BigDecimal}
|
||||
* @param maxInMemorySize maximum memory size
|
||||
* @return the resulting token buffers
|
||||
*/
|
||||
public static Flux<TokenBuffer> tokenize(Flux<DataBuffer> dataBuffers, JsonFactory jsonFactory,
|
||||
ObjectMapper objectMapper, boolean tokenizeArrays, int maxInMemorySize) {
|
||||
ObjectMapper objectMapper, boolean tokenizeArrays, boolean forceUseOfBigDecimal, int maxInMemorySize) {
|
||||
|
||||
try {
|
||||
JsonParser parser = jsonFactory.createNonBlockingByteArrayParser();
|
||||
@@ -229,7 +244,8 @@ final class Jackson2Tokenizer {
|
||||
context = ((DefaultDeserializationContext) context).createInstance(
|
||||
objectMapper.getDeserializationConfig(), parser, objectMapper.getInjectableValues());
|
||||
}
|
||||
Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(parser, context, tokenizeArrays, maxInMemorySize);
|
||||
Jackson2Tokenizer tokenizer =
|
||||
new Jackson2Tokenizer(parser, context, tokenizeArrays, forceUseOfBigDecimal, maxInMemorySize);
|
||||
return dataBuffers.flatMap(tokenizer::tokenize, Flux::error, tokenizer::endOfInput);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
|
||||
+2
@@ -216,6 +216,7 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
|
||||
if (response.getContentType() == null && contentType != null) {
|
||||
response.setContentType(contentType.toString());
|
||||
}
|
||||
getHeaders().remove(HttpHeaders.CONTENT_TYPE);
|
||||
Charset charset = (contentType != null ? contentType.getCharset() : null);
|
||||
if (response.getCharacterEncoding() == null && charset != null) {
|
||||
response.setCharacterEncoding(charset.name());
|
||||
@@ -224,6 +225,7 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
|
||||
if (contentLength != -1) {
|
||||
response.setContentLengthLong(contentLength);
|
||||
}
|
||||
getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -72,13 +72,10 @@ import org.springframework.web.util.UriTemplateHandler;
|
||||
* addition to the generalized {@code exchange} and {@code execute} methods that
|
||||
* support of less frequent cases.
|
||||
*
|
||||
* <p><strong>NOTE:</strong> As of 5.0, the non-blocking, reactive
|
||||
* {@code org.springframework.web.reactive.client.WebClient} offers a
|
||||
* modern alternative to the {@code RestTemplate} with efficient support for
|
||||
* both sync and async, as well as streaming scenarios. The {@code RestTemplate}
|
||||
* will be deprecated in a future version and will not have major new features
|
||||
* added going forward. See the WebClient section of the Spring Framework reference
|
||||
* documentation for more details and example code.
|
||||
* <p><strong>NOTE:</strong> As of 5.0 this class is in maintenance mode, with
|
||||
* only minor requests for changes and bugs to be accepted going forward. Please,
|
||||
* consider using the {@code org.springframework.web.reactive.client.WebClient}
|
||||
* which has a more modern API and supports sync, async, and streaming scenarios.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Brian Clozel
|
||||
@@ -92,7 +89,7 @@ import org.springframework.web.util.UriTemplateHandler;
|
||||
*/
|
||||
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
|
||||
|
||||
private static boolean romePresent;
|
||||
private static final boolean romePresent;
|
||||
|
||||
private static final boolean jaxb2Present;
|
||||
|
||||
@@ -203,6 +200,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
||||
this.uriTemplateHandler = initUriTemplateHandler();
|
||||
}
|
||||
|
||||
|
||||
private static DefaultUriBuilderFactory initUriTemplateHandler() {
|
||||
DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
|
||||
uriFactory.setEncodingMode(EncodingMode.URI_COMPONENT); // for backwards compatibility..
|
||||
|
||||
+63
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.http.codec.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -33,6 +35,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.AbstractDecoderTestCase;
|
||||
import org.springframework.core.codec.CodecException;
|
||||
@@ -42,13 +45,21 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.Pojo;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.ResolvableType.*;
|
||||
import static org.springframework.http.MediaType.*;
|
||||
import static org.springframework.http.codec.json.Jackson2JsonDecoder.*;
|
||||
import static org.springframework.http.codec.json.JacksonViewBean.*;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.core.ResolvableType.forClass;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
|
||||
import static org.springframework.http.MediaType.APPLICATION_STREAM_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_XML;
|
||||
import static org.springframework.http.codec.json.Jackson2JsonDecoder.JSON_VIEW_HINT;
|
||||
import static org.springframework.http.codec.json.JacksonViewBean.MyJacksonView1;
|
||||
import static org.springframework.http.codec.json.JacksonViewBean.MyJacksonView3;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Jackson2JsonDecoder}.
|
||||
@@ -199,9 +210,52 @@ public class Jackson2JsonDecoderTests extends AbstractDecoderTestCase<Jackson2Js
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bigDecimalFlux() {
|
||||
Flux<DataBuffer> input = stringBuffer("[ 1E+2 ]").flux();
|
||||
|
||||
testDecode(input, BigDecimal.class, step -> step
|
||||
.expectNext(new BigDecimal("1E+2"))
|
||||
.verifyComplete()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeNonUtf8Encoding() {
|
||||
Mono<DataBuffer> input = stringBuffer("{\"foo\":\"bar\"}", StandardCharsets.UTF_16);
|
||||
|
||||
testDecode(input, ResolvableType.forType(new ParameterizedTypeReference<Map<String, String>>() {}),
|
||||
step -> step.assertNext(o -> {
|
||||
Map<String, String> map = (Map<String, String>) o;
|
||||
assertEquals("bar", map.get("foo"));
|
||||
})
|
||||
.verifyComplete(),
|
||||
MediaType.parseMediaType("application/json; charset=utf-16"),
|
||||
null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeMonoNonUtf8Encoding() {
|
||||
Mono<DataBuffer> input = stringBuffer("{\"foo\":\"bar\"}", StandardCharsets.UTF_16);
|
||||
|
||||
testDecodeToMono(input, ResolvableType.forType(new ParameterizedTypeReference<Map<String, String>>() {
|
||||
}),
|
||||
step -> step.assertNext(o -> {
|
||||
Map<String, String> map = (Map<String, String>) o;
|
||||
assertEquals("bar", map.get("foo"));
|
||||
})
|
||||
.verifyComplete(),
|
||||
MediaType.parseMediaType("application/json; charset=utf-16"),
|
||||
null);
|
||||
}
|
||||
|
||||
private Mono<DataBuffer> stringBuffer(String value) {
|
||||
return stringBuffer(value, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private Mono<DataBuffer> stringBuffer(String value, Charset charset) {
|
||||
return Mono.defer(() -> {
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] bytes = value.getBytes(charset);
|
||||
DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
return Mono.just(buffer);
|
||||
|
||||
+44
-46
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -20,23 +20,26 @@ import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.fasterxml.jackson.databind.MappingIterator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.AbstractEncoderTestCase;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.http.codec.Pojo;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.core.io.buffer.DataBufferUtils.release;
|
||||
import static org.springframework.http.MediaType.APPLICATION_XML;
|
||||
|
||||
@@ -59,21 +62,6 @@ public class Jackson2SmileEncoderTests extends AbstractEncoderTestCase<Jackson2S
|
||||
|
||||
}
|
||||
|
||||
public Consumer<DataBuffer> pojoConsumer(Pojo expected) {
|
||||
return dataBuffer -> {
|
||||
try {
|
||||
Pojo actual = this.mapper.reader().forType(Pojo.class)
|
||||
.readValue(DataBufferTestUtils.dumpBytes(dataBuffer));
|
||||
assertEquals(expected, actual);
|
||||
release(dataBuffer);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void canEncode() {
|
||||
@@ -106,7 +94,19 @@ public class Jackson2SmileEncoderTests extends AbstractEncoderTestCase<Jackson2S
|
||||
Flux<Pojo> input = Flux.fromIterable(list);
|
||||
|
||||
testEncode(input, Pojo.class, step -> step
|
||||
.consumeNextWith(expect(list, List.class)));
|
||||
.consumeNextWith(dataBuffer -> {
|
||||
try {
|
||||
Object actual = this.mapper.reader().forType(List.class)
|
||||
.readValue(dataBuffer.asInputStream());
|
||||
assertEquals(list, actual);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
finally {
|
||||
release(dataBuffer);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -127,32 +127,30 @@ public class Jackson2SmileEncoderTests extends AbstractEncoderTestCase<Jackson2S
|
||||
Flux<Pojo> input = Flux.just(pojo1, pojo2, pojo3);
|
||||
ResolvableType type = ResolvableType.forClass(Pojo.class);
|
||||
|
||||
testEncodeAll(input, type, step -> step
|
||||
.consumeNextWith(expect(pojo1, Pojo.class))
|
||||
.consumeNextWith(expect(pojo2, Pojo.class))
|
||||
.consumeNextWith(expect(pojo3, Pojo.class))
|
||||
.verifyComplete(),
|
||||
STREAM_SMILE_MIME_TYPE, null);
|
||||
Flux<DataBuffer> result = this.encoder
|
||||
.encode(input, bufferFactory, type, STREAM_SMILE_MIME_TYPE, null);
|
||||
|
||||
Mono<MappingIterator<Pojo>> joined = DataBufferUtils.join(result)
|
||||
.map(buffer -> {
|
||||
try {
|
||||
return this.mapper.reader().forType(Pojo.class).readValues(buffer.asInputStream(true));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
StepVerifier.create(joined)
|
||||
.assertNext(iter -> {
|
||||
assertTrue(iter.hasNext());
|
||||
assertEquals(pojo1, iter.next());
|
||||
assertTrue(iter.hasNext());
|
||||
assertEquals(pojo2, iter.next());
|
||||
assertTrue(iter.hasNext());
|
||||
assertEquals(pojo3, iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
|
||||
private <T> Consumer<DataBuffer> expect(T expected, Class<T> expectedType) {
|
||||
return dataBuffer -> {
|
||||
try {
|
||||
Object actual = this.mapper.reader().forType(expectedType)
|
||||
.readValue(dataBuffer.asInputStream());
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
finally {
|
||||
release(dataBuffer);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+42
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -19,9 +19,12 @@ package org.springframework.http.codec.json;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.core.TreeNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.util.TokenBuffer;
|
||||
@@ -37,8 +40,10 @@ import org.springframework.core.io.buffer.AbstractLeakCheckingTestCase;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferLimitException;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
@@ -242,7 +247,8 @@ public class Jackson2TokenizerTests extends AbstractLeakCheckingTestCase {
|
||||
public void errorInStream() {
|
||||
DataBuffer buffer = stringBuffer("{\"id\":1,\"name\":");
|
||||
Flux<DataBuffer> source = Flux.just(buffer).concatWith(Flux.error(new RuntimeException()));
|
||||
Flux<TokenBuffer> result = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, true, -1);
|
||||
Flux<TokenBuffer> result = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, true,
|
||||
false, -1);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(RuntimeException.class)
|
||||
@@ -252,19 +258,49 @@ public class Jackson2TokenizerTests extends AbstractLeakCheckingTestCase {
|
||||
@Test // SPR-16521
|
||||
public void jsonEOFExceptionIsWrappedAsDecodingError() {
|
||||
Flux<DataBuffer> source = Flux.just(stringBuffer("{\"status\": \"noClosingQuote}"));
|
||||
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, false, -1);
|
||||
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, false,
|
||||
false, -1);
|
||||
|
||||
StepVerifier.create(tokens)
|
||||
.expectError(DecodingException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void useBigDecimalForFloats() {
|
||||
for (boolean useBigDecimalForFloats : Arrays.asList(false, true)) {
|
||||
Flux<DataBuffer> source = Flux.just(stringBuffer("1E+2"));
|
||||
Flux<TokenBuffer> tokens =
|
||||
Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, false,
|
||||
useBigDecimalForFloats, -1);
|
||||
|
||||
StepVerifier.create(tokens)
|
||||
.assertNext(tokenBuffer -> {
|
||||
try {
|
||||
JsonParser parser = tokenBuffer.asParser();
|
||||
JsonToken token = parser.nextToken();
|
||||
assertEquals(JsonToken.VALUE_NUMBER_FLOAT, token);
|
||||
JsonParser.NumberType numberType = parser.getNumberType();
|
||||
if (useBigDecimalForFloats) {
|
||||
assertEquals(JsonParser.NumberType.BIG_DECIMAL, numberType);
|
||||
}
|
||||
else {
|
||||
assertEquals(JsonParser.NumberType.DOUBLE, numberType);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
fail(ex.getMessage());
|
||||
}
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
|
||||
private Flux<String> decode(List<String> source, boolean tokenize, int maxInMemorySize) {
|
||||
|
||||
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
|
||||
Flux.fromIterable(source).map(this::stringBuffer),
|
||||
this.jsonFactory, this.objectMapper, tokenize, maxInMemorySize);
|
||||
this.jsonFactory, this.objectMapper, tokenize, false, maxInMemorySize);
|
||||
|
||||
return tokens
|
||||
.map(tokenBuffer -> {
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -25,9 +25,9 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Representation of a WebSocket message.
|
||||
* <p>See static factory methods in {@link WebSocketSession} for creating messages
|
||||
* with the {@link org.springframework.core.io.buffer.DataBufferFactory
|
||||
* DataBufferFactory} for the session.
|
||||
*
|
||||
* <p>See static factory methods in {@link WebSocketSession} for creating messages with
|
||||
* the {@link org.springframework.core.io.buffer.DataBufferFactory} for the session.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
@@ -139,6 +139,7 @@ public class WebSocketMessage {
|
||||
return "WebSocket " + this.type.name() + " message (" + this.payload.readableByteCount() + " bytes)";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WebSocket message types.
|
||||
*/
|
||||
@@ -158,7 +159,7 @@ public class WebSocketMessage {
|
||||
/**
|
||||
* WebSocket pong.
|
||||
*/
|
||||
PONG;
|
||||
PONG
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1618,11 +1618,10 @@ the Spring Validation API, as the following example shows:
|
||||
==== Configuring Custom Constraints
|
||||
|
||||
Each bean validation constraint consists of two parts:
|
||||
* A `@Constraint` annotation
|
||||
that declares the constraint and its configurable properties.
|
||||
* An implementation
|
||||
of the `javax.validation.ConstraintValidator` interface that implements the constraint's
|
||||
behavior.
|
||||
|
||||
* A `@Constraint` annotation that declares the constraint and its configurable properties.
|
||||
* An implementation of the `javax.validation.ConstraintValidator` interface that implements
|
||||
the constraint's behavior.
|
||||
|
||||
To associate a declaration with an implementation, each `@Constraint` annotation
|
||||
references a corresponding `ConstraintValidator` implementation class. At runtime, a
|
||||
|
||||
@@ -973,10 +973,10 @@ method API.
|
||||
* <<web-reactive.adoc#webflux-client, WebClient>>: a non-blocking, reactive alternative
|
||||
that supports both synchronous and asynchronous as well as streaming scenarios.
|
||||
|
||||
NOTE: As of 5.0, the non-blocking, reactive `WebClient` offers a modern alternative to the
|
||||
`RestTemplate` with efficient support for both synchronous and asynchronous as well as streaming
|
||||
scenarios. The `RestTemplate` will be deprecated in a future version and will not have
|
||||
major new features added going forward.
|
||||
NOTE: As of 5.0 the `RestTemplate` is in maintenance mode, with only minor requests for
|
||||
changes and bugs to be accepted going forward. Please, consider using the
|
||||
<<web-reactive.adoc#webflux-client, WebClient>> which offers a more modern API and
|
||||
supports sync, async, and streaming scenarios.
|
||||
|
||||
|
||||
[[rest-resttemplate]]
|
||||
|
||||
@@ -3102,7 +3102,7 @@ use `FormatterRegistrar` implementations.
|
||||
By default, if <<core.adoc#validation-beanvalidation-overview, Bean Validation>> is present
|
||||
on the classpath (for example, the Hibernate Validator), the `LocalValidatorFactoryBean`
|
||||
is registered as a global <<core.adoc#validator,validator>> for use with `@Valid` and
|
||||
`Validated` on `@Controller` method arguments.
|
||||
`@Validated` on `@Controller` method arguments.
|
||||
|
||||
In your Java configuration, you can customize the global `Validator` instance,
|
||||
as the following example shows:
|
||||
|
||||
@@ -13,12 +13,10 @@ This section describes options for client-side access to REST endpoints.
|
||||
Spring REST client and exposes a simple, template-method API over underlying HTTP client
|
||||
libraries.
|
||||
|
||||
NOTE: As of 5.0, the non-blocking, reactive `WebClient` offers a modern alternative to the
|
||||
`RestTemplate`, with efficient support for both
|
||||
<<web-reactive.adoc#webflux-client-synchronous, synchronous and asynchronous>>, as well as
|
||||
streaming scenarios. The `RestTemplate` will be deprecated in a future version and will
|
||||
not have major new features added going forward.
|
||||
|
||||
NOTE: As of 5.0 the `RestTemplate` is in maintenance mode, with only minor requests for
|
||||
changes and bugs to be accepted going forward. Please, consider using the
|
||||
<<web-reactive.adoc#webflux-client, WebClient>> which offers a more modern API and
|
||||
supports sync, async, and streaming scenarios.
|
||||
|
||||
See <<integration.adoc#rest-client-access, REST Endpoints>> for details.
|
||||
|
||||
|
||||
@@ -4251,7 +4251,7 @@ FormatterRegistrar implementations.
|
||||
By default, if <<core.adoc#validation-beanvalidation-overview, Bean Validation>> is present
|
||||
on the classpath (for example, Hibernate Validator), the `LocalValidatorFactoryBean` is
|
||||
registered as a global <<core.adoc#validator, Validator>> for use with `@Valid` and
|
||||
`Validated` on controller method arguments.
|
||||
`@Validated` on `@Controller` method arguments.
|
||||
|
||||
In Java configuration, you can customize the global `Validator` instance, as the
|
||||
following example shows:
|
||||
|
||||
Vendored
+7
@@ -261,6 +261,13 @@ Per the LICENSE file in the Objenesis ZIP distribution downloaded from
|
||||
http://objenesis.org/download.html, Objenesis 3.0.1 is licensed under the
|
||||
Apache License, version 2.0, the text of which is included above.
|
||||
|
||||
Per the NOTICE file in the Objenesis ZIP distribution downloaded from
|
||||
http://objenesis.org/download.html and corresponding to section 4d of the
|
||||
Apache License, Version 2.0, in this case for Objenesis:
|
||||
|
||||
Objenesis
|
||||
Copyright 2006-2018 Joe Walnes, Henri Tremblay, Leonardo Mesquita
|
||||
|
||||
|
||||
===============================================================================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user