Merge branch '5.1.x'

This commit is contained in:
Juergen Hoeller 2019-02-25 17:57:49 +01:00
commit bc0317af3b
14 changed files with 158 additions and 72 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,27 +27,31 @@ import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
* *
* @author Rod Johnson * @author Rod Johnson
* @author Ramnivas Laddad * @author Ramnivas Laddad
* @author Juergen Hoeller
* @since 2.0 * @since 2.0
*/ */
public abstract class AspectJProxyUtils { public abstract class AspectJProxyUtils {
/** /**
* Add special advisors if necessary to work with a proxy chain that contains AspectJ advisors. * Add special advisors if necessary to work with a proxy chain that contains AspectJ advisors:
* This will expose the current Spring AOP invocation (necessary for some AspectJ pointcut matching) * concretely, {@link ExposeInvocationInterceptor} at the beginning of the list.
* and make available the current AspectJ JoinPoint. The call will have no effect if there are no * <p>This will expose the current Spring AOP invocation (necessary for some AspectJ pointcut
* AspectJ advisors in the advisor chain. * matching) and make available the current AspectJ JoinPoint. The call will have no effect
* if there are no AspectJ advisors in the advisor chain.
* @param advisors the advisors available * @param advisors the advisors available
* @return {@code true} if any special {@link Advisor Advisors} were added, otherwise {@code false} * @return {@code true} if an {@link ExposeInvocationInterceptor} was added to the list,
* otherwise {@code false}
*/ */
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) { public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
// Don't add advisors to an empty list; may indicate that proxying is just not required // Don't add advisors to an empty list; may indicate that proxying is just not required
if (!advisors.isEmpty()) { if (!advisors.isEmpty()) {
boolean foundAspectJAdvice = false; boolean foundAspectJAdvice = false;
for (Advisor advisor : advisors) { for (Advisor advisor : advisors) {
// Be careful not to get the Advice without a guard, as // Be careful not to get the Advice without a guard, as this might eagerly
// this might eagerly instantiate a non-singleton AspectJ aspect // instantiate a non-singleton AspectJ aspect...
if (isAspectJAdvice(advisor)) { if (isAspectJAdvice(advisor)) {
foundAspectJAdvice = true; foundAspectJAdvice = true;
break;
} }
} }
if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -188,9 +188,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
/** /**
* Resolve the method arguments to use for the specified {@link ApplicationEvent}. * Resolve the method arguments to use for the specified {@link ApplicationEvent}.
* <p>These arguments will be used to invoke the method handled by this instance. Can * <p>These arguments will be used to invoke the method handled by this instance.
* return {@code null} to indicate that no suitable arguments could be resolved and * Can return {@code null} to indicate that no suitable arguments could be resolved
* therefore the method should not be invoked at all for the specified event. * and therefore the method should not be invoked at all for the specified event.
*/ */
@Nullable @Nullable
protected Object[] resolveArguments(ApplicationEvent event) { protected Object[] resolveArguments(ApplicationEvent event) {
@ -201,14 +201,16 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
if (this.method.getParameterCount() == 0) { if (this.method.getParameterCount() == 0) {
return new Object[0]; return new Object[0];
} }
if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.toClass()) && Class<?> declaredEventClass = declaredEventType.toClass();
if (!ApplicationEvent.class.isAssignableFrom(declaredEventClass) &&
event instanceof PayloadApplicationEvent) { event instanceof PayloadApplicationEvent) {
return new Object[] {((PayloadApplicationEvent) event).getPayload()}; Object payload = ((PayloadApplicationEvent) event).getPayload();
if (declaredEventClass.isInstance(payload)) {
return new Object[] {payload};
}
} }
else {
return new Object[] {event}; return new Object[] {event};
} }
}
protected void handleResult(Object result) { protected void handleResult(Object result) {
if (result.getClass().isArray()) { if (result.getClass().isArray()) {

View File

@ -36,14 +36,13 @@ import org.springframework.lang.Nullable;
* or through a {@link org.springframework.beans.factory.config.CustomScopeConfigurer} bean. * or through a {@link org.springframework.beans.factory.config.CustomScopeConfigurer} bean.
* *
* <p>{@code SimpleThreadScope} <em>does not clean up any objects</em> associated with it. * <p>{@code SimpleThreadScope} <em>does not clean up any objects</em> associated with it.
* As such, it is typically preferable to use * It is therefore typically preferable to use a request-bound scope implementation such
* {@link org.springframework.web.context.request.RequestScope RequestScope} * as {@code org.springframework.web.context.request.RequestScope} in web environments,
* in web environments. * implementing the full lifecycle for scoped attributes (including reliable destruction).
* *
* <p>For an implementation of a thread-based {@code Scope} with support for * <p>For an implementation of a thread-based {@code Scope} with support for destruction
* destruction callbacks, refer to the * callbacks, refer to
* <a href="http://www.springbyexample.org/examples/custom-thread-scope-module.html"> * <a href="http://www.springbyexample.org/examples/custom-thread-scope-module.html">Spring by Example</a>.
* Spring by Example Custom Thread Scope Module</a>.
* *
* <p>Thanks to Eugene Kuleshov for submitting the original prototype for a thread scope! * <p>Thanks to Eugene Kuleshov for submitting the original prototype for a thread scope!
* *

View File

@ -0,0 +1,72 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.event;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import static org.junit.Assert.*;
/**
* @author Juergen Hoeller
*/
public class PayloadApplicationEventTests {
@Test
public void testEventClassWithInterface() {
ApplicationContext ac = new AnnotationConfigApplicationContext(Listener.class);
MyEventClass event = new MyEventClass<>(this, "xyz");
ac.publishEvent(event);
assertTrue(ac.getBean(Listener.class).events.contains(event));
}
public interface Auditable {
}
public static class MyEventClass<GT> extends PayloadApplicationEvent<GT> implements Auditable {
public MyEventClass(Object source, GT payload) {
super(source, payload);
}
public String toString() {
return "Payload: " + getPayload();
}
}
@Component
public static class Listener {
public final List<Auditable> events = new ArrayList<>();
@EventListener
public void onEvent(Auditable event) {
events.add(event);
}
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -43,7 +43,7 @@ import java.util.StringJoiner;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
* Miscellaneous class utility methods. * Miscellaneous {@code java.lang.Class} utility methods.
* Mainly for internal use within the framework. * Mainly for internal use within the framework.
* *
* @author Juergen Hoeller * @author Juergen Hoeller

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -74,7 +74,8 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B
private MessageType targetType = MessageType.BYTES; private MessageType targetType = MessageType.BYTES;
private String encoding = DEFAULT_ENCODING; @Nullable
private String encoding;
@Nullable @Nullable
private String encodingPropertyName; private String encodingPropertyName;
@ -293,13 +294,21 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B
throws JMSException, IOException { throws JMSException, IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
if (this.encoding != null) {
OutputStreamWriter writer = new OutputStreamWriter(bos, this.encoding); OutputStreamWriter writer = new OutputStreamWriter(bos, this.encoding);
objectWriter.writeValue(writer, object); objectWriter.writeValue(writer, object);
}
else {
// Jackson usually defaults to UTF-8 but can also go straight to bytes, e.g. for Smile.
// We use a direct byte array argument for the latter case to work as well.
objectWriter.writeValue(bos, object);
}
BytesMessage message = session.createBytesMessage(); BytesMessage message = session.createBytesMessage();
message.writeBytes(bos.toByteArray()); message.writeBytes(bos.toByteArray());
if (this.encodingPropertyName != null) { if (this.encodingPropertyName != null) {
message.setStringProperty(this.encodingPropertyName, this.encoding); message.setStringProperty(this.encodingPropertyName,
(this.encoding != null ? this.encoding : DEFAULT_ENCODING));
} }
return message; return message;
} }
@ -393,6 +402,7 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B
} }
byte[] bytes = new byte[(int) message.getBodyLength()]; byte[] bytes = new byte[(int) message.getBodyLength()];
message.readBytes(bytes); message.readBytes(bytes);
if (encoding != null) {
try { try {
String body = new String(bytes, encoding); String body = new String(bytes, encoding);
return this.objectMapper.readValue(body, targetJavaType); return this.objectMapper.readValue(body, targetJavaType);
@ -401,6 +411,11 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B
throw new MessageConversionException("Cannot convert bytes to String", ex); throw new MessageConversionException("Cannot convert bytes to String", ex);
} }
} }
else {
// Jackson internally performs encoding detection, falling back to UTF-8.
return this.objectMapper.readValue(bytes, targetJavaType);
}
}
/** /**
* Template method that allows for custom message mapping. * Template method that allows for custom message mapping.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -56,7 +56,7 @@ public class MappingJackson2MessageConverterTests {
@Before @Before
public void setUp() throws Exception { public void setup() {
sessionMock = mock(Session.class); sessionMock = mock(Session.class);
converter = new MappingJackson2MessageConverter(); converter = new MappingJackson2MessageConverter();
converter.setEncodingPropertyName("__encoding__"); converter.setEncodingPropertyName("__encoding__");
@ -263,8 +263,11 @@ public class MappingJackson2MessageConverterTests {
return new MyAnotherBean(); return new MyAnotherBean();
} }
public static class MyBean { public static class MyBean {
private String foo;
public MyBean() { public MyBean() {
} }
@ -272,8 +275,6 @@ public class MappingJackson2MessageConverterTests {
this.foo = foo; this.foo = foo;
} }
private String foo;
public String getFoo() { public String getFoo() {
return foo; return foo;
} }
@ -290,13 +291,10 @@ public class MappingJackson2MessageConverterTests {
if (o == null || getClass() != o.getClass()) { if (o == null || getClass() != o.getClass()) {
return false; return false;
} }
MyBean bean = (MyBean) o; MyBean bean = (MyBean) o;
if (foo != null ? !foo.equals(bean.foo) : bean.foo != null) { if (foo != null ? !foo.equals(bean.foo) : bean.foo != null) {
return false; return false;
} }
return true; return true;
} }
@ -306,9 +304,12 @@ public class MappingJackson2MessageConverterTests {
} }
} }
private interface Summary {}; private interface Summary {};
private interface Full extends Summary {}; private interface Full extends Summary {};
private static class MyAnotherBean { private static class MyAnotherBean {
@JsonView(Summary.class) @JsonView(Summary.class)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,8 +19,8 @@ package org.springframework.messaging.simp.stomp;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
* Represents a STOMP session with operations to send messages, create * Represents a STOMP session with operations to send messages,
* subscriptions and receive messages on those subscriptions. * create subscriptions and receive messages on those subscriptions.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 4.2 * @since 4.2

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -197,8 +197,7 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
/** /**
* Configure the {@code ApplicationContext} associated with the web application, * Configure the {@code ApplicationContext} associated with the web application,
* if it was initialized with one via * if it was initialized with one via
* {@link org.springframework.web.server.adapter.WebHttpHandlerBuilder#applicationContext * {@link org.springframework.web.server.adapter.WebHttpHandlerBuilder#applicationContext(ApplicationContext)}.
* WebHttpHandlerBuilder#applicationContext}.
* @param applicationContext the context * @param applicationContext the context
* @since 5.0.3 * @since 5.0.3
*/ */
@ -232,11 +231,9 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
@Override @Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
if (this.forwardedHeaderTransformer != null) { if (this.forwardedHeaderTransformer != null) {
request = this.forwardedHeaderTransformer.apply(request); request = this.forwardedHeaderTransformer.apply(request);
} }
ServerWebExchange exchange = createExchange(request, response); ServerWebExchange exchange = createExchange(request, response);
LogFormatUtils.traceDebug(logger, traceOn -> LogFormatUtils.traceDebug(logger, traceOn ->
@ -274,7 +271,6 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
} }
private Mono<Void> handleUnresolvedError(ServerWebExchange exchange, Throwable ex) { private Mono<Void> handleUnresolvedError(ServerWebExchange exchange, Throwable ex) {
ServerHttpRequest request = exchange.getRequest(); ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse(); ServerHttpResponse response = exchange.getResponse();
String logPrefix = exchange.getLogPrefix(); String logPrefix = exchange.getLogPrefix();
@ -294,7 +290,7 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
return Mono.empty(); return Mono.empty();
} }
else { else {
// After the response is committed, propagate errors to the server.. // After the response is committed, propagate errors to the server...
logger.error(logPrefix + "Error [" + ex + "] for " + formatRequest(request) + logger.error(logPrefix + "Error [" + ex + "] for " + formatRequest(request) +
", but ServerHttpResponse already committed (" + response.getStatusCode() + ")"); ", but ServerHttpResponse already committed (" + response.getStatusCode() + ")");
return Mono.error(ex); return Mono.error(ex);

View File

@ -82,8 +82,8 @@ public abstract class MvcNamespaceUtils {
} }
parserContext.getRegistry().registerAlias(urlPathHelperRef.getBeanName(), URL_PATH_HELPER_BEAN_NAME); parserContext.getRegistry().registerAlias(urlPathHelperRef.getBeanName(), URL_PATH_HELPER_BEAN_NAME);
} }
else if (!parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME) else if (!parserContext.getRegistry().isAlias(URL_PATH_HELPER_BEAN_NAME) &&
&& !parserContext.getRegistry().containsBeanDefinition(URL_PATH_HELPER_BEAN_NAME)) { !parserContext.getRegistry().containsBeanDefinition(URL_PATH_HELPER_BEAN_NAME)) {
RootBeanDefinition urlPathHelperDef = new RootBeanDefinition(UrlPathHelper.class); RootBeanDefinition urlPathHelperDef = new RootBeanDefinition(UrlPathHelper.class);
urlPathHelperDef.setSource(source); urlPathHelperDef.setSource(source);
urlPathHelperDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); urlPathHelperDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
@ -107,8 +107,8 @@ public abstract class MvcNamespaceUtils {
} }
parserContext.getRegistry().registerAlias(pathMatcherRef.getBeanName(), PATH_MATCHER_BEAN_NAME); parserContext.getRegistry().registerAlias(pathMatcherRef.getBeanName(), PATH_MATCHER_BEAN_NAME);
} }
else if (!parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME) else if (!parserContext.getRegistry().isAlias(PATH_MATCHER_BEAN_NAME) &&
&& !parserContext.getRegistry().containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) { !parserContext.getRegistry().containsBeanDefinition(PATH_MATCHER_BEAN_NAME)) {
RootBeanDefinition pathMatcherDef = new RootBeanDefinition(AntPathMatcher.class); RootBeanDefinition pathMatcherDef = new RootBeanDefinition(AntPathMatcher.class);
pathMatcherDef.setSource(source); pathMatcherDef.setSource(source);
pathMatcherDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); pathMatcherDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

View File

@ -67,13 +67,12 @@ public class HandlerExceptionResolverComposite implements HandlerExceptionResolv
/** /**
* Resolve the exception by iterating over the list of configured exception resolvers. * Resolve the exception by iterating over the list of configured exception resolvers.
* <p>The first one to return a {@link ModelAndView} wins. Otherwise {@code null} * <p>The first one to return a {@link ModelAndView} wins. Otherwise {@code null} is returned.
* is returned.
*/ */
@Override @Override
@Nullable @Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, public ModelAndView resolveException(
@Nullable Object handler, Exception ex) { HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (this.resolvers != null) { if (this.resolvers != null) {
for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) { for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {

View File

@ -33,10 +33,10 @@ import org.springframework.web.servlet.RequestToViewNameTranslator;
* as the actual return value is left as-is allowing the configured * as the actual return value is left as-is allowing the configured
* {@link RequestToViewNameTranslator} to select a view name by convention. * {@link RequestToViewNameTranslator} to select a view name by convention.
* *
* <p>A String return value can be interpreted in more than one ways depending * <p>A String return value can be interpreted in more than one ways depending on
* on the presence of annotations like {@code @ModelAttribute} or * the presence of annotations like {@code @ModelAttribute} or {@code @ResponseBody}.
* {@code @ResponseBody}. Therefore this handler should be configured after * Therefore this handler should be configured after the handlers that support these
* the handlers that support these annotations. * annotations.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller * @author Juergen Hoeller
@ -49,12 +49,10 @@ public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValu
/** /**
* Configure one more simple patterns (as described in * Configure one more simple patterns (as described in {@link PatternMatchUtils#simpleMatch})
* {@link PatternMatchUtils#simpleMatch}) to use in order to recognize * to use in order to recognize custom redirect prefixes in addition to "redirect:".
* custom redirect prefixes in addition to "redirect:". * <p>Note that simply configuring this property will not make a custom redirect prefix work.
* <p>Note that simply configuring this property will not make a custom * There must be a custom View that recognizes the prefix as well.
* redirect prefix work. There must be a custom View that recognizes the
* prefix as well.
* @since 4.1 * @since 4.1
*/ */
public void setRedirectPatterns(@Nullable String... redirectPatterns) { public void setRedirectPatterns(@Nullable String... redirectPatterns) {