From 9d429e30de7c118115fe898ac108e151fc54bd9b Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Mon, 11 May 2020 09:16:27 +0800 Subject: [PATCH 1/5] Fix SpelEvaluationException with example code org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null --- src/docs/asciidoc/core/core-expressions.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docs/asciidoc/core/core-expressions.adoc b/src/docs/asciidoc/core/core-expressions.adoc index b6d68cbf0b..ee7eae224d 100644 --- a/src/docs/asciidoc/core/core-expressions.adoc +++ b/src/docs/asciidoc/core/core-expressions.adoc @@ -1675,7 +1675,7 @@ The following example shows how to use the Elvis operator: ---- ExpressionParser parser = new SpelExpressionParser(); - String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); + String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class); System.out.println(name); // 'Unknown' ---- [source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] @@ -1683,7 +1683,7 @@ The following example shows how to use the Elvis operator: ---- val parser = SpelExpressionParser() - val name = parser.parseExpression("name?:'Unknown'").getValue(String::class.java) + val name = parser.parseExpression("name?:'Unknown'").getValue(Inventor(), String::class.java) println(name) // 'Unknown' ---- From 28177ad91b22e56de98d4e02be0b94e887734065 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 18 May 2020 14:19:05 +0200 Subject: [PATCH 2/5] Explicit notes on access to standard context beans in SpEL expressions Closes gh-25037 --- .../StandardBeanExpressionResolver.java | 7 ++++++- src/docs/asciidoc/core/core-expressions.adoc | 15 +++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java b/spring-context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java index dc058f1584..c2a0235d19 100644 --- a/spring-context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java +++ b/spring-context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java @@ -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. @@ -41,8 +41,13 @@ import org.springframework.util.StringUtils; * {@link org.springframework.beans.factory.config.BeanExpressionResolver} * interface, parsing and evaluating Spring EL using Spring's expression module. * + *

All beans in the containing {@code BeanFactory} are made available as + * predefined variables with their common bean name, including standard context + * beans such as "environment", "systemProperties" and "systemEnvironment". + * * @author Juergen Hoeller * @since 3.0 + * @see BeanExpressionContext#getBeanFactory() * @see org.springframework.expression.ExpressionParser * @see org.springframework.expression.spel.standard.SpelExpressionParser * @see org.springframework.expression.spel.support.StandardEvaluationContext diff --git a/src/docs/asciidoc/core/core-expressions.adoc b/src/docs/asciidoc/core/core-expressions.adoc index ee7eae224d..729ae85a91 100644 --- a/src/docs/asciidoc/core/core-expressions.adoc +++ b/src/docs/asciidoc/core/core-expressions.adoc @@ -539,8 +539,12 @@ example shows: ---- -The `systemProperties` variable is predefined, so you can use it in your expressions, as -the following example shows: +All beans in the application context are available as predefined variables with their +common bean name. This includes standard context beans such as `environment` (of type +`org.springframework.core.env.Environment`) as well as `systemProperties` and +`systemEnvironment` (of type `Map`) for access to the runtime environment. + +The following example shows access to the `systemProperties` bean as a SpEL variable: [source,xml,indent=0,subs="verbatim"] ---- @@ -551,8 +555,7 @@ the following example shows: ---- -Note that you do not have to prefix the predefined variable with the `#` -symbol in this context. +Note that you do not have to prefix the predefined variable with the `#` symbol here. You can also refer to other bean properties by name, as the following example shows: @@ -576,8 +579,8 @@ You can also refer to other bean properties by name, as the following example sh [[expressions-beandef-annotation-based]] === Annotation Configuration -To specify a default value, you can place the `@Value` annotation on fields, methods, and method or constructor -parameters. +To specify a default value, you can place the `@Value` annotation on fields, methods, +and method or constructor parameters. The following example sets the default value of a field variable: From 35f3277910b7fc8d0fda42ce2c7f74f05aaf5346 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 18 May 2020 14:21:14 +0200 Subject: [PATCH 3/5] Revised javadoc note on Lookup annotation limitations/recommendations Closes gh-25044 --- .../beans/factory/annotation/Lookup.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Lookup.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Lookup.java index 495354aa20..0fca4f3316 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Lookup.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Lookup.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,12 +41,11 @@ import java.lang.annotation.Target; * regular constructors: i.e. lookup methods cannot get replaced on beans returned * from factory methods where we cannot dynamically provide a subclass for them. * - *

Concrete limitations in typical Spring configuration scenarios: - * When used with component scanning or any other mechanism that filters out abstract - * beans, provide stub implementations of your lookup methods to be able to declare - * them as concrete classes. And please remember that lookup methods won't work on - * beans returned from {@code @Bean} methods in configuration classes; you'll have - * to resort to {@code @Inject Provider} or the like instead. + *

Recommendations for typical Spring configuration scenarios: + * When a concrete class may be needed in certain scenarios, consider providing stub + * implementations of your lookup methods. And please remember that lookup methods + * won't work on beans returned from {@code @Bean} methods in configuration classes; + * you'll have to resort to {@code @Inject Provider} or the like instead. * * @author Juergen Hoeller * @since 4.1 From 06cfd80c1cbb2fe0c906295cc336f492bf92b10b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 18 May 2020 14:22:24 +0200 Subject: [PATCH 4/5] Avoid full synchronization in refreshable getBeanFactory() implementation Closes gh-25081 --- ...AbstractRefreshableApplicationContext.java | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java index 173ef62ecb..9c87844e9d 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java @@ -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. @@ -72,10 +72,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl /** Bean factory for this context. */ @Nullable - private DefaultListableBeanFactory beanFactory; - - /** Synchronization monitor for the internal BeanFactory. */ - private final Object beanFactoryMonitor = new Object(); + private volatile DefaultListableBeanFactory beanFactory; /** @@ -131,9 +128,7 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); - synchronized (this.beanFactoryMonitor) { - this.beanFactory = beanFactory; - } + this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); @@ -142,21 +137,19 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl @Override protected void cancelRefresh(BeansException ex) { - synchronized (this.beanFactoryMonitor) { - if (this.beanFactory != null) { - this.beanFactory.setSerializationId(null); - } + DefaultListableBeanFactory beanFactory = this.beanFactory; + if (beanFactory != null) { + beanFactory.setSerializationId(null); } super.cancelRefresh(ex); } @Override protected final void closeBeanFactory() { - synchronized (this.beanFactoryMonitor) { - if (this.beanFactory != null) { - this.beanFactory.setSerializationId(null); - this.beanFactory = null; - } + DefaultListableBeanFactory beanFactory = this.beanFactory; + if (beanFactory != null) { + beanFactory.setSerializationId(null); + this.beanFactory = null; } } @@ -165,20 +158,17 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl * i.e. has been refreshed at least once and not been closed yet. */ protected final boolean hasBeanFactory() { - synchronized (this.beanFactoryMonitor) { - return (this.beanFactory != null); - } + return (this.beanFactory != null); } @Override public final ConfigurableListableBeanFactory getBeanFactory() { - synchronized (this.beanFactoryMonitor) { - if (this.beanFactory == null) { - throw new IllegalStateException("BeanFactory not initialized or already closed - " + - "call 'refresh' before accessing beans via the ApplicationContext"); - } - return this.beanFactory; + DefaultListableBeanFactory beanFactory = this.beanFactory; + if (beanFactory == null) { + throw new IllegalStateException("BeanFactory not initialized or already closed - " + + "call 'refresh' before accessing beans via the ApplicationContext"); } + return beanFactory; } /** From c35b21b49f8541cf182f6ec62c11fdb15f010712 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 18 May 2020 14:23:05 +0200 Subject: [PATCH 5/5] Consistent not-null assertions for configured interceptors Closes gh-25088 --- .../messaging/support/AbstractMessageChannel.java | 5 ++++- .../http/client/support/InterceptingHttpAccessor.java | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/AbstractMessageChannel.java b/spring-messaging/src/main/java/org/springframework/messaging/support/AbstractMessageChannel.java index c933757d33..40d6a09899 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/AbstractMessageChannel.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/AbstractMessageChannel.java @@ -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. @@ -87,17 +87,20 @@ public abstract class AbstractMessageChannel implements MessageChannel, Intercep @Override public void setInterceptors(List interceptors) { + Assert.noNullElements(interceptors, "'interceptors' must not contain null elements"); this.interceptors.clear(); this.interceptors.addAll(interceptors); } @Override public void addInterceptor(ChannelInterceptor interceptor) { + Assert.notNull(interceptor, "'interceptor' must not be null"); this.interceptors.add(interceptor); } @Override public void addInterceptor(int index, ChannelInterceptor interceptor) { + Assert.notNull(interceptor, "'interceptor' must not be null"); this.interceptors.add(index, interceptor); } diff --git a/spring-web/src/main/java/org/springframework/http/client/support/InterceptingHttpAccessor.java b/spring-web/src/main/java/org/springframework/http/client/support/InterceptingHttpAccessor.java index 40b839a486..a8977cce81 100644 --- a/spring-web/src/main/java/org/springframework/http/client/support/InterceptingHttpAccessor.java +++ b/spring-web/src/main/java/org/springframework/http/client/support/InterceptingHttpAccessor.java @@ -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. @@ -24,6 +24,7 @@ import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.InterceptingClientHttpRequestFactory; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; /** @@ -57,6 +58,7 @@ public abstract class InterceptingHttpAccessor extends HttpAccessor { * @see AnnotationAwareOrderComparator */ public void setInterceptors(List interceptors) { + Assert.noNullElements(interceptors, "'interceptors' must not contain null elements"); // Take getInterceptors() List as-is when passed in here if (this.interceptors != interceptors) { this.interceptors.clear();