Merge branch '5.2.x'
# Conflicts: # spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java
This commit is contained in:
commit
6c631e3d5c
|
@ -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.
|
||||
|
@ -238,8 +238,10 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
|||
Assert.state(lo != null, "LookupOverride not found");
|
||||
Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
|
||||
if (StringUtils.hasText(lo.getBeanName())) {
|
||||
return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
|
||||
Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
|
||||
this.owner.getBean(lo.getBeanName()));
|
||||
// Detect package-protected NullBean instance through equals(null) check
|
||||
return (bean.equals(null) ? null : bean);
|
||||
}
|
||||
else {
|
||||
return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
|
||||
|
|
|
@ -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.
|
||||
|
@ -108,10 +108,23 @@ public class LookupAnnotationTests {
|
|||
assertThat(beanFactory.getBean(BeanConsumer.class).abstractBean).isSameAs(bean);
|
||||
}
|
||||
|
||||
@Test // gh-25806
|
||||
public void testWithNullBean() {
|
||||
RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class, () -> null);
|
||||
tbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
beanFactory.registerBeanDefinition("testBean", tbd);
|
||||
|
||||
AbstractBean bean = beanFactory.getBean("beanConsumer", BeanConsumer.class).abstractBean;
|
||||
assertThat(bean).isNotNull();
|
||||
Object expected = bean.get();
|
||||
assertThat(expected).isNull();
|
||||
assertThat(beanFactory.getBean(BeanConsumer.class).abstractBean).isSameAs(bean);
|
||||
}
|
||||
|
||||
|
||||
public static abstract class AbstractBean {
|
||||
|
||||
@Lookup
|
||||
@Lookup("testBean")
|
||||
public abstract TestBean get();
|
||||
|
||||
@Lookup
|
||||
|
|
|
@ -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.
|
||||
|
@ -63,9 +63,9 @@ import org.springframework.util.ObjectUtils;
|
|||
public abstract class AbstractApplicationEventMulticaster
|
||||
implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
|
||||
|
||||
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
|
||||
private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
|
||||
|
||||
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
|
||||
final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
|
||||
|
||||
@Nullable
|
||||
private ClassLoader beanClassLoader;
|
||||
|
@ -73,8 +73,6 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
@Nullable
|
||||
private ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private Object retrievalMutex = this.defaultRetriever;
|
||||
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
|
@ -90,7 +88,6 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
if (this.beanClassLoader == null) {
|
||||
this.beanClassLoader = this.beanFactory.getBeanClassLoader();
|
||||
}
|
||||
this.retrievalMutex = this.beanFactory.getSingletonMutex();
|
||||
}
|
||||
|
||||
private ConfigurableBeanFactory getBeanFactory() {
|
||||
|
@ -104,7 +101,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
|
||||
@Override
|
||||
public void addApplicationListener(ApplicationListener<?> listener) {
|
||||
synchronized (this.retrievalMutex) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
// Explicitly remove target for a proxy, if registered already,
|
||||
// in order to avoid double invocations of the same listener.
|
||||
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
|
||||
|
@ -118,7 +115,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
|
||||
@Override
|
||||
public void addApplicationListenerBean(String listenerBeanName) {
|
||||
synchronized (this.retrievalMutex) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
|
||||
this.retrieverCache.clear();
|
||||
}
|
||||
|
@ -126,7 +123,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
|
||||
@Override
|
||||
public void removeApplicationListener(ApplicationListener<?> listener) {
|
||||
synchronized (this.retrievalMutex) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
this.defaultRetriever.applicationListeners.remove(listener);
|
||||
this.retrieverCache.clear();
|
||||
}
|
||||
|
@ -134,7 +131,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
|
||||
@Override
|
||||
public void removeApplicationListenerBean(String listenerBeanName) {
|
||||
synchronized (this.retrievalMutex) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
this.defaultRetriever.applicationListenerBeans.remove(listenerBeanName);
|
||||
this.retrieverCache.clear();
|
||||
}
|
||||
|
@ -142,7 +139,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
|
||||
@Override
|
||||
public void removeAllListeners() {
|
||||
synchronized (this.retrievalMutex) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
this.defaultRetriever.applicationListeners.clear();
|
||||
this.defaultRetriever.applicationListenerBeans.clear();
|
||||
this.retrieverCache.clear();
|
||||
|
@ -156,7 +153,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
* @see org.springframework.context.ApplicationListener
|
||||
*/
|
||||
protected Collection<ApplicationListener<?>> getApplicationListeners() {
|
||||
synchronized (this.retrievalMutex) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
return this.defaultRetriever.getApplicationListeners();
|
||||
}
|
||||
}
|
||||
|
@ -177,32 +174,34 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
Class<?> sourceType = (source != null ? source.getClass() : null);
|
||||
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
|
||||
|
||||
// Quick check for existing entry on ConcurrentHashMap...
|
||||
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
|
||||
if (retriever != null) {
|
||||
return retriever.getApplicationListeners();
|
||||
}
|
||||
// Potential new retriever to populate
|
||||
CachedListenerRetriever newRetriever = null;
|
||||
|
||||
if (this.beanClassLoader == null ||
|
||||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
|
||||
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
|
||||
// Fully synchronized building and caching of a ListenerRetriever
|
||||
synchronized (this.retrievalMutex) {
|
||||
retriever = this.retrieverCache.get(cacheKey);
|
||||
if (retriever != null) {
|
||||
return retriever.getApplicationListeners();
|
||||
// Quick check for existing entry on ConcurrentHashMap
|
||||
CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
|
||||
if (existingRetriever == null) {
|
||||
// Caching a new ListenerRetriever if possible
|
||||
if (this.beanClassLoader == null ||
|
||||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
|
||||
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
|
||||
newRetriever = new CachedListenerRetriever();
|
||||
existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
|
||||
if (existingRetriever != null) {
|
||||
newRetriever = null; // no need to populate it in retrieveApplicationListeners
|
||||
}
|
||||
retriever = new ListenerRetriever(true);
|
||||
Collection<ApplicationListener<?>> listeners =
|
||||
retrieveApplicationListeners(eventType, sourceType, retriever);
|
||||
this.retrieverCache.put(cacheKey, retriever);
|
||||
return listeners;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No ListenerRetriever caching -> no synchronization necessary
|
||||
return retrieveApplicationListeners(eventType, sourceType, null);
|
||||
|
||||
if (existingRetriever != null) {
|
||||
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
// If result is null, the existing retriever is not fully populated yet by another thread.
|
||||
// Proceed like caching wasn't possible for this current local attempt.
|
||||
}
|
||||
|
||||
return retrieveApplicationListeners(eventType, sourceType, newRetriever);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,12 +212,15 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
* @return the pre-filtered list of application listeners for the given event and source type
|
||||
*/
|
||||
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
|
||||
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
|
||||
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
|
||||
|
||||
List<ApplicationListener<?>> allListeners = new ArrayList<>();
|
||||
Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
|
||||
Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
|
||||
|
||||
Set<ApplicationListener<?>> listeners;
|
||||
Set<String> listenerBeans;
|
||||
synchronized (this.retrievalMutex) {
|
||||
synchronized (this.defaultRetriever) {
|
||||
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
|
||||
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
|
||||
}
|
||||
|
@ -228,7 +230,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
for (ApplicationListener<?> listener : listeners) {
|
||||
if (supportsEvent(listener, eventType, sourceType)) {
|
||||
if (retriever != null) {
|
||||
retriever.applicationListeners.add(listener);
|
||||
filteredListeners.add(listener);
|
||||
}
|
||||
allListeners.add(listener);
|
||||
}
|
||||
|
@ -246,10 +248,10 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
|
||||
if (retriever != null) {
|
||||
if (beanFactory.isSingleton(listenerBeanName)) {
|
||||
retriever.applicationListeners.add(listener);
|
||||
filteredListeners.add(listener);
|
||||
}
|
||||
else {
|
||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||
filteredListenerBeans.add(listenerBeanName);
|
||||
}
|
||||
}
|
||||
allListeners.add(listener);
|
||||
|
@ -261,7 +263,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
// BeanDefinition metadata (e.g. factory method generics) above.
|
||||
Object listener = beanFactory.getSingleton(listenerBeanName);
|
||||
if (retriever != null) {
|
||||
retriever.applicationListeners.remove(listener);
|
||||
filteredListeners.remove(listener);
|
||||
}
|
||||
allListeners.remove(listener);
|
||||
}
|
||||
|
@ -274,9 +276,15 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
}
|
||||
|
||||
AnnotationAwareOrderComparator.sort(allListeners);
|
||||
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
|
||||
retriever.applicationListeners.clear();
|
||||
retriever.applicationListeners.addAll(allListeners);
|
||||
if (retriever != null) {
|
||||
if (filteredListenerBeans.isEmpty()) {
|
||||
retriever.applicationListeners = new LinkedHashSet<>(allListeners);
|
||||
retriever.applicationListenerBeans = filteredListenerBeans;
|
||||
}
|
||||
else {
|
||||
retriever.applicationListeners = filteredListeners;
|
||||
retriever.applicationListenerBeans = filteredListenerBeans;
|
||||
}
|
||||
}
|
||||
return allListeners;
|
||||
}
|
||||
|
@ -415,18 +423,55 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
* allowing for efficient retrieval of pre-filtered listeners.
|
||||
* <p>An instance of this helper gets cached per event type and source type.
|
||||
*/
|
||||
private class ListenerRetriever {
|
||||
private class CachedListenerRetriever {
|
||||
|
||||
@Nullable
|
||||
public volatile Set<ApplicationListener<?>> applicationListeners;
|
||||
|
||||
@Nullable
|
||||
public volatile Set<String> applicationListenerBeans;
|
||||
|
||||
@Nullable
|
||||
public Collection<ApplicationListener<?>> getApplicationListeners() {
|
||||
Set<ApplicationListener<?>> applicationListeners = this.applicationListeners;
|
||||
Set<String> applicationListenerBeans = this.applicationListenerBeans;
|
||||
if (applicationListeners == null || applicationListenerBeans == null) {
|
||||
// Not fully populated yet
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ApplicationListener<?>> allListeners = new ArrayList<>(
|
||||
applicationListeners.size() + applicationListenerBeans.size());
|
||||
allListeners.addAll(applicationListeners);
|
||||
if (!applicationListenerBeans.isEmpty()) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
for (String listenerBeanName : applicationListenerBeans) {
|
||||
try {
|
||||
allListeners.add(beanFactory.getBean(listenerBeanName, ApplicationListener.class));
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Singleton listener instance (without backing bean definition) disappeared -
|
||||
// probably in the middle of the destruction phase
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!applicationListenerBeans.isEmpty()) {
|
||||
AnnotationAwareOrderComparator.sort(allListeners);
|
||||
}
|
||||
return allListeners;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper class that encapsulates a general set of target listeners.
|
||||
*/
|
||||
private class DefaultListenerRetriever {
|
||||
|
||||
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
|
||||
|
||||
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
|
||||
|
||||
private final boolean preFiltered;
|
||||
|
||||
public ListenerRetriever(boolean preFiltered) {
|
||||
this.preFiltered = preFiltered;
|
||||
}
|
||||
|
||||
public Collection<ApplicationListener<?>> getApplicationListeners() {
|
||||
List<ApplicationListener<?>> allListeners = new ArrayList<>(
|
||||
this.applicationListeners.size() + this.applicationListenerBeans.size());
|
||||
|
@ -435,8 +480,9 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
BeanFactory beanFactory = getBeanFactory();
|
||||
for (String listenerBeanName : this.applicationListenerBeans) {
|
||||
try {
|
||||
ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||
if (this.preFiltered || !allListeners.contains(listener)) {
|
||||
ApplicationListener<?> listener =
|
||||
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||
if (!allListeners.contains(listener)) {
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
@ -446,9 +492,7 @@ public abstract class AbstractApplicationEventMulticaster
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
|
||||
AnnotationAwareOrderComparator.sort(allListeners);
|
||||
}
|
||||
AnnotationAwareOrderComparator.sort(allListeners);
|
||||
return allListeners;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
|
@ -35,6 +36,8 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
|
@ -374,6 +377,19 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
|
|||
assertThat(MyNonSingletonListener.seenEvents.contains(event4)).isTrue();
|
||||
MyNonSingletonListener.seenEvents.clear();
|
||||
|
||||
context.publishEvent(event1);
|
||||
context.publishEvent(event2);
|
||||
context.publishEvent(event3);
|
||||
context.publishEvent(event4);
|
||||
assertThat(MyNonSingletonListener.seenEvents.contains(event1)).isTrue();
|
||||
assertThat(MyNonSingletonListener.seenEvents.contains(event2)).isTrue();
|
||||
assertThat(MyNonSingletonListener.seenEvents.contains(event3)).isTrue();
|
||||
assertThat(MyNonSingletonListener.seenEvents.contains(event4)).isTrue();
|
||||
MyNonSingletonListener.seenEvents.clear();
|
||||
|
||||
AbstractApplicationEventMulticaster multicaster = context.getBean(AbstractApplicationEventMulticaster.class);
|
||||
assertThat(multicaster.retrieverCache.size()).isEqualTo(3);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
@ -516,6 +532,36 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
|
|||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initMethodPublishesEvent() {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.registerBeanDefinition("listener", new RootBeanDefinition(BeanThatListens.class));
|
||||
context.registerBeanDefinition("messageSource", new RootBeanDefinition(StaticMessageSource.class));
|
||||
context.registerBeanDefinition("initMethod", new RootBeanDefinition(EventPublishingInitMethod.class));
|
||||
context.refresh();
|
||||
|
||||
context.publishEvent(new MyEvent(this));
|
||||
BeanThatListens listener = context.getBean(BeanThatListens.class);
|
||||
assertThat(listener.getEventCount()).isEqualTo(3);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initMethodPublishesAsyncEvent() {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.registerBeanDefinition("listener", new RootBeanDefinition(BeanThatListens.class));
|
||||
context.registerBeanDefinition("messageSource", new RootBeanDefinition(StaticMessageSource.class));
|
||||
context.registerBeanDefinition("initMethod", new RootBeanDefinition(AsyncEventPublishingInitMethod.class));
|
||||
context.refresh();
|
||||
|
||||
context.publishEvent(new MyEvent(this));
|
||||
BeanThatListens listener = context.getBean(BeanThatListens.class);
|
||||
assertThat(listener.getEventCount()).isEqualTo(3);
|
||||
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class MyEvent extends ApplicationEvent {
|
||||
|
@ -652,4 +698,38 @@ public class ApplicationContextEventTests extends AbstractApplicationEventListen
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static class EventPublishingInitMethod implements ApplicationEventPublisherAware, InitializingBean {
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.publisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.publisher.publishEvent(new MyEvent(this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class AsyncEventPublishingInitMethod implements ApplicationEventPublisherAware, InitializingBean {
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.publisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Thread thread = new Thread(() -> this.publisher.publishEvent(new MyEvent(this)));
|
||||
thread.start();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -132,14 +132,13 @@ public abstract class ObjectUtils {
|
|||
* @see CollectionUtils#isEmpty(java.util.Collection)
|
||||
* @see CollectionUtils#isEmpty(java.util.Map)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static boolean isEmpty(@Nullable Object obj) {
|
||||
if (obj == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj instanceof Optional) {
|
||||
return !((Optional) obj).isPresent();
|
||||
return !((Optional<?>) obj).isPresent();
|
||||
}
|
||||
if (obj instanceof CharSequence) {
|
||||
return ((CharSequence) obj).length() == 0;
|
||||
|
@ -148,10 +147,10 @@ public abstract class ObjectUtils {
|
|||
return Array.getLength(obj) == 0;
|
||||
}
|
||||
if (obj instanceof Collection) {
|
||||
return ((Collection) obj).isEmpty();
|
||||
return ((Collection<?>) obj).isEmpty();
|
||||
}
|
||||
if (obj instanceof Map) {
|
||||
return ((Map) obj).isEmpty();
|
||||
return ((Map<?, ?>) obj).isEmpty();
|
||||
}
|
||||
|
||||
// else
|
||||
|
@ -611,9 +610,7 @@ public abstract class ObjectUtils {
|
|||
if (obj == null) {
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
String className = obj.getClass().getName();
|
||||
String identityHexString = getIdentityHexString(obj);
|
||||
return className + '@' + identityHexString;
|
||||
return obj.getClass().getName() + "@" + getIdentityHexString(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -43,6 +43,7 @@ public final class CallMetaDataProviderFactory {
|
|||
"Apache Derby",
|
||||
"DB2",
|
||||
"Informix Dynamic Server",
|
||||
"MariaDB",
|
||||
"Microsoft SQL Server",
|
||||
"MySQL",
|
||||
"Oracle",
|
||||
|
|
|
@ -457,11 +457,11 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B
|
|||
}
|
||||
Class<?> mappedClass = this.idClassMappings.get(typeId);
|
||||
if (mappedClass != null) {
|
||||
return this.objectMapper.getTypeFactory().constructType(mappedClass);
|
||||
return this.objectMapper.constructType(mappedClass);
|
||||
}
|
||||
try {
|
||||
Class<?> typeClass = ClassUtils.forName(typeId, this.beanClassLoader);
|
||||
return this.objectMapper.getTypeFactory().constructType(typeClass);
|
||||
return this.objectMapper.constructType(typeClass);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new MessageConversionException("Failed to resolve type id [" + typeId + "]", ex);
|
||||
|
|
|
@ -46,6 +46,8 @@ import org.springframework.util.MimeType;
|
|||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 4.2
|
||||
* @see Marshaller
|
||||
* @see Unmarshaller
|
||||
*/
|
||||
public class MarshallingMessageConverter extends AbstractMessageConverter {
|
||||
|
||||
|
@ -61,7 +63,8 @@ public class MarshallingMessageConverter extends AbstractMessageConverter {
|
|||
* {@link #setUnmarshaller(Unmarshaller)} to be invoked separately.
|
||||
*/
|
||||
public MarshallingMessageConverter() {
|
||||
this(new MimeType("application", "xml"), new MimeType("text", "xml"), new MimeType("application", "*+xml"));
|
||||
this(new MimeType("application", "xml"), new MimeType("text", "xml"),
|
||||
new MimeType("application", "*+xml"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +163,7 @@ public class MarshallingMessageConverter extends AbstractMessageConverter {
|
|||
return new StreamSource(new ByteArrayInputStream((byte[]) payload));
|
||||
}
|
||||
else {
|
||||
return new StreamSource(new StringReader((String) payload));
|
||||
return new StreamSource(new StringReader(payload.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,9 +120,9 @@ public class ProtobufMessageConverter extends AbstractMessageConverter {
|
|||
|
||||
@Override
|
||||
protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) {
|
||||
MimeType mimeType = getMimeType(headers);
|
||||
MimeType contentType = getMimeType(headers);
|
||||
return (super.canConvertTo(payload, headers) ||
|
||||
this.protobufFormatSupport != null && this.protobufFormatSupport.supportsWriteOnly(mimeType));
|
||||
this.protobufFormatSupport != null && this.protobufFormatSupport.supportsWriteOnly(contentType));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -97,7 +97,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
|
|||
|
||||
@Override
|
||||
public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) {
|
||||
JavaType javaType = getObjectMapper().getTypeFactory().constructType(elementType.getType());
|
||||
JavaType javaType = getObjectMapper().constructType(elementType.getType());
|
||||
// Skip String: CharSequenceDecoder + "*/*" comes after
|
||||
return (!CharSequence.class.isAssignableFrom(elementType.toClass()) &&
|
||||
getObjectMapper().canDeserialize(javaType) && supportsMimeType(mimeType));
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.Map;
|
|||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
|
@ -113,8 +112,7 @@ public abstract class Jackson2CodecSupport {
|
|||
}
|
||||
|
||||
protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
|
||||
TypeFactory typeFactory = this.objectMapper.getTypeFactory();
|
||||
return typeFactory.constructType(GenericTypeResolver.resolveType(type, contextClass));
|
||||
return this.objectMapper.constructType(GenericTypeResolver.resolveType(type, contextClass));
|
||||
}
|
||||
|
||||
protected Map<String, Object> getHints(ResolvableType resolvableType) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -25,7 +25,7 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
|
||||
* Strategy interface for converting from and to HTTP requests and responses.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
|
|
|
@ -42,7 +42,6 @@ import com.fasterxml.jackson.databind.SerializationConfig;
|
|||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
|
||||
import com.fasterxml.jackson.databind.ser.FilterProvider;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
|
@ -377,8 +376,7 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
|
|||
* @return the Jackson JavaType
|
||||
*/
|
||||
protected JavaType getJavaType(Type type, @Nullable Class<?> contextClass) {
|
||||
TypeFactory typeFactory = this.objectMapper.getTypeFactory();
|
||||
return typeFactory.constructType(GenericTypeResolver.resolveType(type, contextClass));
|
||||
return this.objectMapper.constructType(GenericTypeResolver.resolveType(type, contextClass));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -121,9 +121,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
|
|||
ClassLoader classLoader = RestTemplate.class.getClassLoader();
|
||||
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
|
||||
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
|
||||
jackson2Present =
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
|
||||
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
|
||||
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
|
||||
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
|
||||
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
|
||||
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
@ -454,9 +455,8 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
|
|||
* characters that should have been encoded.
|
||||
*/
|
||||
public UriComponents build(boolean encoded) {
|
||||
return buildInternal(encoded ?
|
||||
EncodingHint.FULLY_ENCODED :
|
||||
this.encodeTemplate ? EncodingHint.ENCODE_TEMPLATE : EncodingHint.NONE);
|
||||
return buildInternal(encoded ? EncodingHint.FULLY_ENCODED :
|
||||
(this.encodeTemplate ? EncodingHint.ENCODE_TEMPLATE : EncodingHint.NONE));
|
||||
}
|
||||
|
||||
private UriComponents buildInternal(EncodingHint hint) {
|
||||
|
@ -468,8 +468,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
|
|||
HierarchicalUriComponents uric = new HierarchicalUriComponents(this.scheme, this.fragment,
|
||||
this.userInfo, this.host, this.port, this.pathBuilder.build(), this.queryParams,
|
||||
hint == EncodingHint.FULLY_ENCODED);
|
||||
|
||||
result = hint == EncodingHint.ENCODE_TEMPLATE ? uric.encodeTemplate(this.charset) : uric;
|
||||
result = (hint == EncodingHint.ENCODE_TEMPLATE ? uric.encodeTemplate(this.charset) : uric);
|
||||
}
|
||||
if (!this.uriVariables.isEmpty()) {
|
||||
result = result.expand(name -> this.uriVariables.getOrDefault(name, UriTemplateVariables.SKIP_VALUE));
|
||||
|
@ -526,9 +525,8 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
|
|||
* @see UriComponents#toUriString()
|
||||
*/
|
||||
public String toUriString() {
|
||||
return this.uriVariables.isEmpty() ?
|
||||
build().encode().toUriString() :
|
||||
buildInternal(EncodingHint.ENCODE_TEMPLATE).toUriString();
|
||||
return (this.uriVariables.isEmpty() ? build().encode().toUriString() :
|
||||
buildInternal(EncodingHint.ENCODE_TEMPLATE).toUriString());
|
||||
}
|
||||
|
||||
|
||||
|
@ -712,7 +710,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
|
|||
|
||||
@Override
|
||||
public UriComponentsBuilder queryParam(String name, @Nullable Collection<?> values) {
|
||||
return queryParam(name, values != null ? values.toArray() : EMPTY_VALUES);
|
||||
return queryParam(name, (CollectionUtils.isEmpty(values) ? EMPTY_VALUES : values.toArray()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -741,7 +739,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
|
|||
|
||||
@Override
|
||||
public UriComponentsBuilder replaceQueryParam(String name, @Nullable Collection<?> values) {
|
||||
return replaceQueryParam(name, values != null ? values.toArray() : EMPTY_VALUES);
|
||||
return replaceQueryParam(name, (CollectionUtils.isEmpty(values) ? EMPTY_VALUES : values.toArray()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -830,12 +828,10 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
|
|||
scheme("https");
|
||||
port(null);
|
||||
}
|
||||
|
||||
String hostHeader = headers.getFirst("X-Forwarded-Host");
|
||||
if (StringUtils.hasText(hostHeader)) {
|
||||
adaptForwardedHost(StringUtils.tokenizeToStringArray(hostHeader, ",")[0]);
|
||||
}
|
||||
|
||||
String portHeader = headers.getFirst("X-Forwarded-Port");
|
||||
if (StringUtils.hasText(portHeader)) {
|
||||
port(Integer.parseInt(StringUtils.tokenizeToStringArray(portHeader, ",")[0]));
|
||||
|
|
|
@ -358,20 +358,7 @@ public class MessageTag extends HtmlEscapingAwareTag implements ArgumentAware {
|
|||
@Nullable
|
||||
protected Object[] resolveArguments(@Nullable Object arguments) throws JspException {
|
||||
if (arguments instanceof String) {
|
||||
String[] stringArray =
|
||||
StringUtils.delimitedListToStringArray((String) arguments, this.argumentSeparator);
|
||||
if (stringArray.length == 1) {
|
||||
Object argument = stringArray[0];
|
||||
if (argument != null && argument.getClass().isArray()) {
|
||||
return ObjectUtils.toObjectArray(argument);
|
||||
}
|
||||
else {
|
||||
return new Object[] {argument};
|
||||
}
|
||||
}
|
||||
else {
|
||||
return stringArray;
|
||||
}
|
||||
return StringUtils.delimitedListToStringArray((String) arguments, this.argumentSeparator);
|
||||
}
|
||||
else if (arguments instanceof Object[]) {
|
||||
return (Object[]) arguments;
|
||||
|
@ -395,7 +382,7 @@ public class MessageTag extends HtmlEscapingAwareTag implements ArgumentAware {
|
|||
* @throws IOException if writing failed
|
||||
*/
|
||||
protected void writeMessage(String msg) throws IOException {
|
||||
this.pageContext.getOut().write(String.valueOf(msg));
|
||||
this.pageContext.getOut().write(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.io.IOException;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -35,6 +34,7 @@ import org.springframework.beans.BeanUtils;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
@ -456,12 +456,12 @@ public class RedirectView extends AbstractUrlBasedView implements SmartView {
|
|||
boolean first = (targetUrl.toString().indexOf('?') < 0);
|
||||
for (Map.Entry<String, Object> entry : queryProperties(model).entrySet()) {
|
||||
Object rawValue = entry.getValue();
|
||||
Collection<Object> values;
|
||||
Collection<?> values;
|
||||
if (rawValue != null && rawValue.getClass().isArray()) {
|
||||
values = Arrays.asList(ObjectUtils.toObjectArray(rawValue));
|
||||
values = CollectionUtils.arrayToList(rawValue);
|
||||
}
|
||||
else if (rawValue instanceof Collection) {
|
||||
values = ((Collection<Object>) rawValue);
|
||||
values = ((Collection<?>) rawValue);
|
||||
}
|
||||
else {
|
||||
values = Collections.singleton(rawValue);
|
||||
|
|
Loading…
Reference in New Issue