Merge branch '5.1.x'
This commit is contained in:
commit
2e7ed915cd
|
@ -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.
|
||||
|
@ -34,6 +34,7 @@ import java.util.Set;
|
|||
import javax.validation.Constraint;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Payload;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.Validation;
|
||||
|
@ -50,11 +51,13 @@ import org.springframework.beans.BeanWrapperImpl;
|
|||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
||||
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.StringContains.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
|
@ -73,7 +76,7 @@ public class SpringValidatorAdapterTests {
|
|||
@Before
|
||||
public void setupSpringValidatorAdapter() {
|
||||
messageSource.addMessage("Size", Locale.ENGLISH, "Size of {0} is must be between {2} and {1}");
|
||||
messageSource.addMessage("Same", Locale.ENGLISH, "{2} must be same value with {1}");
|
||||
messageSource.addMessage("Same", Locale.ENGLISH, "{2} must be same value as {1}");
|
||||
messageSource.addMessage("password", Locale.ENGLISH, "Password");
|
||||
messageSource.addMessage("confirmPassword", Locale.ENGLISH, "Password(Confirm)");
|
||||
}
|
||||
|
@ -96,8 +99,11 @@ public class SpringValidatorAdapterTests {
|
|||
|
||||
assertThat(errors.getFieldErrorCount("password"), is(1));
|
||||
assertThat(errors.getFieldValue("password"), is("pass"));
|
||||
assertThat(messageSource.getMessage(errors.getFieldError("password"), Locale.ENGLISH),
|
||||
is("Size of Password is must be between 8 and 128"));
|
||||
FieldError error = errors.getFieldError("password");
|
||||
assertNotNull(error);
|
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH), is("Size of Password is must be between 8 and 128"));
|
||||
assertTrue(error.contains(ConstraintViolation.class));
|
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("password"));
|
||||
}
|
||||
|
||||
@Test // SPR-13406
|
||||
|
@ -111,8 +117,11 @@ public class SpringValidatorAdapterTests {
|
|||
|
||||
assertThat(errors.getFieldErrorCount("password"), is(1));
|
||||
assertThat(errors.getFieldValue("password"), is("password"));
|
||||
assertThat(messageSource.getMessage(errors.getFieldError("password"), Locale.ENGLISH),
|
||||
is("Password must be same value with Password(Confirm)"));
|
||||
FieldError error = errors.getFieldError("password");
|
||||
assertNotNull(error);
|
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH), is("Password must be same value as Password(Confirm)"));
|
||||
assertTrue(error.contains(ConstraintViolation.class));
|
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("password"));
|
||||
}
|
||||
|
||||
@Test // SPR-13406
|
||||
|
@ -127,10 +136,16 @@ public class SpringValidatorAdapterTests {
|
|||
assertThat(errors.getFieldErrorCount("email"), is(1));
|
||||
assertThat(errors.getFieldValue("email"), is("test@example.com"));
|
||||
assertThat(errors.getFieldErrorCount("confirmEmail"), is(1));
|
||||
assertThat(messageSource.getMessage(errors.getFieldError("email"), Locale.ENGLISH),
|
||||
is("email must be same value with confirmEmail"));
|
||||
assertThat(messageSource.getMessage(errors.getFieldError("confirmEmail"), Locale.ENGLISH),
|
||||
is("Email required"));
|
||||
FieldError error1 = errors.getFieldError("email");
|
||||
FieldError error2 = errors.getFieldError("confirmEmail");
|
||||
assertNotNull(error1);
|
||||
assertNotNull(error2);
|
||||
assertThat(messageSource.getMessage(error1, Locale.ENGLISH), is("email must be same value as confirmEmail"));
|
||||
assertThat(messageSource.getMessage(error2, Locale.ENGLISH), is("Email required"));
|
||||
assertTrue(error1.contains(ConstraintViolation.class));
|
||||
assertThat(error1.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("email"));
|
||||
assertTrue(error2.contains(ConstraintViolation.class));
|
||||
assertThat(error2.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("confirmEmail"));
|
||||
}
|
||||
|
||||
@Test // SPR-15123
|
||||
|
@ -147,10 +162,34 @@ public class SpringValidatorAdapterTests {
|
|||
assertThat(errors.getFieldErrorCount("email"), is(1));
|
||||
assertThat(errors.getFieldValue("email"), is("test@example.com"));
|
||||
assertThat(errors.getFieldErrorCount("confirmEmail"), is(1));
|
||||
assertThat(messageSource.getMessage(errors.getFieldError("email"), Locale.ENGLISH),
|
||||
is("email must be same value with confirmEmail"));
|
||||
assertThat(messageSource.getMessage(errors.getFieldError("confirmEmail"), Locale.ENGLISH),
|
||||
is("Email required"));
|
||||
FieldError error1 = errors.getFieldError("email");
|
||||
FieldError error2 = errors.getFieldError("confirmEmail");
|
||||
assertNotNull(error1);
|
||||
assertNotNull(error2);
|
||||
assertThat(messageSource.getMessage(error1, Locale.ENGLISH), is("email must be same value as confirmEmail"));
|
||||
assertThat(messageSource.getMessage(error2, Locale.ENGLISH), is("Email required"));
|
||||
assertTrue(error1.contains(ConstraintViolation.class));
|
||||
assertThat(error1.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("email"));
|
||||
assertTrue(error2.contains(ConstraintViolation.class));
|
||||
assertThat(error2.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("confirmEmail"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatternMessage() {
|
||||
TestBean testBean = new TestBean();
|
||||
testBean.setEmail("X");
|
||||
testBean.setConfirmEmail("X");
|
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");
|
||||
validatorAdapter.validate(testBean, errors);
|
||||
|
||||
assertThat(errors.getFieldErrorCount("email"), is(1));
|
||||
assertThat(errors.getFieldValue("email"), is("X"));
|
||||
FieldError error = errors.getFieldError("email");
|
||||
assertNotNull(error);
|
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH), containsString("[\\w.'-]{1,}@[\\w.'-]{1,}"));
|
||||
assertTrue(error.contains(ConstraintViolation.class));
|
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("email"));
|
||||
}
|
||||
|
||||
@Test // SPR-16177
|
||||
|
@ -243,6 +282,7 @@ public class SpringValidatorAdapterTests {
|
|||
|
||||
private String confirmPassword;
|
||||
|
||||
@Pattern(regexp = "[\\w.'-]{1,}@[\\w.'-]{1,}")
|
||||
private String email;
|
||||
|
||||
@Pattern(regexp = "[\\p{L} -]*", message = "Email required")
|
||||
|
@ -403,13 +443,13 @@ public class SpringValidatorAdapterTests {
|
|||
|
||||
private Integer id;
|
||||
|
||||
@javax.validation.constraints.NotNull
|
||||
@NotNull
|
||||
private String name;
|
||||
|
||||
@javax.validation.constraints.NotNull
|
||||
@NotNull
|
||||
private Integer age;
|
||||
|
||||
@javax.validation.constraints.NotNull
|
||||
@NotNull
|
||||
private Parent parent;
|
||||
|
||||
public Integer getId() {
|
||||
|
|
|
@ -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.
|
||||
|
@ -284,6 +284,9 @@ public class ValidatorFactoryTests {
|
|||
errors.initConversion(new DefaultConversionService());
|
||||
validator.validate(listContainer, errors);
|
||||
|
||||
FieldError fieldError = errors.getFieldError("list[1]");
|
||||
assertNotNull(fieldError);
|
||||
assertEquals("X", fieldError.getRejectedValue());
|
||||
assertEquals("X", errors.getFieldValue("list[1]"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -57,9 +57,9 @@ public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
|
|||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
boolean candidateFound = false;
|
||||
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
|
||||
for (String annoType : annoTypes) {
|
||||
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
|
||||
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
|
||||
for (String annType : annTypes) {
|
||||
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
|
||||
if (candidate == null) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -534,7 +534,7 @@ class ConfigurationClassParser {
|
|||
if (visited.add(sourceClass)) {
|
||||
for (SourceClass annotation : sourceClass.getAnnotations()) {
|
||||
String annName = annotation.getMetadata().getClassName();
|
||||
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
|
||||
if (!annName.equals(Import.class.getName())) {
|
||||
collectImports(annotation, imports, visited);
|
||||
}
|
||||
}
|
||||
|
@ -542,8 +542,6 @@ class ConfigurationClassParser {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
|
||||
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
|
||||
|
||||
|
@ -565,8 +563,7 @@ class ConfigurationClassParser {
|
|||
ParserStrategyUtils.invokeAwareMethods(
|
||||
selector, this.environment, this.resourceLoader, this.registry);
|
||||
if (selector instanceof DeferredImportSelector) {
|
||||
this.deferredImportSelectorHandler.handle(
|
||||
configClass, (DeferredImportSelector) selector);
|
||||
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
|
||||
}
|
||||
else {
|
||||
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
|
||||
|
@ -1018,13 +1015,32 @@ class ConfigurationClassParser {
|
|||
|
||||
public Set<SourceClass> getAnnotations() {
|
||||
Set<SourceClass> result = new LinkedHashSet<>();
|
||||
for (String className : this.metadata.getAnnotationTypes()) {
|
||||
try {
|
||||
result.add(getRelated(className));
|
||||
if (this.source instanceof Class) {
|
||||
Class<?> sourceClass = (Class<?>) this.source;
|
||||
for (Annotation ann : sourceClass.getAnnotations()) {
|
||||
Class<?> annType = ann.annotationType();
|
||||
if (!annType.getName().startsWith("java")) {
|
||||
try {
|
||||
result.add(asSourceClass(annType));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// An annotation not present on the classpath is being ignored
|
||||
// by the JVM's class loading -> ignore here as well.
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// An annotation not present on the classpath is being ignored
|
||||
// by the JVM's class loading -> ignore here as well.
|
||||
}
|
||||
else {
|
||||
for (String className : this.metadata.getAnnotationTypes()) {
|
||||
if (!className.startsWith("java")) {
|
||||
try {
|
||||
result.add(getRelated(className));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// An annotation not present on the classpath is being ignored
|
||||
// by the JVM's class loading -> ignore here as well.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -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.
|
||||
|
@ -287,6 +287,12 @@ public abstract class AbstractMessageSource extends MessageSourceSupport impleme
|
|||
String defaultMessage = resolvable.getDefaultMessage();
|
||||
String[] codes = resolvable.getCodes();
|
||||
if (defaultMessage != null) {
|
||||
if (resolvable instanceof DefaultMessageSourceResolvable &&
|
||||
!((DefaultMessageSourceResolvable) resolvable).shouldRenderDefaultMessage()) {
|
||||
// Given default message does not contain any argument placeholders
|
||||
// (and isn't escaped for alwaysUseMessageFormat either) -> return as-is.
|
||||
return defaultMessage;
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(codes) && defaultMessage.equals(codes[0])) {
|
||||
// Never format a code-as-default-message, even with alwaysUseMessageFormat=true
|
||||
return defaultMessage;
|
||||
|
|
|
@ -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.
|
||||
|
@ -129,13 +129,28 @@ public class DefaultMessageSourceResolvable implements MessageSourceResolvable,
|
|||
return this.defaultMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the specified default message needs to be rendered for
|
||||
* substituting placeholders and/or {@link java.text.MessageFormat} escaping.
|
||||
* @return {@code true} if the default message may contain argument placeholders;
|
||||
* {@code false} if it definitely does not contain placeholders or custom escaping
|
||||
* and can therefore be simply exposed as-is
|
||||
* @since 5.1.7
|
||||
* @see #getDefaultMessage()
|
||||
* @see #getArguments()
|
||||
* @see AbstractMessageSource#renderDefaultMessage
|
||||
*/
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a default String representation for this MessageSourceResolvable:
|
||||
* including codes, arguments, and default message.
|
||||
*/
|
||||
protected final String resolvableToString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
StringBuilder result = new StringBuilder(64);
|
||||
result.append("codes [").append(StringUtils.arrayToDelimitedString(this.codes, ","));
|
||||
result.append("]; arguments [").append(StringUtils.arrayToDelimitedString(this.arguments, ","));
|
||||
result.append("]; default message [").append(this.defaultMessage).append(']');
|
||||
|
|
|
@ -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.
|
||||
|
@ -148,6 +148,7 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
|||
* @param violations the JSR-303 ConstraintViolation results
|
||||
* @param errors the Spring errors object to register to
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
protected void processConstraintViolations(Set<ConstraintViolation<Object>> violations, Errors errors) {
|
||||
for (ConstraintViolation<Object> violation : violations) {
|
||||
String field = determineField(violation);
|
||||
|
@ -165,7 +166,12 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
|||
if (nestedField.isEmpty()) {
|
||||
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
|
||||
ObjectError error = new ObjectError(
|
||||
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage());
|
||||
errors.getObjectName(), errorCodes, errorArgs, violation.getMessage()) {
|
||||
@Override
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
error.wrap(violation);
|
||||
bindingResult.addError(error);
|
||||
}
|
||||
|
@ -173,7 +179,12 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
|||
Object rejectedValue = getRejectedValue(field, violation, bindingResult);
|
||||
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
|
||||
FieldError error = new FieldError(errors.getObjectName(), nestedField,
|
||||
rejectedValue, false, errorCodes, errorArgs, violation.getMessage());
|
||||
rejectedValue, false, errorCodes, errorArgs, violation.getMessage()) {
|
||||
@Override
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
error.wrap(violation);
|
||||
bindingResult.addError(error);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -54,6 +54,7 @@ import org.springframework.validation.FieldError;
|
|||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.StringContains.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
|
@ -170,6 +171,24 @@ public class SpringValidatorAdapterTests {
|
|||
assertThat(error2.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("confirmEmail"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatternMessage() {
|
||||
TestBean testBean = new TestBean();
|
||||
testBean.setEmail("X");
|
||||
testBean.setConfirmEmail("X");
|
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");
|
||||
validatorAdapter.validate(testBean, errors);
|
||||
|
||||
assertThat(errors.getFieldErrorCount("email"), is(1));
|
||||
assertThat(errors.getFieldValue("email"), is("X"));
|
||||
FieldError error = errors.getFieldError("email");
|
||||
assertNotNull(error);
|
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH), containsString("[\\w.'-]{1,}@[\\w.'-]{1,}"));
|
||||
assertTrue(error.contains(ConstraintViolation.class));
|
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString(), is("email"));
|
||||
}
|
||||
|
||||
@Test // SPR-16177
|
||||
public void testWithList() {
|
||||
Parent parent = new Parent();
|
||||
|
@ -218,6 +237,7 @@ public class SpringValidatorAdapterTests {
|
|||
|
||||
private String confirmPassword;
|
||||
|
||||
@Pattern(regexp = "[\\w.'-]{1,}@[\\w.'-]{1,}")
|
||||
private String email;
|
||||
|
||||
@Pattern(regexp = "[\\p{L} -]*", message = "Email required")
|
||||
|
|
|
@ -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.
|
||||
|
@ -284,6 +284,8 @@ public class ValidatorFactoryTests {
|
|||
validator.validate(listContainer, errors);
|
||||
|
||||
FieldError fieldError = errors.getFieldError("list[1]");
|
||||
assertNotNull(fieldError);
|
||||
assertEquals("X", fieldError.getRejectedValue());
|
||||
assertEquals("X", errors.getFieldValue("list[1]"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -205,19 +205,13 @@ public class AnnotationMetadataTests {
|
|||
assertThat("length of basePackageClasses[]", basePackageClasses.length, is(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* https://jira.spring.io/browse/SPR-11649
|
||||
*/
|
||||
@Test
|
||||
@Test // SPR-11649
|
||||
public void multipleAnnotationsWithIdenticalAttributeNamesUsingStandardAnnotationMetadata() {
|
||||
AnnotationMetadata metadata = new StandardAnnotationMetadata(NamedAnnotationsClass.class);
|
||||
assertMultipleAnnotationsWithIdenticalAttributeNames(metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://jira.spring.io/browse/SPR-11649
|
||||
*/
|
||||
@Test
|
||||
@Test // SPR-11649
|
||||
public void multipleAnnotationsWithIdenticalAttributeNamesUsingAnnotationMetadataReadingVisitor() throws Exception {
|
||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
|
||||
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(NamedAnnotationsClass.class.getName());
|
||||
|
@ -225,19 +219,13 @@ public class AnnotationMetadataTests {
|
|||
assertMultipleAnnotationsWithIdenticalAttributeNames(metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://jira.spring.io/browse/SPR-11649
|
||||
*/
|
||||
@Test
|
||||
@Test // SPR-11649
|
||||
public void composedAnnotationWithMetaAnnotationsWithIdenticalAttributeNamesUsingStandardAnnotationMetadata() {
|
||||
AnnotationMetadata metadata = new StandardAnnotationMetadata(NamedComposedAnnotationClass.class);
|
||||
assertMultipleAnnotationsWithIdenticalAttributeNames(metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://jira.spring.io/browse/SPR-11649
|
||||
*/
|
||||
@Test
|
||||
@Test // SPR-11649
|
||||
public void composedAnnotationWithMetaAnnotationsWithIdenticalAttributeNamesUsingAnnotationMetadataReadingVisitor() throws Exception {
|
||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
|
||||
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(NamedComposedAnnotationClass.class.getName());
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.http.converter.json;
|
|||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
|
@ -62,6 +63,8 @@ import org.springframework.http.HttpLogging;
|
|||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.StaxUtils;
|
||||
|
||||
|
@ -632,24 +635,27 @@ public class Jackson2ObjectMapperBuilder {
|
|||
public void configure(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "ObjectMapper must not be null");
|
||||
|
||||
Map<Object, Module> modulesToRegister = new LinkedHashMap<>();
|
||||
MultiValueMap<Object, Module> modulesToRegister = new LinkedMultiValueMap<>();
|
||||
if (this.findModulesViaServiceLoader) {
|
||||
ObjectMapper.findModules(this.moduleClassLoader).forEach(module -> modulesToRegister.put(module.getTypeId(), module));
|
||||
ObjectMapper.findModules(this.moduleClassLoader).forEach(module -> registerModule(module, modulesToRegister));
|
||||
}
|
||||
else if (this.findWellKnownModules) {
|
||||
registerWellKnownModulesIfAvailable(modulesToRegister);
|
||||
}
|
||||
|
||||
if (this.modules != null) {
|
||||
this.modules.forEach(module -> modulesToRegister.put(module.getTypeId(), module));
|
||||
this.modules.forEach(module -> registerModule(module, modulesToRegister));
|
||||
}
|
||||
if (this.moduleClasses != null) {
|
||||
for (Class<? extends Module> moduleClass : this.moduleClasses) {
|
||||
Module module = BeanUtils.instantiateClass(moduleClass);
|
||||
modulesToRegister.put(module.getTypeId(), module);
|
||||
registerModule(BeanUtils.instantiateClass(moduleClass), modulesToRegister);
|
||||
}
|
||||
}
|
||||
objectMapper.registerModules(modulesToRegister.values());
|
||||
List<Module> modules = new ArrayList<>();
|
||||
for (List<Module> nestedModules : modulesToRegister.values()) {
|
||||
modules.addAll(nestedModules);
|
||||
}
|
||||
objectMapper.registerModules(modules);
|
||||
|
||||
if (this.dateFormat != null) {
|
||||
objectMapper.setDateFormat(this.dateFormat);
|
||||
|
@ -701,6 +707,15 @@ public class Jackson2ObjectMapperBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void registerModule(Module module, MultiValueMap<Object, Module> modulesToRegister) {
|
||||
if (module.getTypeId() == null) {
|
||||
modulesToRegister.add(SimpleModule.class.getName(), module);
|
||||
}
|
||||
else {
|
||||
modulesToRegister.set(module.getTypeId(), module);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Any change to this method should be also applied to spring-jms and spring-messaging
|
||||
// MappingJackson2MessageConverter default constructors
|
||||
|
@ -747,12 +762,12 @@ public class Jackson2ObjectMapperBuilder {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void registerWellKnownModulesIfAvailable(Map<Object, Module> modulesToRegister) {
|
||||
private void registerWellKnownModulesIfAvailable(MultiValueMap<Object, Module> modulesToRegister) {
|
||||
try {
|
||||
Class<? extends Module> jdk8ModuleClass = (Class<? extends Module>)
|
||||
ClassUtils.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", this.moduleClassLoader);
|
||||
Module jdk8Module = BeanUtils.instantiateClass(jdk8ModuleClass);
|
||||
modulesToRegister.put(jdk8Module.getTypeId(), jdk8Module);
|
||||
modulesToRegister.set(jdk8Module.getTypeId(), jdk8Module);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// jackson-datatype-jdk8 not available
|
||||
|
@ -762,7 +777,7 @@ public class Jackson2ObjectMapperBuilder {
|
|||
Class<? extends Module> javaTimeModuleClass = (Class<? extends Module>)
|
||||
ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", this.moduleClassLoader);
|
||||
Module javaTimeModule = BeanUtils.instantiateClass(javaTimeModuleClass);
|
||||
modulesToRegister.put(javaTimeModule.getTypeId(), javaTimeModule);
|
||||
modulesToRegister.set(javaTimeModule.getTypeId(), javaTimeModule);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// jackson-datatype-jsr310 not available
|
||||
|
@ -774,7 +789,7 @@ public class Jackson2ObjectMapperBuilder {
|
|||
Class<? extends Module> jodaModuleClass = (Class<? extends Module>)
|
||||
ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", this.moduleClassLoader);
|
||||
Module jodaModule = BeanUtils.instantiateClass(jodaModuleClass);
|
||||
modulesToRegister.put(jodaModule.getTypeId(), jodaModule);
|
||||
modulesToRegister.set(jodaModule.getTypeId(), jodaModule);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// jackson-datatype-joda not available
|
||||
|
@ -787,7 +802,7 @@ public class Jackson2ObjectMapperBuilder {
|
|||
Class<? extends Module> kotlinModuleClass = (Class<? extends Module>)
|
||||
ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader);
|
||||
Module kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass);
|
||||
modulesToRegister.put(kotlinModule.getTypeId(), kotlinModule);
|
||||
modulesToRegister.set(kotlinModule.getTypeId(), kotlinModule);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
if (!kotlinWarningLogged) {
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TimeZone;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
||||
import com.fasterxml.jackson.annotation.JsonFilter;
|
||||
|
@ -329,6 +330,24 @@ public class Jackson2ObjectMapperBuilderTests {
|
|||
assertNotNull(demoPojo.getOffsetDateTime());
|
||||
}
|
||||
|
||||
@Test // gh-22740
|
||||
public void registerMultipleModulesWithNullTypeId() {
|
||||
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
|
||||
SimpleModule fooModule = new SimpleModule();
|
||||
fooModule.addSerializer(new FooSerializer());
|
||||
SimpleModule barModule = new SimpleModule();
|
||||
barModule.addSerializer(new BarSerializer());
|
||||
builder.modulesToInstall(fooModule, barModule);
|
||||
ObjectMapper objectMapper = builder.build();
|
||||
assertEquals(1, StreamSupport
|
||||
.stream(getSerializerFactoryConfig(objectMapper).serializers().spliterator(), false)
|
||||
.filter(s -> s.findSerializer(null, SimpleType.construct(Foo.class), null) != null)
|
||||
.count());
|
||||
assertEquals(1, StreamSupport
|
||||
.stream(getSerializerFactoryConfig(objectMapper).serializers().spliterator(), false)
|
||||
.filter(s -> s.findSerializer(null, SimpleType.construct(Bar.class), null) != null)
|
||||
.count());
|
||||
}
|
||||
|
||||
private static SerializerFactoryConfig getSerializerFactoryConfig(ObjectMapper objectMapper) {
|
||||
return ((BasicSerializerFactory) objectMapper.getSerializerFactory()).getFactoryConfig();
|
||||
|
@ -680,4 +699,29 @@ public class Jackson2ObjectMapperBuilderTests {
|
|||
public static class MyXmlFactory extends XmlFactory {
|
||||
}
|
||||
|
||||
static class Foo {}
|
||||
|
||||
static class Bar {}
|
||||
|
||||
static class FooSerializer extends JsonSerializer<Foo> {
|
||||
@Override
|
||||
public void serialize(Foo value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Foo> handledType() {
|
||||
return Foo.class;
|
||||
}
|
||||
}
|
||||
|
||||
static class BarSerializer extends JsonSerializer<Bar> {
|
||||
@Override
|
||||
public void serialize(Bar value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
}
|
||||
@Override
|
||||
public Class<Bar> handledType() {
|
||||
return Bar.class;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue