mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
17 Commits
main
...
v5.2.11.RELEASE
| Author | SHA1 | Date | |
|---|---|---|---|
| 41fd015eed | |||
| e9416b369e | |||
| cde95e1446 | |||
| da4e37dc1d | |||
| 010d0947c7 | |||
| 5b06c23a1b | |||
| 7881329cf7 | |||
| 58aa0659cc | |||
| dbbedc6c86 | |||
| 09c1e986b9 | |||
| 10bff054a9 | |||
| e713e0d6d5 | |||
| b4f8fc8177 | |||
| 8c3cdc6118 | |||
| 01827fd8d2 | |||
| af1d721aa3 | |||
| 0acb1e5513 |
+1
-1
@@ -116,7 +116,7 @@ configure(allprojects) { project ->
|
||||
dependency "net.sf.ehcache:ehcache:2.10.6"
|
||||
dependency "org.ehcache:jcache:1.0.1"
|
||||
dependency "org.ehcache:ehcache:3.4.0"
|
||||
dependency "org.hibernate:hibernate-core:5.4.22.Final"
|
||||
dependency "org.hibernate:hibernate-core:5.4.23.Final"
|
||||
dependency "org.hibernate:hibernate-validator:6.1.6.Final"
|
||||
dependency "org.webjars:webjars-locator-core:0.46"
|
||||
dependency "org.webjars:underscorejs:1.8.3"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.2.11.BUILD-SNAPSHOT
|
||||
version=5.2.11.RELEASE
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
+6
-4
@@ -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.
|
||||
@@ -219,10 +219,12 @@ public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint,
|
||||
@Override
|
||||
@Nullable
|
||||
public String[] getParameterNames() {
|
||||
if (this.parameterNames == null) {
|
||||
this.parameterNames = parameterNameDiscoverer.getParameterNames(getMethod());
|
||||
String[] parameterNames = this.parameterNames;
|
||||
if (parameterNames == null) {
|
||||
parameterNames = parameterNameDiscoverer.getParameterNames(getMethod());
|
||||
this.parameterNames = parameterNames;
|
||||
}
|
||||
return this.parameterNames;
|
||||
return parameterNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-4
@@ -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.
|
||||
@@ -422,9 +422,12 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw createNotWritablePropertyException(tokens.canonicalName);
|
||||
if (this.suppressNotWritablePropertyException) {
|
||||
// Optimization for common ignoreUnknown=true scenario since the
|
||||
// exception would be caught and swallowed higher up anyway...
|
||||
return;
|
||||
}
|
||||
throw createNotWritablePropertyException(tokens.canonicalName);
|
||||
}
|
||||
|
||||
Object oldValue = null;
|
||||
@@ -806,7 +809,6 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
* @param propertyPath property path, which may be nested
|
||||
* @return a property accessor for the target bean
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // avoid nested generic
|
||||
protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
|
||||
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
|
||||
// Handle nested properties recursively.
|
||||
|
||||
+33
-20
@@ -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.
|
||||
@@ -40,6 +40,8 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl
|
||||
|
||||
private boolean autoGrowNestedPaths = false;
|
||||
|
||||
boolean suppressNotWritablePropertyException = false;
|
||||
|
||||
|
||||
@Override
|
||||
public void setExtractOldValueForEditor(boolean extractOldValueForEditor) {
|
||||
@@ -89,30 +91,41 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl
|
||||
List<PropertyAccessException> propertyAccessExceptions = null;
|
||||
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
|
||||
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
|
||||
for (PropertyValue pv : propertyValues) {
|
||||
try {
|
||||
// This method may throw any BeansException, which won't be caught
|
||||
|
||||
if (ignoreUnknown) {
|
||||
this.suppressNotWritablePropertyException = true;
|
||||
}
|
||||
try {
|
||||
for (PropertyValue pv : propertyValues) {
|
||||
// setPropertyValue may throw any BeansException, which won't be caught
|
||||
// here, if there is a critical failure such as no matching field.
|
||||
// We can attempt to deal only with less serious exceptions.
|
||||
setPropertyValue(pv);
|
||||
}
|
||||
catch (NotWritablePropertyException ex) {
|
||||
if (!ignoreUnknown) {
|
||||
throw ex;
|
||||
try {
|
||||
setPropertyValue(pv);
|
||||
}
|
||||
// Otherwise, just ignore it and continue...
|
||||
}
|
||||
catch (NullValueInNestedPathException ex) {
|
||||
if (!ignoreInvalid) {
|
||||
throw ex;
|
||||
catch (NotWritablePropertyException ex) {
|
||||
if (!ignoreUnknown) {
|
||||
throw ex;
|
||||
}
|
||||
// Otherwise, just ignore it and continue...
|
||||
}
|
||||
// Otherwise, just ignore it and continue...
|
||||
}
|
||||
catch (PropertyAccessException ex) {
|
||||
if (propertyAccessExceptions == null) {
|
||||
propertyAccessExceptions = new ArrayList<>();
|
||||
catch (NullValueInNestedPathException ex) {
|
||||
if (!ignoreInvalid) {
|
||||
throw ex;
|
||||
}
|
||||
// Otherwise, just ignore it and continue...
|
||||
}
|
||||
propertyAccessExceptions.add(ex);
|
||||
catch (PropertyAccessException ex) {
|
||||
if (propertyAccessExceptions == null) {
|
||||
propertyAccessExceptions = new ArrayList<>();
|
||||
}
|
||||
propertyAccessExceptions.add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (ignoreUnknown) {
|
||||
this.suppressNotWritablePropertyException = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -92,8 +92,7 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
|
||||
@Override
|
||||
protected NotWritablePropertyException createNotWritablePropertyException(String propertyName) {
|
||||
PropertyMatches matches = PropertyMatches.forField(propertyName, getRootClass());
|
||||
throw new NotWritablePropertyException(
|
||||
getRootClass(), getNestedPath() + propertyName,
|
||||
throw new NotWritablePropertyException(getRootClass(), getNestedPath() + propertyName,
|
||||
matches.buildErrorMessage(), matches.getPossibleMatches());
|
||||
}
|
||||
|
||||
|
||||
+7
-8
@@ -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.
|
||||
@@ -613,7 +613,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
|
||||
private final boolean required;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
private volatile boolean cached;
|
||||
|
||||
@Nullable
|
||||
private volatile Object cachedFieldValue;
|
||||
@@ -644,21 +644,20 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
Object cachedFieldValue = null;
|
||||
if (value != null || this.required) {
|
||||
this.cachedFieldValue = desc;
|
||||
cachedFieldValue = desc;
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
||||
this.cachedFieldValue = new ShortcutDependencyDescriptor(
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(
|
||||
desc, autowiredBeanName, field.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.cachedFieldValue = null;
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
@@ -678,7 +677,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
|
||||
private final boolean required;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
private volatile boolean cached;
|
||||
|
||||
@Nullable
|
||||
private volatile Object[] cachedMethodArguments;
|
||||
|
||||
+2
-12
@@ -26,9 +26,6 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -69,8 +66,6 @@ public class InjectionMetadata {
|
||||
};
|
||||
|
||||
|
||||
private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
private final Collection<InjectedElement> injectedElements;
|
||||
@@ -110,9 +105,6 @@ public class InjectionMetadata {
|
||||
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
|
||||
beanDefinition.registerExternallyManagedConfigMember(member);
|
||||
checkedElements.add(element);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.checkedElements = checkedElements;
|
||||
@@ -124,9 +116,6 @@ public class InjectionMetadata {
|
||||
(checkedElements != null ? checkedElements : this.injectedElements);
|
||||
if (!elementsToIterate.isEmpty()) {
|
||||
for (InjectedElement element : elementsToIterate) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
|
||||
}
|
||||
element.inject(target, beanName, pvs);
|
||||
}
|
||||
}
|
||||
@@ -157,7 +146,8 @@ public class InjectionMetadata {
|
||||
* @since 5.2
|
||||
*/
|
||||
public static InjectionMetadata forElements(Collection<InjectedElement> elements, Class<?> clazz) {
|
||||
return (elements.isEmpty() ? InjectionMetadata.EMPTY : new InjectionMetadata(clazz, elements));
|
||||
return (elements.isEmpty() ? new InjectionMetadata(clazz, Collections.emptyList()) :
|
||||
new InjectionMetadata(clazz, elements));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
@@ -43,6 +43,15 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class FullyQualifiedAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
|
||||
|
||||
/**
|
||||
* A convenient constant for a default {@code FullyQualifiedAnnotationBeanNameGenerator}
|
||||
* instance, as used for configuration-level import purposes.
|
||||
* @since 5.2.11
|
||||
*/
|
||||
public static final FullyQualifiedAnnotationBeanNameGenerator INSTANCE =
|
||||
new FullyQualifiedAnnotationBeanNameGenerator();
|
||||
|
||||
|
||||
@Override
|
||||
protected String buildDefaultBeanName(BeanDefinition definition) {
|
||||
String beanClassName = definition.getBeanClassName();
|
||||
|
||||
+55
@@ -307,6 +307,23 @@ public class ConfigurationClassProcessingTests {
|
||||
assertThat(tb.getLawyer()).isEqualTo(ctx.getBean(NestedTestBean.class));
|
||||
}
|
||||
|
||||
@Test // gh-26019
|
||||
public void autowiringWithDynamicPrototypeBeanClass() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
|
||||
ConfigWithDynamicPrototype.class, PrototypeDependency.class);
|
||||
|
||||
PrototypeInterface p1 = ctx.getBean(PrototypeInterface.class, 1);
|
||||
assertThat(p1).isInstanceOf(PrototypeOne.class);
|
||||
assertThat(((PrototypeOne) p1).prototypeDependency).isNotNull();
|
||||
|
||||
PrototypeInterface p2 = ctx.getBean(PrototypeInterface.class, 2);
|
||||
assertThat(p2).isInstanceOf(PrototypeTwo.class);
|
||||
|
||||
PrototypeInterface p3 = ctx.getBean(PrototypeInterface.class, 1);
|
||||
assertThat(p3).isInstanceOf(PrototypeOne.class);
|
||||
assertThat(((PrototypeOne) p3).prototypeDependency).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition}
|
||||
@@ -632,4 +649,42 @@ public class ConfigurationClassProcessingTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class PrototypeDependency {
|
||||
}
|
||||
|
||||
interface PrototypeInterface {
|
||||
}
|
||||
|
||||
static class PrototypeOne extends AbstractPrototype {
|
||||
|
||||
@Autowired
|
||||
PrototypeDependency prototypeDependency;
|
||||
|
||||
}
|
||||
|
||||
static class PrototypeTwo extends AbstractPrototype {
|
||||
|
||||
// no autowired dependency here, in contrast to above
|
||||
}
|
||||
|
||||
static class AbstractPrototype implements PrototypeInterface {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithDynamicPrototype {
|
||||
|
||||
@Bean
|
||||
@Scope(value = "prototype")
|
||||
public PrototypeInterface getDemoBean( int i) {
|
||||
switch ( i) {
|
||||
case 1: return new PrototypeOne();
|
||||
case 2:
|
||||
default:
|
||||
return new PrototypeTwo();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-17
@@ -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,8 +18,8 @@ package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
@@ -31,10 +31,11 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Collector implementations that provide various reduction operations for
|
||||
* {@link Collector} implementations that provide various reduction operations for
|
||||
* {@link MergedAnnotation} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 5.2
|
||||
*/
|
||||
public abstract class MergedAnnotationCollectors {
|
||||
@@ -52,13 +53,16 @@ public abstract class MergedAnnotationCollectors {
|
||||
* Create a new {@link Collector} that accumulates merged annotations to a
|
||||
* {@link LinkedHashSet} containing {@linkplain MergedAnnotation#synthesize()
|
||||
* synthesized} versions.
|
||||
* <p>The collector returned by this method is effectively equivalent to
|
||||
* {@code Collectors.mapping(MergedAnnotation::synthesize, Collectors.toCollection(LinkedHashSet::new))}
|
||||
* but avoids the creation of a composite collector.
|
||||
* @param <A> the annotation type
|
||||
* @return a {@link Collector} which collects and synthesizes the
|
||||
* annotations into a {@link Set}
|
||||
*/
|
||||
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, Set<A>> toAnnotationSet() {
|
||||
return Collector.of(ArrayList<A>::new, (list, annotation) -> list.add(annotation.synthesize()),
|
||||
MergedAnnotationCollectors::addAll, LinkedHashSet::new);
|
||||
return Collector.of(LinkedHashSet::new, (set, annotation) -> set.add(annotation.synthesize()),
|
||||
MergedAnnotationCollectors::combiner);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,14 +94,14 @@ public abstract class MergedAnnotationCollectors {
|
||||
IntFunction<R[]> generator) {
|
||||
|
||||
return Collector.of(ArrayList::new, (list, annotation) -> list.add(annotation.synthesize()),
|
||||
MergedAnnotationCollectors::addAll, list -> list.toArray(generator.apply(list.size())));
|
||||
MergedAnnotationCollectors::combiner, list -> list.toArray(generator.apply(list.size())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Collector} that accumulates merged annotations to an
|
||||
* Create a new {@link Collector} that accumulates merged annotations to a
|
||||
* {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object)
|
||||
* added} from each merged annotation
|
||||
* {@link MergedAnnotation#asMap(Adapt...) as a map}.
|
||||
* {@linkplain MergedAnnotation#asMap(Adapt...) as a map}.
|
||||
* @param <A> the annotation type
|
||||
* @param adaptations the adaptations that should be applied to the annotation values
|
||||
* @return a {@link Collector} which collects and synthesizes the
|
||||
@@ -111,13 +115,13 @@ public abstract class MergedAnnotationCollectors {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Collector} that accumulates merged annotations to an
|
||||
* Create a new {@link Collector} that accumulates merged annotations to a
|
||||
* {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object)
|
||||
* added} from each merged annotation
|
||||
* {@link MergedAnnotation#asMap(Adapt...) as a map}.
|
||||
* {@linkplain MergedAnnotation#asMap(Adapt...) as a map}.
|
||||
* @param <A> the annotation type
|
||||
* @param adaptations the adaptations that should be applied to the annotation values
|
||||
* @param finisher the finisher function for the new {@link MultiValueMap}
|
||||
* @param adaptations the adaptations that should be applied to the annotation values
|
||||
* @return a {@link Collector} which collects and synthesizes the
|
||||
* annotations into a {@link LinkedMultiValueMap}
|
||||
* @see #toMultiValueMap(MergedAnnotation.Adapt...)
|
||||
@@ -130,7 +134,7 @@ public abstract class MergedAnnotationCollectors {
|
||||
IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS);
|
||||
return Collector.of(LinkedMultiValueMap::new,
|
||||
(map, annotation) -> annotation.asMap(adaptations).forEach(map::add),
|
||||
MergedAnnotationCollectors::merge, finisher, characteristics);
|
||||
MergedAnnotationCollectors::combiner, finisher, characteristics);
|
||||
}
|
||||
|
||||
|
||||
@@ -138,13 +142,22 @@ public abstract class MergedAnnotationCollectors {
|
||||
return instance == candidate;
|
||||
}
|
||||
|
||||
private static <E, L extends List<E>> L addAll(L list, L additions) {
|
||||
list.addAll(additions);
|
||||
return list;
|
||||
/**
|
||||
* {@link Collector#combiner() Combiner} for collections.
|
||||
* <p>This method is only invoked if the {@link java.util.stream.Stream} is
|
||||
* processed in {@linkplain java.util.stream.Stream#parallel() parallel}.
|
||||
*/
|
||||
private static <E, C extends Collection<E>> C combiner(C collection, C additions) {
|
||||
collection.addAll(additions);
|
||||
return collection;
|
||||
}
|
||||
|
||||
private static <K, V> MultiValueMap<K, V> merge(MultiValueMap<K, V> map,
|
||||
MultiValueMap<K, V> additions) {
|
||||
/**
|
||||
* {@link Collector#combiner() Combiner} for multi-value maps.
|
||||
* <p>This method is only invoked if the {@link java.util.stream.Stream} is
|
||||
* processed in {@linkplain java.util.stream.Stream#parallel() parallel}.
|
||||
*/
|
||||
private static <K, V> MultiValueMap<K, V> combiner(MultiValueMap<K, V> map, MultiValueMap<K, V> additions) {
|
||||
map.addAll(additions);
|
||||
return map;
|
||||
}
|
||||
|
||||
+23
-15
@@ -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.
|
||||
@@ -63,18 +63,20 @@ import org.springframework.util.StringUtils;
|
||||
* <p>Individual expressions can be compiled by calling {@code SpelCompiler.compile(expression)}.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
*/
|
||||
public final class SpelCompiler implements Opcodes {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpelCompiler.class);
|
||||
|
||||
private static final int CLASSES_DEFINED_LIMIT = 100;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpelCompiler.class);
|
||||
|
||||
// A compiler is created for each classloader, it manages a child class loader of that
|
||||
// classloader and the child is used to load the compiled expressions.
|
||||
private static final Map<ClassLoader, SpelCompiler> compilers = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
|
||||
// The child ClassLoader used to load the compiled expression classes
|
||||
private ChildClassLoader ccl;
|
||||
|
||||
@@ -90,7 +92,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
/**
|
||||
* Attempt compilation of the supplied expression. A check is made to see
|
||||
* if it is compilable before compilation proceeds. The check involves
|
||||
* visiting all the nodes in the expression Ast and ensuring enough state
|
||||
* visiting all the nodes in the expression AST and ensuring enough state
|
||||
* is known about them that bytecode can be generated for them.
|
||||
* @param expression the expression to compile
|
||||
* @return an instance of the class implementing the compiled expression,
|
||||
@@ -125,7 +127,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
|
||||
/**
|
||||
* Generate the class that encapsulates the compiled expression and define it.
|
||||
* The generated class will be a subtype of CompiledExpression.
|
||||
* The generated class will be a subtype of CompiledExpression.
|
||||
* @param expressionToCompile the expression to be compiled
|
||||
* @return the expression call, or {@code null} if the decision was to opt out of
|
||||
* compilation during code generation
|
||||
@@ -150,7 +152,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
// Create getValue() method
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "getValue",
|
||||
"(Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;", null,
|
||||
new String[ ]{"org/springframework/expression/EvaluationException"});
|
||||
new String[] {"org/springframework/expression/EvaluationException"});
|
||||
mv.visitCode();
|
||||
|
||||
CodeFlow cf = new CodeFlow(className, cw);
|
||||
@@ -187,7 +189,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
|
||||
/**
|
||||
* Load a compiled expression class. Makes sure the classloaders aren't used too much
|
||||
* because they anchor compiled classes in memory and prevent GC. If you have expressions
|
||||
* because they anchor compiled classes in memory and prevent GC. If you have expressions
|
||||
* continually recompiling over time then by replacing the classloader periodically
|
||||
* at least some of the older variants can be garbage collected.
|
||||
* @param name the name of the class
|
||||
@@ -202,6 +204,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
return (Class<? extends CompiledExpression>) this.ccl.defineClass(name, bytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method for compiler instances. The returned SpelCompiler will
|
||||
* attach a class loader as the child of the given class loader and this
|
||||
@@ -222,10 +225,12 @@ public final class SpelCompiler implements Opcodes {
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that an attempt is made to compile the specified expression. It may fail if
|
||||
* components of the expression are not suitable for compilation or the data types
|
||||
* involved are not suitable for compilation. Used for testing.
|
||||
* @return true if the expression was successfully compiled
|
||||
* Request that an attempt is made to compile the specified expression.
|
||||
* It may fail if components of the expression are not suitable for compilation
|
||||
* or the data types involved are not suitable for compilation. Used for testing.
|
||||
* @param expression the expression to compile
|
||||
* @return {@code true} if the expression was successfully compiled,
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
public static boolean compile(Expression expression) {
|
||||
return (expression instanceof SpelExpression && ((SpelExpression) expression).compileExpression());
|
||||
@@ -256,18 +261,21 @@ public final class SpelCompiler implements Opcodes {
|
||||
super(NO_URLS, classLoader);
|
||||
}
|
||||
|
||||
int getClassesDefinedCount() {
|
||||
return this.classesDefinedCount;
|
||||
}
|
||||
|
||||
public Class<?> defineClass(String name, byte[] bytes) {
|
||||
Class<?> clazz = super.defineClass(name, bytes, 0, bytes.length);
|
||||
this.classesDefinedCount++;
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public int getClassesDefinedCount() {
|
||||
return this.classesDefinedCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An ASM ClassWriter extension bound to the SpelCompiler's ClassLoader.
|
||||
*/
|
||||
private class ExpressionClassWriter extends ClassWriter {
|
||||
|
||||
public ExpressionClassWriter() {
|
||||
|
||||
-1
@@ -76,7 +76,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
*/
|
||||
public class SpelReproTests extends AbstractExpressionTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void NPE_SPR5661() {
|
||||
evaluate("joinThreeStrings('a',null,'c')", "anullc", String.class);
|
||||
|
||||
+4
-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.
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package org.springframework.expression.spel.testresources;
|
||||
|
||||
///CLOVER:OFF
|
||||
public class Person {
|
||||
|
||||
private String privateName;
|
||||
|
||||
Company company;
|
||||
|
||||
public Person(String name) {
|
||||
@@ -41,4 +42,5 @@ public class Person {
|
||||
public Company getCompany() {
|
||||
return company;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-15
@@ -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,19 +19,25 @@ package org.springframework.expression.spel.testresources;
|
||||
import java.util.List;
|
||||
|
||||
public class TestAddress{
|
||||
private String street;
|
||||
private List<String> crossStreets;
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
public List<String> getCrossStreets() {
|
||||
return crossStreets;
|
||||
}
|
||||
public void setCrossStreets(List<String> crossStreets) {
|
||||
this.crossStreets = crossStreets;
|
||||
}
|
||||
private String street;
|
||||
|
||||
private List<String> crossStreets;
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
public List<String> getCrossStreets() {
|
||||
return crossStreets;
|
||||
}
|
||||
|
||||
public void setCrossStreets(List<String> crossStreets) {
|
||||
this.crossStreets = crossStreets;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-15
@@ -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,19 +17,25 @@
|
||||
package org.springframework.expression.spel.testresources;
|
||||
|
||||
public class TestPerson {
|
||||
private String name;
|
||||
private TestAddress address;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public TestAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
public void setAddress(TestAddress address) {
|
||||
this.address = address;
|
||||
}
|
||||
private String name;
|
||||
|
||||
private TestAddress address;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public TestAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(TestAddress address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-9
@@ -637,21 +637,23 @@ public class CallMetaDataContext {
|
||||
schemaNameToUse = this.metaDataProvider.schemaNameToUse(getSchemaName());
|
||||
}
|
||||
|
||||
String procedureNameToUse = this.metaDataProvider.procedureNameToUse(getProcedureName());
|
||||
if (isFunction() || isReturnValueRequired()) {
|
||||
callString = new StringBuilder().append("{? = call ").
|
||||
append(StringUtils.hasLength(catalogNameToUse) ? catalogNameToUse + "." : "").
|
||||
append(StringUtils.hasLength(schemaNameToUse) ? schemaNameToUse + "." : "").
|
||||
append(procedureNameToUse).append("(");
|
||||
callString = new StringBuilder("{? = call ");
|
||||
parameterCount = -1;
|
||||
}
|
||||
else {
|
||||
callString = new StringBuilder().append("{call ").
|
||||
append(StringUtils.hasLength(catalogNameToUse) ? catalogNameToUse + "." : "").
|
||||
append(StringUtils.hasLength(schemaNameToUse) ? schemaNameToUse + "." : "").
|
||||
append(procedureNameToUse).append("(");
|
||||
callString = new StringBuilder("{call ");
|
||||
}
|
||||
|
||||
if (StringUtils.hasLength(catalogNameToUse)) {
|
||||
callString.append(catalogNameToUse).append(".");
|
||||
}
|
||||
if (StringUtils.hasLength(schemaNameToUse)) {
|
||||
callString.append(schemaNameToUse).append(".");
|
||||
}
|
||||
callString.append(this.metaDataProvider.procedureNameToUse(getProcedureName()));
|
||||
callString.append("(");
|
||||
|
||||
for (SqlParameter parameter : this.callParameters) {
|
||||
if (!parameter.isResultsParameter()) {
|
||||
if (parameterCount > 0) {
|
||||
|
||||
+1
-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.
|
||||
@@ -233,7 +233,6 @@ public class StompBrokerRelayRegistration extends AbstractBrokerRegistration {
|
||||
|
||||
@Override
|
||||
protected StompBrokerRelayMessageHandler getMessageHandler(SubscribableChannel brokerChannel) {
|
||||
|
||||
StompBrokerRelayMessageHandler handler = new StompBrokerRelayMessageHandler(
|
||||
getClientInboundChannel(), getClientOutboundChannel(),
|
||||
brokerChannel, getDestinationPrefixes());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -79,7 +79,7 @@ public @interface ActiveProfiles {
|
||||
* <p>The default value is {@code true}, which means that a test
|
||||
* class will <em>inherit</em> bean definition profiles defined by a
|
||||
* test superclass. Specifically, the bean definition profiles for a test
|
||||
* class will be added to the list of bean definition profiles
|
||||
* class will be appended to the list of bean definition profiles
|
||||
* defined by a test superclass. Thus, subclasses have the option of
|
||||
* <em>extending</em> the list of bean definition profiles.
|
||||
* <p>If {@code inheritProfiles} is set to {@code false}, the bean
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2017 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,8 +19,8 @@ package org.springframework.test.context;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
@@ -533,8 +533,8 @@ public class MergedContextConfiguration implements Serializable {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
// Active profiles must be unique and sorted
|
||||
Set<String> profilesSet = new TreeSet<>(Arrays.asList(activeProfiles));
|
||||
// Active profiles must be unique
|
||||
Set<String> profilesSet = new LinkedHashSet<>(Arrays.asList(activeProfiles));
|
||||
return StringUtils.toStringArray(profilesSet);
|
||||
}
|
||||
|
||||
|
||||
+18
-7
@@ -16,8 +16,11 @@
|
||||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -67,7 +70,7 @@ abstract class ActiveProfilesUtils {
|
||||
static String[] resolveActiveProfiles(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
||||
Set<String> activeProfiles = new TreeSet<>();
|
||||
List<String[]> profileArrays = new ArrayList<>();
|
||||
|
||||
Class<ActiveProfiles> annotationType = ActiveProfiles.class;
|
||||
AnnotationDescriptor<ActiveProfiles> descriptor =
|
||||
@@ -106,17 +109,25 @@ abstract class ActiveProfilesUtils {
|
||||
|
||||
String[] profiles = resolver.resolve(rootDeclaringClass);
|
||||
if (!ObjectUtils.isEmpty(profiles)) {
|
||||
for (String profile : profiles) {
|
||||
if (StringUtils.hasText(profile)) {
|
||||
activeProfiles.add(profile.trim());
|
||||
}
|
||||
}
|
||||
profileArrays.add(profiles);
|
||||
}
|
||||
|
||||
descriptor = (annotation.inheritProfiles() ? MetaAnnotationUtils.findAnnotationDescriptor(
|
||||
rootDeclaringClass.getSuperclass(), annotationType) : null);
|
||||
}
|
||||
|
||||
// Reverse the list so that we can traverse "down" the hierarchy.
|
||||
Collections.reverse(profileArrays);
|
||||
|
||||
Set<String> activeProfiles = new LinkedHashSet<>();
|
||||
for (String[] profiles : profileArrays) {
|
||||
for (String profile : profiles) {
|
||||
if (StringUtils.hasText(profile)) {
|
||||
activeProfiles.add(profile.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StringUtils.toStringArray(activeProfiles);
|
||||
}
|
||||
|
||||
|
||||
+7
-21
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
@@ -26,7 +23,6 @@ import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.ActiveProfilesResolver;
|
||||
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.test.util.MetaAnnotationUtils.findAnnotationDescriptor;
|
||||
|
||||
@@ -43,6 +39,8 @@ import static org.springframework.test.util.MetaAnnotationUtils.findAnnotationDe
|
||||
*/
|
||||
public class DefaultActiveProfilesResolver implements ActiveProfilesResolver {
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultActiveProfilesResolver.class);
|
||||
|
||||
|
||||
@@ -58,36 +56,24 @@ public class DefaultActiveProfilesResolver implements ActiveProfilesResolver {
|
||||
@Override
|
||||
public String[] resolve(Class<?> testClass) {
|
||||
Assert.notNull(testClass, "Class must not be null");
|
||||
|
||||
Set<String> activeProfiles = new TreeSet<>();
|
||||
|
||||
Class<ActiveProfiles> annotationType = ActiveProfiles.class;
|
||||
AnnotationDescriptor<ActiveProfiles> descriptor = findAnnotationDescriptor(testClass, annotationType);
|
||||
AnnotationDescriptor<ActiveProfiles> descriptor = findAnnotationDescriptor(testClass, ActiveProfiles.class);
|
||||
|
||||
if (descriptor == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
|
||||
annotationType.getName(), testClass.getName()));
|
||||
ActiveProfiles.class.getName(), testClass.getName()));
|
||||
}
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
else {
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
ActiveProfiles annotation = descriptor.synthesizeAnnotation();
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Retrieved @ActiveProfiles [%s] for declaring class [%s].", annotation,
|
||||
declaringClass.getName()));
|
||||
}
|
||||
|
||||
for (String profile : annotation.profiles()) {
|
||||
if (StringUtils.hasText(profile)) {
|
||||
activeProfiles.add(profile.trim());
|
||||
}
|
||||
descriptor.getDeclaringClass().getName()));
|
||||
}
|
||||
return annotation.profiles();
|
||||
}
|
||||
|
||||
return StringUtils.toStringArray(activeProfiles);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -143,7 +143,7 @@ class MergedContextConfigurationTests {
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertThat(mergedConfig2).hasSameHashCodeAs(mergedConfig1);
|
||||
assertThat(mergedConfig2.hashCode()).isNotEqualTo(mergedConfig1.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -339,7 +339,7 @@ class MergedContextConfigurationTests {
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles1, loader);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(),
|
||||
EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, activeProfiles2, loader);
|
||||
assertThat(mergedConfig2).isEqualTo(mergedConfig1);
|
||||
assertThat(mergedConfig2).isNotEqualTo(mergedConfig1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -90,8 +90,8 @@ class ContextCacheTests {
|
||||
int size = 0, hit = 0, miss = 0;
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, ++size, hit, ++miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, ++hit, miss);
|
||||
// Profiles {foo, bar} MUST hash to the same as {bar, foo}
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, size, ++hit, miss);
|
||||
// Profiles {foo, bar} should not hash to the same as {bar,foo}
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, ++size, hit, ++miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, ++hit, miss);
|
||||
loadCtxAndAssertStats(FooBarProfilesTestCase.class, size, ++hit, miss);
|
||||
loadCtxAndAssertStats(BarFooProfilesTestCase.class, size, ++hit, miss);
|
||||
|
||||
+7
-7
@@ -67,12 +67,12 @@ class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsTests {
|
||||
|
||||
@Test
|
||||
void resolveActiveProfilesWithDuplicatedProfiles() {
|
||||
assertResolvedProfiles(DuplicatedProfiles.class, "bar", "baz", "foo");
|
||||
assertResolvedProfiles(DuplicatedProfiles.class, "foo", "bar", "baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveActiveProfilesWithLocalAndInheritedDuplicatedProfiles() {
|
||||
assertResolvedProfiles(ExtendedDuplicatedProfiles.class, "bar", "baz", "cat", "dog", "foo");
|
||||
assertResolvedProfiles(ExtendedDuplicatedProfiles.class, "foo", "bar", "baz", "cat", "dog");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -92,12 +92,12 @@ class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsTests {
|
||||
|
||||
@Test
|
||||
void resolveActiveProfilesWithLocalAndInheritedAnnotations() {
|
||||
assertResolvedProfiles(LocationsBar.class, "bar", "foo");
|
||||
assertResolvedProfiles(LocationsBar.class, "foo", "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveActiveProfilesWithOverriddenAnnotation() {
|
||||
assertResolvedProfiles(Animals.class, "cat", "dog");
|
||||
assertResolvedProfiles(Animals.class, "dog", "cat");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +129,7 @@ class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsTests {
|
||||
*/
|
||||
@Test
|
||||
void resolveActiveProfilesWithLocalAndInheritedMetaAnnotations() {
|
||||
assertResolvedProfiles(MetaLocationsBar.class, "bar", "foo");
|
||||
assertResolvedProfiles(MetaLocationsBar.class, "foo", "bar");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +137,7 @@ class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsTests {
|
||||
*/
|
||||
@Test
|
||||
void resolveActiveProfilesWithOverriddenMetaAnnotation() {
|
||||
assertResolvedProfiles(MetaAnimals.class, "cat", "dog");
|
||||
assertResolvedProfiles(MetaAnimals.class, "dog", "cat");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,7 +161,7 @@ class ActiveProfilesUtilsTests extends AbstractContextConfigurationUtilsTests {
|
||||
*/
|
||||
@Test
|
||||
void resolveActiveProfilesWithMergedInheritedResolver() {
|
||||
assertResolvedProfiles(MergedInheritedFooActiveProfilesResolverTestCase.class, "bar", "foo");
|
||||
assertResolvedProfiles(MergedInheritedFooActiveProfilesResolverTestCase.class, "foo", "bar");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-1
@@ -18,6 +18,7 @@ package org.springframework.http.converter.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
@@ -55,6 +56,7 @@ import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.TypeUtils;
|
||||
|
||||
/**
|
||||
@@ -308,7 +310,8 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
|
||||
MediaType contentType = outputMessage.getHeaders().getContentType();
|
||||
JsonEncoding encoding = getJsonEncoding(contentType);
|
||||
|
||||
try (JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding)) {
|
||||
OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());
|
||||
try (JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputStream, encoding)) {
|
||||
writePrefix(generator, object);
|
||||
|
||||
Object value = object;
|
||||
|
||||
+5
-5
@@ -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.
|
||||
@@ -42,8 +42,8 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return Map.class.isAssignableFrom(parameter.getParameterType()) &&
|
||||
parameter.getParameterAnnotations().length == 0;
|
||||
return (Map.class.isAssignableFrom(parameter.getParameterType()) &&
|
||||
parameter.getParameterAnnotations().length == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,8 +70,8 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle
|
||||
}
|
||||
else if (returnValue != null) {
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException("Unexpected return type: " +
|
||||
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||||
throw new UnsupportedOperationException("Unexpected return type [" +
|
||||
returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-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.
|
||||
@@ -70,8 +70,8 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
|
||||
}
|
||||
else {
|
||||
// should not happen
|
||||
throw new UnsupportedOperationException("Unexpected return type: " +
|
||||
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||||
throw new UnsupportedOperationException("Unexpected return type [" +
|
||||
returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-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.
|
||||
@@ -47,6 +47,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.assertj.core.api.Assertions.within;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Jackson 2.x converter tests.
|
||||
@@ -149,6 +151,7 @@ public class MappingJackson2HttpMessageConverterTests {
|
||||
assertThat(result.contains("\"bool\":true")).isTrue();
|
||||
assertThat(result.contains("\"bytes\":\"AQI=\"")).isTrue();
|
||||
assertThat(outputMessage.getHeaders().getContentType()).as("Invalid content-type").isEqualTo(MediaType.APPLICATION_JSON);
|
||||
verify(outputMessage.getBody(), never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+9
-9
@@ -53,8 +53,8 @@ class UriComponentsBuilderTests {
|
||||
void plain() throws URISyntaxException {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
|
||||
UriComponents result = builder.scheme("https").host("example.com")
|
||||
.path("foo").queryParam("bar").fragment("baz")
|
||||
.build();
|
||||
.path("foo").queryParam("bar").fragment("baz").build();
|
||||
|
||||
assertThat(result.getScheme()).isEqualTo("https");
|
||||
assertThat(result.getHost()).isEqualTo("example.com");
|
||||
assertThat(result.getPath()).isEqualTo("foo");
|
||||
@@ -91,10 +91,10 @@ class UriComponentsBuilderTests {
|
||||
@Test
|
||||
void fromPath() throws URISyntaxException {
|
||||
UriComponents result = UriComponentsBuilder.fromPath("foo").queryParam("bar").fragment("baz").build();
|
||||
|
||||
assertThat(result.getPath()).isEqualTo("foo");
|
||||
assertThat(result.getQuery()).isEqualTo("bar");
|
||||
assertThat(result.getFragment()).isEqualTo("baz");
|
||||
|
||||
assertThat(result.toUriString()).as("Invalid result URI String").isEqualTo("foo?bar#baz");
|
||||
|
||||
URI expected = new URI("foo?bar#baz");
|
||||
@@ -111,12 +111,12 @@ class UriComponentsBuilderTests {
|
||||
void fromHierarchicalUri() throws URISyntaxException {
|
||||
URI uri = new URI("https://example.com/foo?bar#baz");
|
||||
UriComponents result = UriComponentsBuilder.fromUri(uri).build();
|
||||
|
||||
assertThat(result.getScheme()).isEqualTo("https");
|
||||
assertThat(result.getHost()).isEqualTo("example.com");
|
||||
assertThat(result.getPath()).isEqualTo("/foo");
|
||||
assertThat(result.getQuery()).isEqualTo("bar");
|
||||
assertThat(result.getFragment()).isEqualTo("baz");
|
||||
|
||||
assertThat(result.toUri()).as("Invalid result URI").isEqualTo(uri);
|
||||
}
|
||||
|
||||
@@ -124,10 +124,10 @@ class UriComponentsBuilderTests {
|
||||
void fromOpaqueUri() throws URISyntaxException {
|
||||
URI uri = new URI("mailto:foo@bar.com#baz");
|
||||
UriComponents result = UriComponentsBuilder.fromUri(uri).build();
|
||||
|
||||
assertThat(result.getScheme()).isEqualTo("mailto");
|
||||
assertThat(result.getSchemeSpecificPart()).isEqualTo("foo@bar.com");
|
||||
assertThat(result.getFragment()).isEqualTo("baz");
|
||||
|
||||
assertThat(result.toUri()).as("Invalid result URI").isEqualTo(uri);
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ class UriComponentsBuilderTests {
|
||||
assertThat(after.getPath()).isEqualTo("/foo/");
|
||||
}
|
||||
|
||||
@Test // gh-19890
|
||||
@Test // gh-19890
|
||||
void fromHttpRequestWithEmptyScheme() {
|
||||
HttpRequest request = new HttpRequest() {
|
||||
@Override
|
||||
@@ -854,7 +854,7 @@ class UriComponentsBuilderTests {
|
||||
assertThat(uriComponents.getQueryParams().get("bar").get(0)).isNull();
|
||||
}
|
||||
|
||||
@Test // gh-24444
|
||||
@Test // gh-24444
|
||||
void opaqueUriDoesNotResetOnNullInput() throws URISyntaxException {
|
||||
URI uri = new URI("urn:ietf:wg:oauth:2.0:oob");
|
||||
UriComponents result = UriComponentsBuilder.fromUri(uri)
|
||||
@@ -1114,7 +1114,7 @@ class UriComponentsBuilderTests {
|
||||
assertThat(result.toUriString()).isEqualTo("https://example.com/rest/mobile/users/1");
|
||||
}
|
||||
|
||||
@Test // gh-25737
|
||||
@Test // gh-25737
|
||||
void fromHttpRequestForwardedHeaderComma() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader("Forwarded", "for=192.0.2.0,for=192.0.2.1;proto=https;host=192.0.2.3:9090");
|
||||
@@ -1153,7 +1153,7 @@ class UriComponentsBuilderTests {
|
||||
assertThat(uri).isEqualTo("http://localhost:8081/{path}?sort={sort}&sort=another_value");
|
||||
}
|
||||
|
||||
@Test // SPR-17630
|
||||
@Test // SPR-17630
|
||||
void toUriStringWithCurlyBraces() {
|
||||
assertThat(UriComponentsBuilder.fromUriString("/path?q={asa}asa").toUriString()).isEqualTo("/path?q=%7Basa%7Dasa");
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -43,7 +43,6 @@ public class UriComponentsTests {
|
||||
|
||||
@Test
|
||||
public void expandAndEncode() {
|
||||
|
||||
UriComponents uri = UriComponentsBuilder
|
||||
.fromPath("/hotel list/{city} specials").queryParam("q", "{value}").build()
|
||||
.expand("Z\u00fcrich", "a+b").encode();
|
||||
@@ -53,7 +52,6 @@ public class UriComponentsTests {
|
||||
|
||||
@Test
|
||||
public void encodeAndExpand() {
|
||||
|
||||
UriComponents uri = UriComponentsBuilder
|
||||
.fromPath("/hotel list/{city} specials").queryParam("q", "{value}").encode().build()
|
||||
.expand("Z\u00fcrich", "a+b");
|
||||
@@ -63,16 +61,14 @@ public class UriComponentsTests {
|
||||
|
||||
@Test
|
||||
public void encodeAndExpandPartially() {
|
||||
|
||||
UriComponents uri = UriComponentsBuilder
|
||||
.fromPath("/hotel list/{city} specials").queryParam("q", "{value}").encode()
|
||||
.uriVariables(Collections.singletonMap("city", "Z\u00fcrich"))
|
||||
.build();
|
||||
.uriVariables(Collections.singletonMap("city", "Z\u00fcrich")).build();
|
||||
|
||||
assertThat(uri.expand("a+b").toString()).isEqualTo("/hotel%20list/Z%C3%BCrich%20specials?q=a%2Bb");
|
||||
}
|
||||
|
||||
@Test // SPR-17168
|
||||
@Test // SPR-17168
|
||||
public void encodeAndExpandWithDollarSign() {
|
||||
UriComponents uri = UriComponentsBuilder.fromPath("/path").queryParam("q", "{value}").encode().build();
|
||||
assertThat(uri.expand("JavaClass$1.class").toString()).isEqualTo("/path?q=JavaClass%241.class");
|
||||
@@ -80,71 +76,71 @@ public class UriComponentsTests {
|
||||
|
||||
@Test
|
||||
public void toUriEncoded() throws URISyntaxException {
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
|
||||
"https://example.com/hotel list/Z\u00fcrich").build();
|
||||
assertThat(uriComponents.encode().toUri()).isEqualTo(new URI("https://example.com/hotel%20list/Z%C3%BCrich"));
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString("https://example.com/hotel list/Z\u00fcrich").build();
|
||||
assertThat(uri.encode().toUri()).isEqualTo(new URI("https://example.com/hotel%20list/Z%C3%BCrich"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toUriNotEncoded() throws URISyntaxException {
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
|
||||
"https://example.com/hotel list/Z\u00fcrich").build();
|
||||
assertThat(uriComponents.toUri()).isEqualTo(new URI("https://example.com/hotel%20list/Z\u00fcrich"));
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString("https://example.com/hotel list/Z\u00fcrich").build();
|
||||
assertThat(uri.toUri()).isEqualTo(new URI("https://example.com/hotel%20list/Z\u00fcrich"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toUriAlreadyEncoded() throws URISyntaxException {
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
|
||||
"https://example.com/hotel%20list/Z%C3%BCrich").build(true);
|
||||
UriComponents encoded = uriComponents.encode();
|
||||
assertThat(encoded.toUri()).isEqualTo(new URI("https://example.com/hotel%20list/Z%C3%BCrich"));
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString("https://example.com/hotel%20list/Z%C3%BCrich").build(true);
|
||||
assertThat(uri.encode().toUri()).isEqualTo(new URI("https://example.com/hotel%20list/Z%C3%BCrich"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toUriWithIpv6HostAlreadyEncoded() throws URISyntaxException {
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString(
|
||||
"http://[1abc:2abc:3abc::5ABC:6abc]:8080/hotel%20list/Z%C3%BCrich").build(true);
|
||||
UriComponents encoded = uriComponents.encode();
|
||||
assertThat(encoded.toUri()).isEqualTo(new URI("http://[1abc:2abc:3abc::5ABC:6abc]:8080/hotel%20list/Z%C3%BCrich"));
|
||||
|
||||
assertThat(uri.encode().toUri()).isEqualTo(
|
||||
new URI("http://[1abc:2abc:3abc::5ABC:6abc]:8080/hotel%20list/Z%C3%BCrich"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expand() {
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
|
||||
"https://example.com").path("/{foo} {bar}").build();
|
||||
uriComponents = uriComponents.expand("1 2", "3 4");
|
||||
assertThat(uriComponents.getPath()).isEqualTo("/1 2 3 4");
|
||||
assertThat(uriComponents.toUriString()).isEqualTo("https://example.com/1 2 3 4");
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString("https://example.com").path("/{foo} {bar}").build();
|
||||
uri = uri.expand("1 2", "3 4");
|
||||
|
||||
assertThat(uri.getPath()).isEqualTo("/1 2 3 4");
|
||||
assertThat(uri.toUriString()).isEqualTo("https://example.com/1 2 3 4");
|
||||
}
|
||||
|
||||
@Test // SPR-13311
|
||||
@Test // SPR-13311
|
||||
public void expandWithRegexVar() {
|
||||
String template = "/myurl/{name:[a-z]{1,5}}/show";
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(template).build();
|
||||
uriComponents = uriComponents.expand(Collections.singletonMap("name", "test"));
|
||||
assertThat(uriComponents.getPath()).isEqualTo("/myurl/test/show");
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString(template).build();
|
||||
uri = uri.expand(Collections.singletonMap("name", "test"));
|
||||
|
||||
assertThat(uri.getPath()).isEqualTo("/myurl/test/show");
|
||||
}
|
||||
|
||||
@Test // SPR-17630
|
||||
@Test // SPR-17630
|
||||
public void uirTemplateExpandWithMismatchedCurlyBraces() {
|
||||
assertThat(UriComponentsBuilder.fromUriString("/myurl/?q={{{{").encode().build().toUriString()).isEqualTo("/myurl/?q=%7B%7B%7B%7B");
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString("/myurl/?q={{{{").encode().build();
|
||||
assertThat(uri.toUriString()).isEqualTo("/myurl/?q=%7B%7B%7B%7B");
|
||||
}
|
||||
|
||||
@Test // gh-22447
|
||||
@Test // gh-22447
|
||||
public void expandWithFragmentOrder() {
|
||||
UriComponents uriComponents = UriComponentsBuilder
|
||||
UriComponents uri = UriComponentsBuilder
|
||||
.fromUriString("https://{host}/{path}#{fragment}").build()
|
||||
.expand("example.com", "foo", "bar");
|
||||
|
||||
assertThat(uriComponents.toUriString()).isEqualTo("https://example.com/foo#bar");
|
||||
assertThat(uri.toUriString()).isEqualTo("https://example.com/foo#bar");
|
||||
}
|
||||
|
||||
@Test // SPR-12123
|
||||
@Test // SPR-12123
|
||||
public void port() {
|
||||
UriComponents uri1 = fromUriString("https://example.com:8080/bar").build();
|
||||
UriComponents uri2 = fromUriString("https://example.com/bar").port(8080).build();
|
||||
UriComponents uri3 = fromUriString("https://example.com/bar").port("{port}").build().expand(8080);
|
||||
UriComponents uri4 = fromUriString("https://example.com/bar").port("808{digit}").build().expand(0);
|
||||
|
||||
assertThat(uri1.getPort()).isEqualTo(8080);
|
||||
assertThat(uri1.toUriString()).isEqualTo("https://example.com:8080/bar");
|
||||
assertThat(uri2.getPort()).isEqualTo(8080);
|
||||
@@ -175,20 +171,22 @@ public class UriComponentsTests {
|
||||
|
||||
@Test
|
||||
public void normalize() {
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString("https://example.com/foo/../bar").build();
|
||||
assertThat(uriComponents.normalize().toString()).isEqualTo("https://example.com/bar");
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString("https://example.com/foo/../bar").build();
|
||||
assertThat(uri.normalize().toString()).isEqualTo("https://example.com/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializable() throws Exception {
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
|
||||
UriComponents uri = UriComponentsBuilder.fromUriString(
|
||||
"https://example.com").path("/{foo}").query("bar={baz}").build();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(uriComponents);
|
||||
oos.writeObject(uri);
|
||||
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
|
||||
UriComponents readObject = (UriComponents) ois.readObject();
|
||||
assertThat(uriComponents.toString()).isEqualTo(readObject.toString());
|
||||
|
||||
assertThat(uri.toString()).isEqualTo(readObject.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -197,6 +195,7 @@ public class UriComponentsTests {
|
||||
UriComponentsBuilder targetBuilder = UriComponentsBuilder.newInstance();
|
||||
source.copyToUriComponentsBuilder(targetBuilder);
|
||||
UriComponents result = targetBuilder.build().encode();
|
||||
|
||||
assertThat(result.getPath()).isEqualTo("/foo/bar/ba%2Fz");
|
||||
assertThat(result.getPathSegments()).isEqualTo(Arrays.asList("foo", "bar", "ba%2Fz"));
|
||||
}
|
||||
@@ -207,6 +206,7 @@ public class UriComponentsTests {
|
||||
UriComponents uric1 = UriComponentsBuilder.fromUriString(url).path("/{foo}").query("bar={baz}").build();
|
||||
UriComponents uric2 = UriComponentsBuilder.fromUriString(url).path("/{foo}").query("bar={baz}").build();
|
||||
UriComponents uric3 = UriComponentsBuilder.fromUriString(url).path("/{foo}").query("bin={baz}").build();
|
||||
|
||||
assertThat(uric1).isInstanceOf(HierarchicalUriComponents.class);
|
||||
assertThat(uric1).isEqualTo(uric1);
|
||||
assertThat(uric1).isEqualTo(uric2);
|
||||
@@ -219,6 +219,7 @@ public class UriComponentsTests {
|
||||
UriComponents uric1 = UriComponentsBuilder.fromUriString(baseUrl + "/foo/bar").build();
|
||||
UriComponents uric2 = UriComponentsBuilder.fromUriString(baseUrl + "/foo/bar").build();
|
||||
UriComponents uric3 = UriComponentsBuilder.fromUriString(baseUrl + "/foo/bin").build();
|
||||
|
||||
assertThat(uric1).isInstanceOf(OpaqueUriComponents.class);
|
||||
assertThat(uric1).isEqualTo(uric1);
|
||||
assertThat(uric1).isEqualTo(uric2);
|
||||
|
||||
@@ -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.
|
||||
@@ -49,9 +49,7 @@ public class UriTemplateTests {
|
||||
assertThat(result).as("Invalid expanded template").isEqualTo(new URI("/hotels/1/bookings/42"));
|
||||
}
|
||||
|
||||
// SPR-9712
|
||||
|
||||
@Test
|
||||
@Test // SPR-9712
|
||||
public void expandVarArgsWithArrayValue() throws Exception {
|
||||
UriTemplate template = new UriTemplate("/sum?numbers={numbers}");
|
||||
URI result = template.expand(new int[] {1, 2, 3});
|
||||
@@ -61,8 +59,7 @@ public class UriTemplateTests {
|
||||
@Test
|
||||
public void expandVarArgsNotEnoughVariables() throws Exception {
|
||||
UriTemplate template = new UriTemplate("/hotels/{hotel}/bookings/{booking}");
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
template.expand("1"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> template.expand("1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,7 +153,7 @@ public class UriTemplateTests {
|
||||
assertThat(result).as("Invalid match").isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test // SPR-13627
|
||||
@Test // SPR-13627
|
||||
public void matchCustomRegexWithNestedCurlyBraces() throws Exception {
|
||||
UriTemplate template = new UriTemplate("/site.{domain:co.[a-z]{2}}");
|
||||
Map<String, String> result = template.match("/site.co.eu");
|
||||
@@ -181,8 +178,8 @@ public class UriTemplateTests {
|
||||
assertThat(result).as("Invalid match").isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test // SPR-16169
|
||||
public void matchWithMultipleSegmentsAtTheEnd() {
|
||||
@Test // SPR-16169
|
||||
public void matchWithMultipleSegmentsAtTheEnd() throws Exception {
|
||||
UriTemplate template = new UriTemplate("/account/{accountId}");
|
||||
assertThat(template.matches("/account/15/alias/5")).isFalse();
|
||||
}
|
||||
@@ -202,21 +199,20 @@ public class UriTemplateTests {
|
||||
assertThat(template.matches("/search?query=foo#bar")).isTrue();
|
||||
}
|
||||
|
||||
@Test // SPR-13705
|
||||
public void matchesWithSlashAtTheEnd() {
|
||||
UriTemplate uriTemplate = new UriTemplate("/test/");
|
||||
assertThat(uriTemplate.matches("/test/")).isTrue();
|
||||
@Test // SPR-13705
|
||||
public void matchesWithSlashAtTheEnd() throws Exception {
|
||||
assertThat(new UriTemplate("/test/").matches("/test/")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandWithDollar() {
|
||||
public void expandWithDollar() throws Exception {
|
||||
UriTemplate template = new UriTemplate("/{a}");
|
||||
URI uri = template.expand("$replacement");
|
||||
assertThat(uri.toString()).isEqualTo("/$replacement");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandWithAtSign() {
|
||||
public void expandWithAtSign() throws Exception {
|
||||
UriTemplate template = new UriTemplate("http://localhost/query={query}");
|
||||
URI uri = template.expand("foo@bar");
|
||||
assertThat(uri.toString()).isEqualTo("http://localhost/query=foo@bar");
|
||||
|
||||
+31
-2
@@ -263,9 +263,14 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
.reduce(ExchangeFilterFunction::andThen)
|
||||
.map(filter -> filter.apply(exchange))
|
||||
.orElse(exchange) : exchange);
|
||||
|
||||
HttpHeaders defaultHeaders = copyDefaultHeaders();
|
||||
|
||||
MultiValueMap<String, String> defaultCookies = copyDefaultCookies();
|
||||
|
||||
return new DefaultWebClient(filteredExchange, initUriBuilderFactory(),
|
||||
this.defaultHeaders != null ? HttpHeaders.readOnlyHttpHeaders(this.defaultHeaders) : null,
|
||||
this.defaultCookies != null ? CollectionUtils.unmodifiableMultiValueMap(this.defaultCookies) : null,
|
||||
defaultHeaders,
|
||||
defaultCookies,
|
||||
this.defaultRequest, new DefaultWebClientBuilder(this));
|
||||
}
|
||||
|
||||
@@ -302,4 +307,28 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private HttpHeaders copyDefaultHeaders() {
|
||||
if (this.defaultHeaders != null) {
|
||||
HttpHeaders copy = new HttpHeaders();
|
||||
this.defaultHeaders.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
|
||||
return HttpHeaders.readOnlyHttpHeaders(copy);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MultiValueMap<String, String> copyDefaultCookies() {
|
||||
if (this.defaultCookies != null) {
|
||||
MultiValueMap<String, String> copy = new LinkedMultiValueMap<>(this.defaultCookies.size());
|
||||
this.defaultCookies.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
|
||||
return CollectionUtils.unmodifiableMultiValueMap(copy);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-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.
|
||||
@@ -66,7 +66,6 @@ public interface WebSocketSession {
|
||||
* is closed. In a typical {@link WebSocketHandler} implementation this
|
||||
* stream is composed into the overall processing flow, so that when the
|
||||
* connection is closed, handling will end.
|
||||
*
|
||||
* <p>See the class-level doc of {@link WebSocketHandler} and the reference
|
||||
* for more details and examples of how to handle the session.
|
||||
*/
|
||||
@@ -76,7 +75,6 @@ public interface WebSocketSession {
|
||||
* Give a source of outgoing messages, write the messages and return a
|
||||
* {@code Mono<Void>} that completes when the source completes and writing
|
||||
* is done.
|
||||
*
|
||||
* <p>See the class-level doc of {@link WebSocketHandler} and the reference
|
||||
* for more details and examples of how to handle the session.
|
||||
*/
|
||||
|
||||
+31
-1
@@ -130,7 +130,8 @@ public class DefaultWebClientTests {
|
||||
@Test
|
||||
public void defaultHeaderAndCookie() {
|
||||
WebClient client = this.builder
|
||||
.defaultHeader("Accept", "application/json").defaultCookie("id", "123")
|
||||
.defaultHeader("Accept", "application/json")
|
||||
.defaultCookie("id", "123")
|
||||
.build();
|
||||
|
||||
client.get().uri("/path").exchange().block(Duration.ofSeconds(10));
|
||||
@@ -157,6 +158,35 @@ public class DefaultWebClientTests {
|
||||
assertThat(request.cookies().getFirst("id")).isEqualTo("456");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHeaderAndCookieCopies() {
|
||||
WebClient client1 = this.builder
|
||||
.defaultHeader("Accept", "application/json")
|
||||
.defaultCookie("id", "123")
|
||||
.build();
|
||||
WebClient client2 = this.builder
|
||||
.defaultHeader("Accept", "application/xml")
|
||||
.defaultCookies(cookies -> cookies.set("id", "456"))
|
||||
.build();
|
||||
|
||||
client1.get().uri("/path")
|
||||
.exchange().block(Duration.ofSeconds(10));
|
||||
|
||||
ClientRequest request = verifyAndGetRequest();
|
||||
assertThat(request.headers().getFirst("Accept")).isEqualTo("application/json");
|
||||
assertThat(request.cookies().getFirst("id")).isEqualTo("123");
|
||||
|
||||
|
||||
client2.get().uri("/path")
|
||||
.exchange().block(Duration.ofSeconds(10));
|
||||
|
||||
request = verifyAndGetRequest();
|
||||
assertThat(request.headers().getFirst("Accept")).isEqualTo("application/xml");
|
||||
assertThat(request.cookies().getFirst("id")).isEqualTo("456");
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultRequest() {
|
||||
ThreadLocal<String> context = new NamedThreadLocal<>("foo");
|
||||
|
||||
+2
-2
@@ -162,7 +162,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final boolean javaxValidationPresent;
|
||||
|
||||
private static boolean romePresent;
|
||||
private static final boolean romePresent;
|
||||
|
||||
private static final boolean jaxb2Present;
|
||||
|
||||
@@ -208,7 +208,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
|
||||
|
||||
if (element.hasAttribute("enable-matrix-variables")) {
|
||||
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
|
||||
boolean enableMatrixVariables = Boolean.parseBoolean(element.getAttribute("enable-matrix-variables"));
|
||||
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
|
||||
}
|
||||
|
||||
|
||||
+3
-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.
|
||||
@@ -81,7 +81,7 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String RESOURCE_URL_PROVIDER = "mvcResourceUrlProvider";
|
||||
|
||||
private static final boolean isWebJarsAssetLocatorPresent = ClassUtils.isPresent(
|
||||
private static final boolean webJarsPresent = ClassUtils.isPresent(
|
||||
"org.webjars.WebJarAssetLocator", ResourcesBeanDefinitionParser.class.getClassLoader());
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
if (isAutoRegistration) {
|
||||
if (isWebJarsAssetLocatorPresent) {
|
||||
if (webJarsPresent) {
|
||||
RootBeanDefinition webJarsResolverDef = new RootBeanDefinition(WebJarsResourceResolver.class);
|
||||
webJarsResolverDef.setSource(source);
|
||||
webJarsResolverDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
+3
-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.
|
||||
@@ -84,7 +84,7 @@ public class DispatcherServletTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws ServletException {
|
||||
public void setup() throws ServletException {
|
||||
MockServletConfig complexConfig = new MockServletConfig(getServletContext(), "complex");
|
||||
complexConfig.addInitParameter("publishContext", "false");
|
||||
complexConfig.addInitParameter("class", "notWritable");
|
||||
@@ -105,6 +105,7 @@ public class DispatcherServletTests {
|
||||
return servletConfig.getServletContext();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void configuredDispatcherServlets() {
|
||||
assertThat(("simple" + FrameworkServlet.DEFAULT_NAMESPACE_SUFFIX).equals(simpleDispatcherServlet.getNamespace())).as("Correct namespace").isTrue();
|
||||
|
||||
@@ -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.
|
||||
@@ -120,7 +120,6 @@ public interface WebSocketSession extends Closeable {
|
||||
|
||||
/**
|
||||
* Send a WebSocket message: either {@link TextMessage} or {@link BinaryMessage}.
|
||||
*
|
||||
* <p><strong>Note:</strong> The underlying standard WebSocket session (JSR-356) does
|
||||
* not allow concurrent sending. Therefore sending must be synchronized. To ensure
|
||||
* that, one option is to wrap the {@code WebSocketSession} with the
|
||||
|
||||
+6
-9
@@ -27,6 +27,7 @@ import org.springframework.context.event.SmartApplicationListener;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.user.DestinationUserNameProvider;
|
||||
import org.springframework.messaging.simp.user.SimpSession;
|
||||
@@ -34,7 +35,6 @@ import org.springframework.messaging.simp.user.SimpSubscription;
|
||||
import org.springframework.messaging.simp.user.SimpSubscriptionMatcher;
|
||||
import org.springframework.messaging.simp.user.SimpUser;
|
||||
import org.springframework.messaging.simp.user.SimpUserRegistry;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -84,19 +84,16 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
AbstractSubProtocolEvent subProtocolEvent = (AbstractSubProtocolEvent) event;
|
||||
Message<?> message = subProtocolEvent.getMessage();
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
|
||||
SimpMessageHeaderAccessor accessor =
|
||||
MessageHeaderAccessor.getAccessor(message, SimpMessageHeaderAccessor.class);
|
||||
Assert.state(accessor != null, "No SimpMessageHeaderAccessor");
|
||||
|
||||
String sessionId = accessor.getSessionId();
|
||||
String sessionId = SimpMessageHeaderAccessor.getSessionId(headers);
|
||||
Assert.state(sessionId != null, "No session id");
|
||||
|
||||
if (event instanceof SessionSubscribeEvent) {
|
||||
LocalSimpSession session = this.sessions.get(sessionId);
|
||||
if (session != null) {
|
||||
String id = accessor.getSubscriptionId();
|
||||
String destination = accessor.getDestination();
|
||||
String id = SimpMessageHeaderAccessor.getSubscriptionId(headers);
|
||||
String destination = SimpMessageHeaderAccessor.getDestination(headers);
|
||||
if (id != null && destination != null) {
|
||||
session.addSubscription(id, destination);
|
||||
}
|
||||
@@ -137,7 +134,7 @@ public class DefaultSimpUserRegistry implements SimpUserRegistry, SmartApplicati
|
||||
else if (event instanceof SessionUnsubscribeEvent) {
|
||||
LocalSimpSession session = this.sessions.get(sessionId);
|
||||
if (session != null) {
|
||||
String subscriptionId = accessor.getSubscriptionId();
|
||||
String subscriptionId = SimpMessageHeaderAccessor.getSubscriptionId(headers);
|
||||
if (subscriptionId != null) {
|
||||
session.removeSubscription(subscriptionId);
|
||||
}
|
||||
|
||||
+7
-2
@@ -326,8 +326,13 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Failed to send client message to application via MessageChannel" +
|
||||
" in session " + session.getId() + ". Sending STOMP ERROR to client.", ex);
|
||||
String errorText = "Failed to send message to MessageChannel in session " + session.getId();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(errorText, ex);
|
||||
}
|
||||
else {
|
||||
logger.error(errorText + ":" + ex.getMessage());
|
||||
}
|
||||
}
|
||||
handleError(session, ex, message);
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ files named `services.xml` and `daos.xml` (which are on the classpath) can be in
|
||||
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "daos.xml"), MessengerService::class.java)
|
||||
----
|
||||
|
||||
See the api-spring-framework}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`]
|
||||
See the {api-spring-framework}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`]
|
||||
javadoc for details on the various constructors.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user