mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 925b078260 | |||
| 1a03ffef62 | |||
| e954d7fc20 | |||
| ec13a3f1ba | |||
| 842f5b354d | |||
| 67a4b63d42 | |||
| 354dad615b | |||
| 0668323176 | |||
| e03e62c76a | |||
| 96a7fc693b | |||
| 50f6db2704 | |||
| 24ebb5ecd5 | |||
| a439a83f34 | |||
| f0de3a9924 | |||
| 17c3976bca | |||
| 9c4a33e64c | |||
| dd3ae4e63d | |||
| dfc435d045 | |||
| 78a24c0dd1 | |||
| 2bec54e7b3 | |||
| 52e2953de9 | |||
| 7b149a68c6 | |||
| d1b3b8eaf6 | |||
| f94794e5c0 | |||
| 26af506441 | |||
| b14a940716 | |||
| 4ecbf6511d | |||
| 5b6a32d1b9 | |||
| 263244f637 | |||
| 3a166ea742 | |||
| 6d4dfed772 | |||
| 09ef597836 | |||
| 50920e1a96 | |||
| 8c4d1d4d29 | |||
| 82f8d0dfd7 | |||
| 38f616a8e8 | |||
| 441c4d5d5c | |||
| 66f4d20fd4 | |||
| 2fa44d62b9 | |||
| c86ac97267 | |||
| fd916baf79 | |||
| 790d56dd18 | |||
| 5a5f118efc | |||
| edf664c1cf | |||
| 137bca129f | |||
| b01a3aeb6b | |||
| 195d375964 | |||
| ebd351753f | |||
| 9524ecd5b0 | |||
| 9053613a65 |
+1
-1
@@ -8,4 +8,4 @@ wiki page.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please see https://pivotal.io/security.
|
||||
Please see https://spring.io/security-policy.
|
||||
|
||||
+9
-9
@@ -2,14 +2,14 @@ plugins {
|
||||
id 'io.spring.dependency-management' version '1.0.9.RELEASE' apply false
|
||||
id 'io.spring.ge.conventions' version '0.0.7'
|
||||
id 'io.spring.nohttp' version '0.0.5.RELEASE'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.72' apply false
|
||||
id "io.freefair.aspectj" version '4.1.6' apply false
|
||||
id 'org.jetbrains.dokka' version '0.10.1' apply false
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.72' apply false
|
||||
id 'org.asciidoctor.jvm.convert' version '2.4.0'
|
||||
id 'org.asciidoctor.jvm.pdf' version '2.4.0'
|
||||
id 'de.undercouch.download' version '4.1.1'
|
||||
id "io.freefair.aspectj" version '4.1.6' apply false
|
||||
id "com.github.ben-manes.versions" version '0.28.0'
|
||||
id 'com.gradle.build-scan' version '3.2'
|
||||
id 'de.undercouch.download' version '4.1.1'
|
||||
}
|
||||
|
||||
apply from: "$rootDir/gradle/build-scan-user-data.gradle"
|
||||
@@ -28,7 +28,7 @@ configure(allprojects) { project ->
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.10.5"
|
||||
mavenBom "io.netty:netty-bom:4.1.51.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:Dysprosium-SR23"
|
||||
mavenBom "io.projectreactor:reactor-bom:Dysprosium-SR25"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.0.4"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.31.v20200723"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.3.72"
|
||||
@@ -36,21 +36,21 @@ configure(allprojects) { project ->
|
||||
mavenBom "org.junit:junit-bom:5.6.3"
|
||||
}
|
||||
dependencies {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.13.3') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.16.0') {
|
||||
entry 'log4j-api'
|
||||
entry 'log4j-core'
|
||||
entry 'log4j-jul'
|
||||
entry 'log4j-slf4j-impl'
|
||||
}
|
||||
dependency "org.slf4j:slf4j-api:1.7.30"
|
||||
dependency "org.slf4j:slf4j-api:1.7.32"
|
||||
dependency "com.google.code.findbugs:jsr305:3.0.2"
|
||||
|
||||
dependencySet(group: 'org.aspectj', version: '1.9.6') {
|
||||
dependencySet(group: 'org.aspectj', version: '1.9.7') {
|
||||
entry 'aspectjrt'
|
||||
entry 'aspectjtools'
|
||||
entry 'aspectjweaver'
|
||||
}
|
||||
dependencySet(group: 'org.codehaus.groovy', version: '2.5.13') {
|
||||
dependencySet(group: 'org.codehaus.groovy', version: '2.5.15') {
|
||||
entry 'groovy'
|
||||
entry 'groovy-jsr223'
|
||||
entry 'groovy-templates'
|
||||
@@ -61,7 +61,7 @@ configure(allprojects) { project ->
|
||||
dependency "io.reactivex:rxjava:1.3.8"
|
||||
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
|
||||
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.4.RELEASE"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
dependency "com.fasterxml:aalto-xml:1.2.2"
|
||||
|
||||
@@ -49,6 +49,11 @@ resource_types:
|
||||
source:
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.13
|
||||
- name: github-release
|
||||
type: registry-image
|
||||
source:
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.5.5
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
|
||||
@@ -4,7 +4,7 @@ image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.6'
|
||||
tag: '0.0.7'
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.2.17.BUILD-SNAPSHOT
|
||||
version=5.2.19.RELEASE
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -237,7 +237,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
* validates it if not.
|
||||
*/
|
||||
private void validateClassIfNecessary(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
synchronized (validatedClasses) {
|
||||
if (!validatedClasses.containsKey(proxySuperClass)) {
|
||||
doValidateClass(proxySuperClass, proxyClassLoader,
|
||||
@@ -679,13 +679,19 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Object retVal;
|
||||
// Check whether we only have one InvokerInterceptor: that is,
|
||||
// no real advice, but just reflective invocation of the target.
|
||||
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
|
||||
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
|
||||
// We can skip creating a MethodInvocation: just invoke the target directly.
|
||||
// Note that the final invoker must be an InvokerInterceptor, so we know
|
||||
// it does nothing but a reflective operation on the target, and no hot
|
||||
// swapping or fancy proxying.
|
||||
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
|
||||
retVal = methodProxy.invoke(target, argsToUse);
|
||||
try {
|
||||
retVal = methodProxy.invoke(target, argsToUse);
|
||||
}
|
||||
catch (CodeGenerationException ex) {
|
||||
CglibMethodInvocation.logFastClassGenerationFailure(method);
|
||||
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We need to create a method invocation...
|
||||
@@ -737,10 +743,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
|
||||
|
||||
// Only use method proxy for public methods not derived from java.lang.Object
|
||||
this.methodProxy = (Modifier.isPublic(method.getModifiers()) &&
|
||||
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
|
||||
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ?
|
||||
methodProxy : null);
|
||||
this.methodProxy = (isMethodProxyCompatible(method) ? methodProxy : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -776,10 +779,25 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
@Override
|
||||
protected Object invokeJoinpoint() throws Throwable {
|
||||
if (this.methodProxy != null) {
|
||||
return this.methodProxy.invoke(this.target, this.arguments);
|
||||
try {
|
||||
return this.methodProxy.invoke(this.target, this.arguments);
|
||||
}
|
||||
catch (CodeGenerationException ex) {
|
||||
logFastClassGenerationFailure(this.method);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return super.invokeJoinpoint();
|
||||
return super.invokeJoinpoint();
|
||||
}
|
||||
|
||||
static boolean isMethodProxyCompatible(Method method) {
|
||||
return (Modifier.isPublic(method.getModifiers()) &&
|
||||
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
|
||||
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method));
|
||||
}
|
||||
|
||||
static void logFastClassGenerationFailure(Method method) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to generate CGLIB fast class for method: " + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2021 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,9 +43,11 @@ import org.springframework.aop.SpringProxy;
|
||||
* @see AdvisedSupport#setProxyTargetClass
|
||||
* @see AdvisedSupport#setInterfaces
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7930414337282325166L;
|
||||
|
||||
|
||||
@Override
|
||||
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
|
||||
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -421,11 +421,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
|
||||
* are unaffected by such changes.
|
||||
*/
|
||||
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
|
||||
if (this.advisorChainInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
|
||||
if (!this.advisorChainInitialized && !ObjectUtils.isEmpty(this.interceptorNames)) {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
|
||||
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
|
||||
@@ -464,9 +460,9 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
|
||||
addAdvisorOnChainCreation(advice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.advisorChainInitialized = true;
|
||||
this.advisorChainInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+8
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -22,13 +22,18 @@ import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnot
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class JCacheAspectJNamespaceConfigTests extends AbstractJCacheAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext(
|
||||
"/org/springframework/cache/config/annotation-jcache-aspectj.xml");
|
||||
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
|
||||
// Disallow bean definition overriding to test https://github.com/spring-projects/spring-framework/pull/27499
|
||||
context.setAllowBeanDefinitionOverriding(false);
|
||||
context.load("/org/springframework/cache/config/annotation-jcache-aspectj.xml");
|
||||
context.refresh();
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -459,7 +459,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
|
||||
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
|
||||
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
|
||||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -350,7 +350,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
else {
|
||||
String scopeName = mbd.getScope();
|
||||
if (!StringUtils.hasLength(scopeName)) {
|
||||
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
|
||||
throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
|
||||
}
|
||||
Scope scope = this.scopes.get(scopeName);
|
||||
if (scope == null) {
|
||||
|
||||
+11
-5
@@ -62,6 +62,8 @@ import org.springframework.util.StringUtils;
|
||||
@SuppressWarnings("serial")
|
||||
class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
|
||||
private static final String DESTROY_METHOD_NAME = "destroy";
|
||||
|
||||
private static final String CLOSE_METHOD_NAME = "close";
|
||||
|
||||
private static final String SHUTDOWN_METHOD_NAME = "shutdown";
|
||||
@@ -104,13 +106,16 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
Assert.notNull(bean, "Disposable bean must not be null");
|
||||
this.bean = bean;
|
||||
this.beanName = beanName;
|
||||
this.invokeDisposableBean =
|
||||
(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
|
||||
this.invokeDisposableBean = (bean instanceof DisposableBean &&
|
||||
!beanDefinition.isExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
|
||||
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
|
||||
this.acc = acc;
|
||||
|
||||
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
|
||||
if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
|
||||
if (destroyMethodName != null &&
|
||||
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) &&
|
||||
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
|
||||
|
||||
this.destroyMethodName = destroyMethodName;
|
||||
Method destroyMethod = determineDestroyMethod(destroyMethodName);
|
||||
if (destroyMethod == null) {
|
||||
@@ -135,6 +140,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
this.destroyMethod = destroyMethod;
|
||||
}
|
||||
|
||||
this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
|
||||
}
|
||||
|
||||
@@ -349,8 +355,8 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
destroyMethodName = beanDefinition.getDestroyMethodName();
|
||||
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
|
||||
(destroyMethodName == null && bean instanceof AutoCloseable)) {
|
||||
// Only perform destroy method inference or Closeable detection
|
||||
// in case of the bean not explicitly implementing DisposableBean
|
||||
// Only perform destroy method inference in case of the bean
|
||||
// not explicitly implementing the DisposableBean interface
|
||||
destroyMethodName = null;
|
||||
if (!(bean instanceof DisposableBean)) {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -64,24 +64,24 @@ public final class ParserContext {
|
||||
}
|
||||
|
||||
|
||||
public final XmlReaderContext getReaderContext() {
|
||||
public XmlReaderContext getReaderContext() {
|
||||
return this.readerContext;
|
||||
}
|
||||
|
||||
public final BeanDefinitionRegistry getRegistry() {
|
||||
public BeanDefinitionRegistry getRegistry() {
|
||||
return this.readerContext.getRegistry();
|
||||
}
|
||||
|
||||
public final BeanDefinitionParserDelegate getDelegate() {
|
||||
public BeanDefinitionParserDelegate getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final BeanDefinition getContainingBeanDefinition() {
|
||||
public BeanDefinition getContainingBeanDefinition() {
|
||||
return this.containingBeanDefinition;
|
||||
}
|
||||
|
||||
public final boolean isNested() {
|
||||
public boolean isNested() {
|
||||
return (this.containingBeanDefinition != null);
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -58,6 +58,8 @@ import org.springframework.lang.Nullable;
|
||||
* @since 1.1
|
||||
* @see SchedulerFactoryBean#setDataSource
|
||||
* @see SchedulerFactoryBean#setNonTransactionalDataSource
|
||||
* @see SchedulerFactoryBean#getConfigTimeDataSource()
|
||||
* @see SchedulerFactoryBean#getConfigTimeNonTransactionalDataSource()
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
|
||||
*/
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -313,6 +313,7 @@ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBe
|
||||
* If set, this will override corresponding settings in Quartz properties.
|
||||
* <p>Note: If this is set, the Quartz settings should not define
|
||||
* a job store "dataSource" to avoid meaningless double configuration.
|
||||
* Also, do not define a "org.quartz.jobStore.class" property at all.
|
||||
* <p>A Spring-specific subclass of Quartz' JobStoreCMT will be used.
|
||||
* It is therefore strongly recommended to perform all operations on
|
||||
* the Scheduler within Spring-managed (or plain JTA) transactions.
|
||||
|
||||
@@ -34,8 +34,8 @@ dependencies {
|
||||
testCompile("org.codehaus.groovy:groovy-test")
|
||||
testCompile("org.codehaus.groovy:groovy-xml")
|
||||
testCompile("org.apache.commons:commons-pool2")
|
||||
testCompile("javax.inject:javax.inject-tck")
|
||||
testCompile("org.awaitility:awaitility")
|
||||
testCompile("javax.inject:javax.inject-tck")
|
||||
testRuntime("javax.xml.bind:jaxb-api")
|
||||
testRuntime("org.glassfish:javax.el")
|
||||
// Substitute for javax.management:jmxremote_optional:1.0.1_04 (not available on Maven Central)
|
||||
|
||||
+15
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -244,17 +244,21 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
||||
|
||||
private static void registerCacheAspect(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME)) {
|
||||
Object eleSource = parserContext.extractSource(element);
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(JCACHE_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, eleSource);
|
||||
String sourceName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||
def.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME));
|
||||
BeanDefinition cacheOperationSourceDef = createJCacheOperationSourceBeanDefinition(element, source);
|
||||
String cacheOperationSourceName = parserContext.getReaderContext().registerWithGeneratedName(cacheOperationSourceDef);
|
||||
|
||||
RootBeanDefinition jcacheAspectDef = new RootBeanDefinition();
|
||||
jcacheAspectDef.setBeanClassName(JCACHE_ASPECT_CLASS_NAME);
|
||||
jcacheAspectDef.setFactoryMethodName("aspectOf");
|
||||
jcacheAspectDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(cacheOperationSourceName));
|
||||
parserContext.getRegistry().registerBeanDefinition(CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME, jcacheAspectDef);
|
||||
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(cacheOperationSourceDef, cacheOperationSourceName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(jcacheAspectDef, CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME));
|
||||
parserContext.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-27
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,11 +16,12 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -93,9 +94,9 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
|
||||
private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
|
||||
|
||||
private final List<TypeFilter> includeFilters = new LinkedList<>();
|
||||
private final List<TypeFilter> includeFilters = new ArrayList<>();
|
||||
|
||||
private final List<TypeFilter> excludeFilters = new LinkedList<>();
|
||||
private final List<TypeFilter> excludeFilters = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
private Environment environment;
|
||||
@@ -424,40 +425,38 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
if (traceEnabled) {
|
||||
logger.trace("Scanning " + resource);
|
||||
}
|
||||
if (resource.isReadable()) {
|
||||
try {
|
||||
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
|
||||
if (isCandidateComponent(metadataReader)) {
|
||||
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
|
||||
sbd.setSource(resource);
|
||||
if (isCandidateComponent(sbd)) {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Identified candidate component class: " + resource);
|
||||
}
|
||||
candidates.add(sbd);
|
||||
}
|
||||
else {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Ignored because not a concrete top-level class: " + resource);
|
||||
}
|
||||
try {
|
||||
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
|
||||
if (isCandidateComponent(metadataReader)) {
|
||||
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
|
||||
sbd.setSource(resource);
|
||||
if (isCandidateComponent(sbd)) {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Identified candidate component class: " + resource);
|
||||
}
|
||||
candidates.add(sbd);
|
||||
}
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Ignored because not matching any filter: " + resource);
|
||||
if (debugEnabled) {
|
||||
logger.debug("Ignored because not a concrete top-level class: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Ignored because not matching any filter: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
catch (FileNotFoundException ex) {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Ignored because not readable: " + resource);
|
||||
logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -332,7 +332,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
|
||||
|
||||
private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
|
||||
private InjectionMetadata findResourceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
|
||||
// Fall back to class name as cache key, for backwards compatibility with custom callers.
|
||||
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
|
||||
// Quick check on the concurrent map first, with minimal locking.
|
||||
@@ -352,7 +352,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
|
||||
private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
|
||||
if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
|
||||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
@@ -464,6 +464,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
public void releaseTarget(Object target) {
|
||||
}
|
||||
};
|
||||
|
||||
ProxyFactory pf = new ProxyFactory();
|
||||
pf.setTargetSource(ts);
|
||||
if (element.lookupType.isInterface()) {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -73,7 +73,7 @@ class ComponentScanAnnotationParser {
|
||||
}
|
||||
|
||||
|
||||
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
|
||||
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
|
||||
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
|
||||
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -453,8 +453,8 @@ class ConfigurationClassEnhancer {
|
||||
* instance directly. If a FactoryBean instance is fetched through the container via &-dereferencing,
|
||||
* it will not be proxied. This too is aligned with the way XML configuration works.
|
||||
*/
|
||||
private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
|
||||
final ConfigurableBeanFactory beanFactory, final String beanName) {
|
||||
private Object enhanceFactoryBean(Object factoryBean, Class<?> exposedType,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
try {
|
||||
Class<?> clazz = factoryBean.getClass();
|
||||
@@ -489,8 +489,8 @@ class ConfigurationClassEnhancer {
|
||||
return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
|
||||
}
|
||||
|
||||
private Object createInterfaceProxyForFactoryBean(final Object factoryBean, Class<?> interfaceType,
|
||||
final ConfigurableBeanFactory beanFactory, final String beanName) {
|
||||
private Object createInterfaceProxyForFactoryBean(Object factoryBean, Class<?> interfaceType,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
return Proxy.newProxyInstance(
|
||||
factoryBean.getClass().getClassLoader(), new Class<?>[] {interfaceType},
|
||||
@@ -502,8 +502,8 @@ class ConfigurationClassEnhancer {
|
||||
});
|
||||
}
|
||||
|
||||
private Object createCglibProxyForFactoryBean(final Object factoryBean,
|
||||
final ConfigurableBeanFactory beanFactory, final String beanName) {
|
||||
private Object createCglibProxyForFactoryBean(Object factoryBean,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(factoryBean.getClass());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2021 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,11 @@ import java.lang.annotation.Target;
|
||||
* or {@link javax.inject.Inject}: In that context, it leads to the creation of a
|
||||
* lazy-resolution proxy for all affected dependencies, as an alternative to using
|
||||
* {@link org.springframework.beans.factory.ObjectFactory} or {@link javax.inject.Provider}.
|
||||
* Please note that such a lazy-resolution proxy will always be injected; if the target
|
||||
* dependency does not exist, you will only be able to find out through an exception on
|
||||
* invocation. As a consequence, such an injection point results in unintuitive behavior
|
||||
* for optional dependencies. For a programmatic equivalent, allowing for lazy references
|
||||
* with more sophistication, consider {@link org.springframework.beans.factory.ObjectProvider}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
|
||||
+34
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -65,10 +65,10 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIOException;
|
||||
|
||||
/**
|
||||
* @since 13.03.2003
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 13.03.2003
|
||||
*/
|
||||
public class ProxyFactoryBeanTests {
|
||||
|
||||
@@ -631,20 +631,50 @@ public class ProxyFactoryBeanTests {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(FROZEN_CONTEXT, CLASS));
|
||||
|
||||
Advised advised = (Advised)bf.getBean("frozen");
|
||||
Advised advised = (Advised) bf.getBean("frozen");
|
||||
assertThat(advised.isFrozen()).as("The proxy should be frozen").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDetectsInterfaces() throws Exception {
|
||||
public void testDetectsInterfaces() {
|
||||
ProxyFactoryBean fb = new ProxyFactoryBean();
|
||||
fb.setTarget(new TestBean());
|
||||
fb.addAdvice(new DebugInterceptor());
|
||||
fb.setBeanFactory(new DefaultListableBeanFactory());
|
||||
|
||||
ITestBean proxy = (ITestBean) fb.getObject();
|
||||
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithInterceptorNames() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerSingleton("debug", new DebugInterceptor());
|
||||
|
||||
ProxyFactoryBean fb = new ProxyFactoryBean();
|
||||
fb.setTarget(new TestBean());
|
||||
fb.setInterceptorNames("debug");
|
||||
fb.setBeanFactory(bf);
|
||||
|
||||
Advised proxy = (Advised) fb.getObject();
|
||||
assertThat(proxy.getAdvisors().length).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithLateInterceptorNames() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerSingleton("debug", new DebugInterceptor());
|
||||
|
||||
ProxyFactoryBean fb = new ProxyFactoryBean();
|
||||
fb.setTarget(new TestBean());
|
||||
fb.setBeanFactory(bf);
|
||||
fb.getObject();
|
||||
|
||||
fb.setInterceptorNames("debug");
|
||||
Advised proxy = (Advised) fb.getObject();
|
||||
assertThat(proxy.getAdvisors().length).isEqualTo(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fires only on void methods. Saves list of methods intercepted.
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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,6 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/**
|
||||
* Tests cornering the issue reported in SPR-8080. If the product of a @Bean method
|
||||
* was @Autowired into a configuration class while at the same time the declaring
|
||||
@@ -34,7 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* 'currently in creation' status of the autowired bean and result in creating multiple
|
||||
* instances of the given @Bean, violating container scoping / singleton semantics.
|
||||
*
|
||||
* This is resolved through no longer relying on 'currently in creation' status, but
|
||||
* <p>This is resolved through no longer relying on 'currently in creation' status, but
|
||||
* rather on a thread local that informs the enhanced bean method implementation whether
|
||||
* the factory is the caller or not.
|
||||
*
|
||||
|
||||
+41
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -26,7 +26,6 @@ import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
/**
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
@@ -47,6 +46,7 @@ public class DestroyMethodInferenceTests {
|
||||
WithLocalShutdownMethod c7 = ctx.getBean("c7", WithLocalShutdownMethod.class);
|
||||
WithInheritedCloseMethod c8 = ctx.getBean("c8", WithInheritedCloseMethod.class);
|
||||
WithDisposableBean c9 = ctx.getBean("c9", WithDisposableBean.class);
|
||||
WithAutoCloseable c10 = ctx.getBean("c10", WithAutoCloseable.class);
|
||||
|
||||
assertThat(c0.closed).as("c0").isFalse();
|
||||
assertThat(c1.closed).as("c1").isFalse();
|
||||
@@ -58,6 +58,8 @@ public class DestroyMethodInferenceTests {
|
||||
assertThat(c7.closed).as("c7").isFalse();
|
||||
assertThat(c8.closed).as("c8").isFalse();
|
||||
assertThat(c9.closed).as("c9").isFalse();
|
||||
assertThat(c10.closed).as("c10").isFalse();
|
||||
|
||||
ctx.close();
|
||||
assertThat(c0.closed).as("c0").isTrue();
|
||||
assertThat(c1.closed).as("c1").isTrue();
|
||||
@@ -69,6 +71,7 @@ public class DestroyMethodInferenceTests {
|
||||
assertThat(c7.closed).as("c7").isTrue();
|
||||
assertThat(c8.closed).as("c8").isFalse();
|
||||
assertThat(c9.closed).as("c9").isTrue();
|
||||
assertThat(c10.closed).as("c10").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,21 +83,29 @@ public class DestroyMethodInferenceTests {
|
||||
WithLocalCloseMethod x3 = ctx.getBean("x3", WithLocalCloseMethod.class);
|
||||
WithNoCloseMethod x4 = ctx.getBean("x4", WithNoCloseMethod.class);
|
||||
WithInheritedCloseMethod x8 = ctx.getBean("x8", WithInheritedCloseMethod.class);
|
||||
WithDisposableBean x9 = ctx.getBean("x9", WithDisposableBean.class);
|
||||
WithAutoCloseable x10 = ctx.getBean("x10", WithAutoCloseable.class);
|
||||
|
||||
assertThat(x1.closed).isFalse();
|
||||
assertThat(x2.closed).isFalse();
|
||||
assertThat(x3.closed).isFalse();
|
||||
assertThat(x4.closed).isFalse();
|
||||
assertThat(x8.closed).isFalse();
|
||||
assertThat(x9.closed).isFalse();
|
||||
assertThat(x10.closed).isFalse();
|
||||
|
||||
ctx.close();
|
||||
assertThat(x1.closed).isFalse();
|
||||
assertThat(x2.closed).isTrue();
|
||||
assertThat(x3.closed).isTrue();
|
||||
assertThat(x4.closed).isFalse();
|
||||
assertThat(x8.closed).isFalse();
|
||||
assertThat(x9.closed).isTrue();
|
||||
assertThat(x10.closed).isTrue();
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Config {
|
||||
|
||||
@Bean(destroyMethod = "explicitClose")
|
||||
@@ -155,6 +166,11 @@ public class DestroyMethodInferenceTests {
|
||||
public WithDisposableBean c9() {
|
||||
return new WithDisposableBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WithAutoCloseable c10() {
|
||||
return new WithAutoCloseable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,17 +205,6 @@ public class DestroyMethodInferenceTests {
|
||||
}
|
||||
|
||||
|
||||
static class WithDisposableBean implements DisposableBean {
|
||||
|
||||
boolean closed = false;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class WithNoCloseMethod {
|
||||
|
||||
boolean closed = false;
|
||||
@@ -215,4 +220,26 @@ public class DestroyMethodInferenceTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class WithDisposableBean implements DisposableBean {
|
||||
|
||||
boolean closed = false;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class WithAutoCloseable implements AutoCloseable {
|
||||
|
||||
boolean closed = false;
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-1
@@ -15,10 +15,16 @@
|
||||
class="org.springframework.context.annotation.DestroyMethodInferenceTests.WithInheritedCloseMethod"
|
||||
destroy-method=""/>
|
||||
|
||||
<bean id="x9"
|
||||
class="org.springframework.context.annotation.DestroyMethodInferenceTests.WithDisposableBean"
|
||||
destroy-method=""/>
|
||||
|
||||
<bean id="x10"
|
||||
class="org.springframework.context.annotation.DestroyMethodInferenceTests.WithAutoCloseable"/>
|
||||
|
||||
<beans default-destroy-method="(inferred)">
|
||||
<bean id="x3"
|
||||
class="org.springframework.context.annotation.DestroyMethodInferenceTests$WithLocalCloseMethod"/>
|
||||
|
||||
<bean id="x4"
|
||||
class="org.springframework.context.annotation.DestroyMethodInferenceTests$WithNoCloseMethod"/>
|
||||
</beans>
|
||||
|
||||
@@ -53,22 +53,22 @@ dependencies {
|
||||
optional("io.reactivex:rxjava-reactive-streams")
|
||||
optional("io.reactivex.rxjava2:rxjava")
|
||||
optional("io.netty:netty-buffer")
|
||||
testCompile("io.projectreactor:reactor-test")
|
||||
testCompile("com.google.code.findbugs:jsr305")
|
||||
testCompile(project(":kotlin-coroutines"))
|
||||
testCompile("javax.annotation:javax.annotation-api")
|
||||
testCompile("javax.xml.bind:jaxb-api")
|
||||
testCompile("com.google.code.findbugs:jsr305")
|
||||
testCompile("com.fasterxml.woodstox:woodstox-core")
|
||||
testCompile("org.xmlunit:xmlunit-assertj")
|
||||
testCompile("org.xmlunit:xmlunit-matchers")
|
||||
testCompile(project(":kotlin-coroutines"))
|
||||
testCompile("io.projectreactor:reactor-test")
|
||||
testCompile("io.projectreactor.tools:blockhound")
|
||||
testFixturesImplementation("io.projectreactor:reactor-test")
|
||||
testFixturesImplementation("com.google.code.findbugs:jsr305")
|
||||
testFixturesImplementation("org.junit.platform:junit-platform-launcher")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params")
|
||||
testFixturesImplementation("org.assertj:assertj-core")
|
||||
testFixturesImplementation("org.xmlunit:xmlunit-assertj")
|
||||
testFixturesImplementation("io.projectreactor:reactor-test")
|
||||
}
|
||||
|
||||
jar {
|
||||
|
||||
@@ -499,6 +499,7 @@ public class ReflectUtils {
|
||||
ProtectionDomain protectionDomain, Class<?> contextClass) throws Exception {
|
||||
|
||||
Class c = null;
|
||||
Throwable t = THROWABLE;
|
||||
|
||||
// Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches
|
||||
if (contextClass != null && contextClass.getClassLoader() == loader &&
|
||||
@@ -516,6 +517,7 @@ public class ReflectUtils {
|
||||
// in case of plain LinkageError (class already defined)
|
||||
// or IllegalArgumentException (class in different package):
|
||||
// fall through to traditional ClassLoader.defineClass below
|
||||
t = target;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new CodeGenerationException(ex);
|
||||
@@ -543,6 +545,7 @@ public class ReflectUtils {
|
||||
if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) {
|
||||
throw new CodeGenerationException(ex);
|
||||
}
|
||||
t = ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,7 +567,7 @@ public class ReflectUtils {
|
||||
|
||||
// No defineClass variant available at all?
|
||||
if (c == null) {
|
||||
throw new CodeGenerationException(THROWABLE);
|
||||
throw new CodeGenerationException(t);
|
||||
}
|
||||
|
||||
// Force static initializers to run.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,7 +47,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
*
|
||||
* <p>By default, depending on classpath availability, adapters are registered
|
||||
* for Reactor, RxJava 1, RxJava 2 types, {@link CompletableFuture}, Java 9+
|
||||
* {@code Flow.Publisher} and Kotlin Coroutines {@code Deferred} and {@code Flow}.
|
||||
* {@code Flow.Publisher}, and Kotlin Coroutines {@code Deferred} and {@code Flow}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
@@ -74,6 +74,10 @@ public class ReactiveAdapterRegistry {
|
||||
boolean reactorRegistered = false;
|
||||
if (ClassUtils.isPresent("reactor.core.publisher.Flux", classLoader)) {
|
||||
new ReactorRegistrar().registerAdapters(this);
|
||||
if (ClassUtils.isPresent("java.util.concurrent.Flow.Publisher", classLoader)) {
|
||||
// Java 9+ Flow.Publisher
|
||||
new ReactorJdkFlowAdapterRegistrar().registerAdapter(this);
|
||||
}
|
||||
reactorRegistered = true;
|
||||
}
|
||||
this.reactorPresent = reactorRegistered;
|
||||
@@ -83,20 +87,12 @@ public class ReactiveAdapterRegistry {
|
||||
ClassUtils.isPresent("rx.RxReactiveStreams", classLoader)) {
|
||||
new RxJava1Registrar().registerAdapters(this);
|
||||
}
|
||||
|
||||
// RxJava2
|
||||
if (ClassUtils.isPresent("io.reactivex.Flowable", classLoader)) {
|
||||
new RxJava2Registrar().registerAdapters(this);
|
||||
}
|
||||
|
||||
// Java 9+ Flow.Publisher
|
||||
if (ClassUtils.isPresent("java.util.concurrent.Flow.Publisher", classLoader)) {
|
||||
new ReactorJdkFlowAdapterRegistrar().registerAdapter(this);
|
||||
}
|
||||
// If not present, do nothing for the time being...
|
||||
// We can fall back on "reactive-streams-flow-bridge" (once released)
|
||||
|
||||
// Coroutines
|
||||
// Kotlin Coroutines
|
||||
if (this.reactorPresent && ClassUtils.isPresent("kotlinx.coroutines.reactor.MonoKt", classLoader)) {
|
||||
new CoroutinesRegistrar().registerAdapters(this);
|
||||
}
|
||||
@@ -226,6 +222,35 @@ public class ReactiveAdapterRegistry {
|
||||
}
|
||||
|
||||
|
||||
private static class ReactorJdkFlowAdapterRegistrar {
|
||||
|
||||
void registerAdapter(ReactiveAdapterRegistry registry) {
|
||||
// Reflectively access optional JDK 9+ API (for runtime compatibility with JDK 8)
|
||||
|
||||
try {
|
||||
String publisherName = "java.util.concurrent.Flow.Publisher";
|
||||
Class<?> publisherClass = ClassUtils.forName(publisherName, getClass().getClassLoader());
|
||||
|
||||
String adapterName = "reactor.adapter.JdkFlowAdapter";
|
||||
Class<?> flowAdapterClass = ClassUtils.forName(adapterName, getClass().getClassLoader());
|
||||
|
||||
Method toFluxMethod = flowAdapterClass.getMethod("flowPublisherToFlux", publisherClass);
|
||||
Method toFlowMethod = flowAdapterClass.getMethod("publisherToFlowPublisher", Publisher.class);
|
||||
Object emptyFlow = ReflectionUtils.invokeMethod(toFlowMethod, null, Flux.empty());
|
||||
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.multiValue(publisherClass, () -> emptyFlow),
|
||||
source -> (Publisher<?>) ReflectionUtils.invokeMethod(toFluxMethod, null, source),
|
||||
publisher -> ReflectionUtils.invokeMethod(toFlowMethod, null, publisher)
|
||||
);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class RxJava1Registrar {
|
||||
|
||||
void registerAdapters(ReactiveAdapterRegistry registry) {
|
||||
@@ -280,35 +305,6 @@ public class ReactiveAdapterRegistry {
|
||||
}
|
||||
|
||||
|
||||
private static class ReactorJdkFlowAdapterRegistrar {
|
||||
|
||||
void registerAdapter(ReactiveAdapterRegistry registry) {
|
||||
// TODO: remove reflection when build requires JDK 9+
|
||||
|
||||
try {
|
||||
String publisherName = "java.util.concurrent.Flow.Publisher";
|
||||
Class<?> publisherClass = ClassUtils.forName(publisherName, getClass().getClassLoader());
|
||||
|
||||
String adapterName = "reactor.adapter.JdkFlowAdapter";
|
||||
Class<?> flowAdapterClass = ClassUtils.forName(adapterName, getClass().getClassLoader());
|
||||
|
||||
Method toFluxMethod = flowAdapterClass.getMethod("flowPublisherToFlux", publisherClass);
|
||||
Method toFlowMethod = flowAdapterClass.getMethod("publisherToFlowPublisher", Publisher.class);
|
||||
Object emptyFlow = ReflectionUtils.invokeMethod(toFlowMethod, null, Flux.empty());
|
||||
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.multiValue(publisherClass, () -> emptyFlow),
|
||||
source -> (Publisher<?>) ReflectionUtils.invokeMethod(toFluxMethod, null, source),
|
||||
publisher -> ReflectionUtils.invokeMethod(toFlowMethod, null, publisher)
|
||||
);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ReactiveAdapter variant that wraps adapted Publishers as {@link Flux} or
|
||||
* {@link Mono} depending on {@link ReactiveTypeDescriptor#isMultiValue()}.
|
||||
|
||||
+10
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -88,7 +88,15 @@ public abstract class AbstractFileResolvingResource extends AbstractResource {
|
||||
@Override
|
||||
public boolean isReadable() {
|
||||
try {
|
||||
URL url = getURL();
|
||||
return checkReadable(getURL());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean checkReadable(URL url) {
|
||||
try {
|
||||
if (ResourceUtils.isFileURL(url)) {
|
||||
// Proceed with file system resolution
|
||||
File file = getFile();
|
||||
|
||||
@@ -142,6 +142,18 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
return (resolveURL() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation checks for the resolution of a resource URL upfront,
|
||||
* then proceeding with {@link AbstractFileResolvingResource}'s length check.
|
||||
* @see java.lang.ClassLoader#getResource(String)
|
||||
* @see java.lang.Class#getResource(String)
|
||||
*/
|
||||
@Override
|
||||
public boolean isReadable() {
|
||||
URL url = resolveURL();
|
||||
return (url != null && checkReadable(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a URL for the underlying class path resource.
|
||||
* @return the resolved URL, or {@code null} if not resolvable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -48,8 +48,9 @@ public interface InputStreamSource {
|
||||
* creating mail attachments. For such a use case, it is <i>required</i>
|
||||
* that each {@code getInputStream()} call returns a fresh stream.
|
||||
* @return the input stream for the underlying resource (must not be {@code null})
|
||||
* @throws java.io.FileNotFoundException if the underlying resource doesn't exist
|
||||
* @throws java.io.FileNotFoundException if the underlying resource does not exist
|
||||
* @throws IOException if the content stream could not be opened
|
||||
* @see Resource#isReadable()
|
||||
*/
|
||||
InputStream getInputStream() throws IOException;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -462,9 +462,9 @@ public class DefaultDataBuffer implements DataBuffer {
|
||||
|
||||
private void checkIndex(int index, int length) {
|
||||
assertIndex(index >= 0, "index %d must be >= 0", index);
|
||||
assertIndex(length >= 0, "length %d must be >= 0", index);
|
||||
assertIndex(length >= 0, "length %d must be >= 0", length);
|
||||
assertIndex(index <= this.capacity, "index %d must be <= %d", index, this.capacity);
|
||||
assertIndex(length <= this.capacity, "length %d must be <= %d", index, this.capacity);
|
||||
assertIndex(length <= this.capacity, "length %d must be <= %d", length, this.capacity);
|
||||
}
|
||||
|
||||
private void assertIndex(boolean expression, String format, Object... args) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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,10 +17,12 @@
|
||||
package org.springframework.core.log;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for formatting and logging messages.
|
||||
@@ -35,31 +37,57 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public abstract class LogFormatUtils {
|
||||
|
||||
private static final Pattern NEWLINE_PATTERN = Pattern.compile("[\n\r]");
|
||||
|
||||
private static final Pattern CONTROL_CHARACTER_PATTERN = Pattern.compile("\\p{Cc}");
|
||||
|
||||
|
||||
/**
|
||||
* Format the given value via {@code toString()}, quoting it if it is a
|
||||
* {@link CharSequence}, and possibly truncating at 100 if limitLength is
|
||||
* set to true.
|
||||
* Convenience variant of {@link #formatValue(Object, int, boolean)} that
|
||||
* limits the length of a log message to 100 characters and also replaces
|
||||
* newline and control characters if {@code limitLength} is set to "true".
|
||||
* @param value the value to format
|
||||
* @param limitLength whether to truncate large formatted values (over 100)
|
||||
* @param limitLength whether to truncate the value at a length of 100
|
||||
* @return the formatted value
|
||||
*/
|
||||
public static String formatValue(@Nullable Object value, boolean limitLength) {
|
||||
return formatValue(value, (limitLength ? 100 : -1), limitLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given value via {@code toString()}, quoting it if it is a
|
||||
* {@link CharSequence}, truncating at the specified {@code maxLength}, and
|
||||
* compacting it into a single line when {@code replaceNewLines} is set.
|
||||
* @param value the value to be formatted
|
||||
* @param maxLength the max length, after which to truncate, or -1 for unlimited
|
||||
* @param replaceNewlinesAndControlCharacters whether to replace newline and
|
||||
* control characters with placeholders
|
||||
* @return the formatted value
|
||||
*/
|
||||
public static String formatValue(
|
||||
@Nullable Object value, int maxLength, boolean replaceNewlinesAndControlCharacters) {
|
||||
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
String str;
|
||||
String result;
|
||||
try {
|
||||
result = ObjectUtils.nullSafeToString(value);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
result = ObjectUtils.nullSafeToString(ex);
|
||||
}
|
||||
if (maxLength != -1) {
|
||||
result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result);
|
||||
}
|
||||
if (replaceNewlinesAndControlCharacters) {
|
||||
result = NEWLINE_PATTERN.matcher(result).replaceAll("<EOL>");
|
||||
result = CONTROL_CHARACTER_PATTERN.matcher(result).replaceAll("?");
|
||||
}
|
||||
if (value instanceof CharSequence) {
|
||||
str = "\"" + value + "\"";
|
||||
result = "\"" + result + "\"";
|
||||
}
|
||||
else {
|
||||
try {
|
||||
str = value.toString();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
str = ex.toString();
|
||||
}
|
||||
}
|
||||
return (limitLength && str.length() > 100 ? str.substring(0, 100) + " (truncated)..." : str);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -77,7 +77,7 @@ public class AntPathMatcher implements PathMatcher {
|
||||
|
||||
private static final int CACHE_TURNOFF_THRESHOLD = 65536;
|
||||
|
||||
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?}");
|
||||
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}");
|
||||
|
||||
private static final char[] WILDCARD_CHARS = { '*', '?', '{' };
|
||||
|
||||
@@ -641,7 +641,7 @@ public class AntPathMatcher implements PathMatcher {
|
||||
*/
|
||||
protected static class AntPathStringMatcher {
|
||||
|
||||
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?}|[^/{}]|\\\\[{}])+?)}");
|
||||
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
|
||||
|
||||
private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
|
||||
|
||||
|
||||
@@ -858,7 +858,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
|
||||
Reference<K, V> ref = ConcurrentReferenceHashMap.this.getReference(entry.getKey(), Restructure.NEVER);
|
||||
Entry<K, V> otherEntry = (ref != null ? ref.get() : null);
|
||||
if (otherEntry != null) {
|
||||
return ObjectUtils.nullSafeEquals(otherEntry.getValue(), otherEntry.getValue());
|
||||
return ObjectUtils.nullSafeEquals(entry.getValue(), otherEntry.getValue());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -966,6 +966,7 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen
|
||||
public void remove() {
|
||||
Assert.state(this.last != null, "No element to remove");
|
||||
ConcurrentReferenceHashMap.this.remove(this.last.getKey());
|
||||
this.last = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+25
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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,11 +41,13 @@ import org.springframework.util.comparator.NullSafeComparator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConcurrentReferenceHashMap}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class ConcurrentReferenceHashMapTests {
|
||||
|
||||
@@ -466,6 +468,7 @@ class ConcurrentReferenceHashMapTests {
|
||||
iterator.next();
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
assertThatIllegalStateException().isThrownBy(iterator::remove);
|
||||
iterator.next();
|
||||
assertThat(iterator.hasNext()).isFalse();
|
||||
assertThat(this.map).hasSize(2);
|
||||
@@ -486,6 +489,26 @@ class ConcurrentReferenceHashMapTests {
|
||||
assertThat(this.map.get(2)).isEqualTo("2b");
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsViaEntrySet() {
|
||||
this.map.put(1, "1");
|
||||
this.map.put(2, "2");
|
||||
this.map.put(3, "3");
|
||||
Set<Map.Entry<Integer, String>> entrySet = this.map.entrySet();
|
||||
Set<Map.Entry<Integer, String>> copy = new HashMap<>(this.map).entrySet();
|
||||
copy.forEach(entry -> assertThat(entrySet.contains(entry)).isTrue());
|
||||
this.map.put(1, "A");
|
||||
this.map.put(2, "B");
|
||||
this.map.put(3, "C");
|
||||
copy.forEach(entry -> assertThat(entrySet.contains(entry)).isFalse());
|
||||
this.map.put(1, "1");
|
||||
this.map.put(2, "2");
|
||||
this.map.put(3, "3");
|
||||
copy.forEach(entry -> assertThat(entrySet.contains(entry)).isTrue());
|
||||
entrySet.clear();
|
||||
copy.forEach(entry -> assertThat(entrySet.contains(entry)).isFalse());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Intended for use during development only")
|
||||
void shouldBeFasterThanSynchronizedMap() throws InterruptedException {
|
||||
@@ -583,7 +606,7 @@ class ConcurrentReferenceHashMapTests {
|
||||
}
|
||||
// For testing we want more control of the hash
|
||||
this.supplementalHash = super.getHash(o);
|
||||
return o == null ? 0 : o.hashCode();
|
||||
return (o != null ? o.hashCode() : 0);
|
||||
}
|
||||
|
||||
public int getSupplementalHash() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -20,7 +20,6 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.StopWatch.TaskInfo;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
@@ -41,7 +40,7 @@ class StopWatchTests {
|
||||
|
||||
private static final long duration1 = 200;
|
||||
private static final long duration2 = 100;
|
||||
private static final long fudgeFactor = 50;
|
||||
// private static final long fudgeFactor = 100;
|
||||
|
||||
private final StopWatch stopWatch = new StopWatch(ID);
|
||||
|
||||
@@ -78,18 +77,21 @@ class StopWatchTests {
|
||||
assertThat(stopWatch.currentTaskName()).isEqualTo(name1);
|
||||
stopWatch.stop();
|
||||
assertThat(stopWatch.isRunning()).isFalse();
|
||||
|
||||
/* Flaky StopWatch time assertions...
|
||||
assertThat(stopWatch.getLastTaskTimeNanos())
|
||||
.as("last task time in nanoseconds for task #1")
|
||||
.isGreaterThanOrEqualTo(millisToNanos(duration1 - fudgeFactor))
|
||||
.isLessThanOrEqualTo(millisToNanos(duration1 + fudgeFactor));
|
||||
.as("last task time in nanoseconds for task #1")
|
||||
.isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(duration1 - fudgeFactor))
|
||||
.isLessThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(duration1 + fudgeFactor));
|
||||
assertThat(stopWatch.getTotalTimeMillis())
|
||||
.as("total time in milliseconds for task #1")
|
||||
.isGreaterThanOrEqualTo(duration1 - fudgeFactor)
|
||||
.isLessThanOrEqualTo(duration1 + fudgeFactor);
|
||||
.as("total time in milliseconds for task #1")
|
||||
.isGreaterThanOrEqualTo(duration1 - fudgeFactor)
|
||||
.isLessThanOrEqualTo(duration1 + fudgeFactor);
|
||||
assertThat(stopWatch.getTotalTimeSeconds())
|
||||
.as("total time in seconds for task #1")
|
||||
.isGreaterThanOrEqualTo((duration1 - fudgeFactor) / 1000.0)
|
||||
.isLessThanOrEqualTo((duration1 + fudgeFactor) / 1000.0);
|
||||
.as("total time in seconds for task #1")
|
||||
.isGreaterThanOrEqualTo((duration1 - fudgeFactor) / 1000.0)
|
||||
.isLessThanOrEqualTo((duration1 + fudgeFactor) / 1000.0);
|
||||
*/
|
||||
|
||||
stopWatch.start(name2);
|
||||
Thread.sleep(duration2);
|
||||
@@ -97,18 +99,21 @@ class StopWatchTests {
|
||||
assertThat(stopWatch.currentTaskName()).isEqualTo(name2);
|
||||
stopWatch.stop();
|
||||
assertThat(stopWatch.isRunning()).isFalse();
|
||||
|
||||
/* Flaky StopWatch time assertions...
|
||||
assertThat(stopWatch.getLastTaskTimeNanos())
|
||||
.as("last task time in nanoseconds for task #2")
|
||||
.isGreaterThanOrEqualTo(millisToNanos(duration2))
|
||||
.isLessThanOrEqualTo(millisToNanos(duration2 + fudgeFactor));
|
||||
.as("last task time in nanoseconds for task #2")
|
||||
.isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(duration2))
|
||||
.isLessThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(duration2 + fudgeFactor));
|
||||
assertThat(stopWatch.getTotalTimeMillis())
|
||||
.as("total time in milliseconds for tasks #1 and #2")
|
||||
.isGreaterThanOrEqualTo(duration1 + duration2 - fudgeFactor)
|
||||
.isLessThanOrEqualTo(duration1 + duration2 + fudgeFactor);
|
||||
.as("total time in milliseconds for tasks #1 and #2")
|
||||
.isGreaterThanOrEqualTo(duration1 + duration2 - fudgeFactor)
|
||||
.isLessThanOrEqualTo(duration1 + duration2 + fudgeFactor);
|
||||
assertThat(stopWatch.getTotalTimeSeconds())
|
||||
.as("total time in seconds for task #2")
|
||||
.isGreaterThanOrEqualTo((duration1 + duration2 - fudgeFactor) / 1000.0)
|
||||
.isLessThanOrEqualTo((duration1 + duration2 + fudgeFactor) / 1000.0);
|
||||
.as("total time in seconds for task #2")
|
||||
.isGreaterThanOrEqualTo((duration1 + duration2 - fudgeFactor) / 1000.0)
|
||||
.isLessThanOrEqualTo((duration1 + duration2 + fudgeFactor) / 1000.0);
|
||||
*/
|
||||
|
||||
assertThat(stopWatch.getTaskCount()).isEqualTo(2);
|
||||
assertThat(stopWatch.prettyPrint()).contains(name1, name2);
|
||||
@@ -135,12 +140,8 @@ class StopWatchTests {
|
||||
assertThat(stopWatch.prettyPrint()).contains("No task info kept");
|
||||
assertThat(stopWatch.toString()).doesNotContain(name1, name2);
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
.isThrownBy(stopWatch::getTaskInfo)
|
||||
.withMessage("Task info is not being kept!");
|
||||
}
|
||||
|
||||
private static long millisToNanos(long duration) {
|
||||
return MILLISECONDS.toNanos(duration);
|
||||
.isThrownBy(stopWatch::getTaskInfo)
|
||||
.withMessage("Task info is not being kept!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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,7 +19,7 @@ package org.springframework.expression;
|
||||
/**
|
||||
* A bean resolver can be registered with the evaluation context and will kick in
|
||||
* for bean references: {@code @myBeanName} and {@code &myBeanName} expressions.
|
||||
* The <tt>&</tt> variant syntax allows access to the factory bean where relevant.
|
||||
* The {@code &} variant syntax allows access to the factory bean where relevant.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0.3
|
||||
@@ -28,7 +28,7 @@ public interface BeanResolver {
|
||||
|
||||
/**
|
||||
* Look up a bean by the given name and return a corresponding instance for it.
|
||||
* For attempting access to a factory bean, the name needs a <tt>&</tt> prefix.
|
||||
* For attempting access to a factory bean, the name needs a {@code &} prefix.
|
||||
* @param context the current evaluation context
|
||||
* @param beanName the name of the bean to look up
|
||||
* @return an object representing the bean
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2021 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,10 +40,11 @@ public interface TypeComparator {
|
||||
* Compare two given objects.
|
||||
* @param firstObject the first object
|
||||
* @param secondObject the second object
|
||||
* @return 0 if they are equal, <0 if the first is smaller than the second,
|
||||
* or >0 if the first is larger than the second
|
||||
* @return 0 if they are equal, a negative integer if the first is smaller than
|
||||
* the second, or a positive integer if the first is larger than the second
|
||||
* @throws EvaluationException if a problem occurs during comparison
|
||||
* (or if they are not comparable in the first place)
|
||||
* @see Comparable#compareTo
|
||||
*/
|
||||
int compare(@Nullable Object firstObject, @Nullable Object secondObject) throws EvaluationException;
|
||||
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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,8 +25,8 @@ import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
|
||||
/**
|
||||
* Represents a bean reference to a type, for example <tt>@foo</tt> or <tt>@'foo.bar'</tt>.
|
||||
* For a FactoryBean the syntax <tt>&foo</tt> can be used to access the factory itself.
|
||||
* Represents a bean reference to a type, for example {@code @foo} or {@code @'foo.bar'}.
|
||||
* For a FactoryBean the syntax {@code &foo} can be used to access the factory itself.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
@@ -64,13 +64,13 @@ public class BeanReference extends SpelNodeImpl {
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!this.beanName.startsWith(FACTORY_BEAN_PREFIX)) {
|
||||
sb.append("@");
|
||||
sb.append('@');
|
||||
}
|
||||
if (!this.beanName.contains(".")) {
|
||||
sb.append(this.beanName);
|
||||
}
|
||||
else {
|
||||
sb.append("'").append(this.beanName).append("'");
|
||||
sb.append('\'').append(this.beanName).append('\'');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
+46
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -46,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class MethodInvocationTests extends AbstractExpressionTests {
|
||||
|
||||
@@ -232,26 +233,54 @@ public class MethodInvocationTests extends AbstractExpressionTests {
|
||||
|
||||
@Test
|
||||
public void testVarargsInvocation01() {
|
||||
// Calling 'public int aVarargsMethod(String... strings)'
|
||||
//evaluate("aVarargsMethod('a','b','c')", 3, Integer.class);
|
||||
//evaluate("aVarargsMethod('a')", 1, Integer.class);
|
||||
evaluate("aVarargsMethod()", 0, Integer.class);
|
||||
evaluate("aVarargsMethod(1,2,3)", 3, Integer.class); // all need converting to strings
|
||||
evaluate("aVarargsMethod(1)", 1, Integer.class); // needs string conversion
|
||||
evaluate("aVarargsMethod(1,'a',3.0d)", 3, Integer.class); // first and last need conversion
|
||||
// evaluate("aVarargsMethod(new String[]{'a','b','c'})", 3, Integer.class);
|
||||
// Calling 'public String aVarargsMethod(String... strings)'
|
||||
evaluate("aVarargsMethod('a','b','c')", "[a, b, c]", String.class);
|
||||
evaluate("aVarargsMethod('a')", "[a]", String.class);
|
||||
evaluate("aVarargsMethod()", "[]", String.class);
|
||||
evaluate("aVarargsMethod(1,2,3)", "[1, 2, 3]", String.class); // all need converting to strings
|
||||
evaluate("aVarargsMethod(1)", "[1]", String.class); // needs string conversion
|
||||
evaluate("aVarargsMethod(1,'a',3.0d)", "[1, a, 3.0]", String.class); // first and last need conversion
|
||||
evaluate("aVarargsMethod(new String[]{'a','b','c'})", "[a, b, c]", String.class);
|
||||
evaluate("aVarargsMethod(new String[]{})", "[]", String.class);
|
||||
evaluate("aVarargsMethod(null)", "[null]", String.class);
|
||||
evaluate("aVarargsMethod(null,'a')", "[null, a]", String.class);
|
||||
evaluate("aVarargsMethod('a',null,'b')", "[a, null, b]", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVarargsInvocation02() {
|
||||
// Calling 'public int aVarargsMethod2(int i, String... strings)' - returns int+length_of_strings
|
||||
evaluate("aVarargsMethod2(5,'a','b','c')", 8, Integer.class);
|
||||
evaluate("aVarargsMethod2(2,'a')", 3, Integer.class);
|
||||
evaluate("aVarargsMethod2(4)", 4, Integer.class);
|
||||
evaluate("aVarargsMethod2(8,2,3)", 10, Integer.class);
|
||||
evaluate("aVarargsMethod2(9)", 9, Integer.class);
|
||||
evaluate("aVarargsMethod2(2,'a',3.0d)", 4, Integer.class);
|
||||
// evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class);
|
||||
// Calling 'public String aVarargsMethod2(int i, String... strings)'
|
||||
evaluate("aVarargsMethod2(5,'a','b','c')", "5-[a, b, c]", String.class);
|
||||
evaluate("aVarargsMethod2(2,'a')", "2-[a]", String.class);
|
||||
evaluate("aVarargsMethod2(4)", "4-[]", String.class);
|
||||
evaluate("aVarargsMethod2(8,2,3)", "8-[2, 3]", String.class);
|
||||
evaluate("aVarargsMethod2(2,'a',3.0d)", "2-[a, 3.0]", String.class);
|
||||
evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", "8-[a, b, c]", String.class);
|
||||
evaluate("aVarargsMethod2(8,new String[]{})", "8-[]", String.class);
|
||||
evaluate("aVarargsMethod2(8,null)", "8-[null]", String.class);
|
||||
evaluate("aVarargsMethod2(8,null,'a')", "8-[null, a]", String.class);
|
||||
evaluate("aVarargsMethod2(8,'a',null,'b')", "8-[a, null, b]", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVarargsOptionalInvocation() {
|
||||
// Calling 'public String optionalVarargsMethod(Optional<String>... values)'
|
||||
evaluate("optionalVarargsMethod()", "[]", String.class);
|
||||
evaluate("optionalVarargsMethod(new String[0])", "[]", String.class);
|
||||
evaluate("optionalVarargsMethod('a')", "[Optional[a]]", String.class);
|
||||
evaluate("optionalVarargsMethod('a','b','c')", "[Optional[a], Optional[b], Optional[c]]", String.class);
|
||||
evaluate("optionalVarargsMethod(9)", "[Optional[9]]", String.class);
|
||||
evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class);
|
||||
evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class);
|
||||
evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class);
|
||||
// The following should actually evaluate to [Optional.empty] instead of [null],
|
||||
// but ReflectionHelper.convertArguments() passes the array type instead of
|
||||
// the array's component type as the target type to the ConversionService, and
|
||||
// GenericConversionService.convertNullSource() therefore fails to convert null
|
||||
// to Optional.empty().
|
||||
evaluate("optionalVarargsMethod(null)", "[null]", String.class);
|
||||
evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class);
|
||||
evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+10
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2021 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,6 +16,7 @@
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
@@ -51,10 +52,10 @@ public class TestScenarioCreator {
|
||||
TestScenarioCreator.class.getDeclaredMethod("reverseInt", Integer.TYPE, Integer.TYPE, Integer.TYPE));
|
||||
testContext.registerFunction("reverseString",
|
||||
TestScenarioCreator.class.getDeclaredMethod("reverseString", String.class));
|
||||
testContext.registerFunction("varargsFunctionReverseStringsAndMerge",
|
||||
TestScenarioCreator.class.getDeclaredMethod("varargsFunctionReverseStringsAndMerge", String[].class));
|
||||
testContext.registerFunction("varargsFunctionReverseStringsAndMerge2",
|
||||
TestScenarioCreator.class.getDeclaredMethod("varargsFunctionReverseStringsAndMerge2", Integer.TYPE, String[].class));
|
||||
testContext.registerFunction("varargsFunction",
|
||||
TestScenarioCreator.class.getDeclaredMethod("varargsFunction", String[].class));
|
||||
testContext.registerFunction("varargsFunction2",
|
||||
TestScenarioCreator.class.getDeclaredMethod("varargsFunction2", Integer.TYPE, String[].class));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
@@ -108,25 +109,12 @@ public class TestScenarioCreator {
|
||||
return backwards.toString();
|
||||
}
|
||||
|
||||
public static String varargsFunctionReverseStringsAndMerge(String... strings) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (strings != null) {
|
||||
for (int i = strings.length - 1; i >= 0; i--) {
|
||||
sb.append(strings[i]);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
public static String varargsFunction(String... strings) {
|
||||
return Arrays.toString(strings);
|
||||
}
|
||||
|
||||
public static String varargsFunctionReverseStringsAndMerge2(int j, String... strings) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(j);
|
||||
if (strings != null) {
|
||||
for (int i = strings.length - 1; i >= 0; i--) {
|
||||
sb.append(strings[i]);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
public static String varargsFunction2(int i, String... strings) {
|
||||
return String.valueOf(i) + "-" + Arrays.toString(strings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-10
@@ -58,16 +58,23 @@ public class VariableAndFunctionTests extends AbstractExpressionTests {
|
||||
|
||||
@Test
|
||||
public void testCallVarargsFunction() {
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge('a','b','c')", "cba", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge('a')", "a", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge()", "", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge('b',25)", "25b", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge(25)", "25", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge2(1,'a','b','c')", "1cba", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge2(2,'a')", "2a", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge2(3)", "3", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge2(4,'b',25)", "425b", String.class);
|
||||
evaluate("#varargsFunctionReverseStringsAndMerge2(5,25)", "525", String.class);
|
||||
evaluate("#varargsFunction()", "[]", String.class);
|
||||
evaluate("#varargsFunction(new String[0])", "[]", String.class);
|
||||
evaluate("#varargsFunction('a','b','c')", "[a, b, c]", String.class);
|
||||
evaluate("#varargsFunction('a')", "[a]", String.class);
|
||||
evaluate("#varargsFunction('b',25)", "[b, 25]", String.class);
|
||||
evaluate("#varargsFunction(25)", "[25]", String.class);
|
||||
evaluate("#varargsFunction(null)", "[null]", String.class);
|
||||
evaluate("#varargsFunction('a',null,'b')", "[a, null, b]", String.class);
|
||||
|
||||
evaluate("#varargsFunction2(9)", "9-[]", String.class);
|
||||
evaluate("#varargsFunction2(9, new String[0])", "9-[]", String.class);
|
||||
evaluate("#varargsFunction2(9,'a','b','c')", "9-[a, b, c]", String.class);
|
||||
evaluate("#varargsFunction2(9,'a')", "9-[a]", String.class);
|
||||
evaluate("#varargsFunction2(9,'b',25)", "9-[b, 25]", String.class);
|
||||
evaluate("#varargsFunction2(9,25)", "9-[25]", String.class);
|
||||
evaluate("#varargsFunction2(9,null)", "9-[null]", String.class);
|
||||
evaluate("#varargsFunction2(9,'a',null,'b')", "9-[a, null, b]", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+12
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2021 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,11 +17,13 @@
|
||||
package org.springframework.expression.spel.testresources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -190,16 +192,17 @@ public class Inventor {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
public int aVarargsMethod(String... strings) {
|
||||
if (strings == null)
|
||||
return 0;
|
||||
return strings.length;
|
||||
public String aVarargsMethod(String... strings) {
|
||||
return Arrays.toString(strings);
|
||||
}
|
||||
|
||||
public int aVarargsMethod2(int i, String... strings) {
|
||||
if (strings == null)
|
||||
return i;
|
||||
return strings.length + i;
|
||||
public String aVarargsMethod2(int i, String... strings) {
|
||||
return String.valueOf(i) + "-" + Arrays.toString(strings);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public String optionalVarargsMethod(Optional<String>... values) {
|
||||
return Arrays.toString(values);
|
||||
}
|
||||
|
||||
public Inventor(String... strings) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -21,22 +21,21 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.jdbc.support.KeyHolder;
|
||||
import org.springframework.jdbc.support.rowset.SqlRowSet;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Interface specifying a basic set of JDBC operations.
|
||||
* Implemented by {@link JdbcTemplate}. Not often used directly, but a useful
|
||||
*
|
||||
* <p>Implemented by {@link JdbcTemplate}. Not often used directly, but a useful
|
||||
* option to enhance testability, as it can easily be mocked or stubbed.
|
||||
*
|
||||
* <p>Alternatively, the standard JDBC infrastructure can be mocked.
|
||||
* However, mocking this interface constitutes significantly less work.
|
||||
* As an alternative to a mock objects approach to testing data access code,
|
||||
* consider the powerful integration testing support provided in the
|
||||
* {@code org.springframework.test} package, shipped in
|
||||
* {@code spring-test.jar}.
|
||||
* consider the powerful integration testing support provided via the <em>Spring
|
||||
* TestContext Framework</em>, in the {@code spring-test} artifact.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
@@ -143,8 +142,8 @@ public interface JdbcOperations {
|
||||
* @param rowMapper a callback that will map one object per row
|
||||
* @return the single mapped object (may be {@code null} if the given
|
||||
* {@link RowMapper} returned {@code} null)
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not
|
||||
* return exactly one row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if there is any problem executing the query
|
||||
* @see #queryForObject(String, RowMapper, Object...)
|
||||
*/
|
||||
@@ -163,8 +162,10 @@ public interface JdbcOperations {
|
||||
* @param sql the SQL query to execute
|
||||
* @param requiredType the type that the result object is expected to match
|
||||
* @return the result object of the required type, or {@code null} in case of SQL NULL
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not return
|
||||
* exactly one row, or does not return exactly one column in that row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws org.springframework.jdbc.IncorrectResultSetColumnCountException
|
||||
* if the query does not return a row containing a single column
|
||||
* @throws DataAccessException if there is any problem executing the query
|
||||
* @see #queryForObject(String, Class, Object...)
|
||||
*/
|
||||
@@ -181,8 +182,8 @@ public interface JdbcOperations {
|
||||
* mapped to a Map (one entry for each column, using the column name as the key).
|
||||
* @param sql the SQL query to execute
|
||||
* @return the result Map (one entry per column, with column name as key)
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not
|
||||
* return exactly one row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if there is any problem executing the query
|
||||
* @see #queryForMap(String, Object...)
|
||||
* @see ColumnMapRowMapper
|
||||
@@ -528,8 +529,8 @@ public interface JdbcOperations {
|
||||
* @param rowMapper a callback that will map one object per row
|
||||
* @return the single mapped object (may be {@code null} if the given
|
||||
* {@link RowMapper} returned {@code} null)
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not
|
||||
* return exactly one row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if the query fails
|
||||
*/
|
||||
@Nullable
|
||||
@@ -548,8 +549,8 @@ public interface JdbcOperations {
|
||||
* @param rowMapper a callback that will map one object per row
|
||||
* @return the single mapped object (may be {@code null} if the given
|
||||
* {@link RowMapper} returned {@code} null)
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not
|
||||
* return exactly one row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if the query fails
|
||||
*/
|
||||
@Nullable
|
||||
@@ -567,8 +568,8 @@ public interface JdbcOperations {
|
||||
* only the argument value but also the SQL type and optionally the scale
|
||||
* @return the single mapped object (may be {@code null} if the given
|
||||
* {@link RowMapper} returned {@code} null)
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not
|
||||
* return exactly one row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if the query fails
|
||||
* @since 3.0.1
|
||||
*/
|
||||
@@ -586,8 +587,10 @@ public interface JdbcOperations {
|
||||
* (constants from {@code java.sql.Types})
|
||||
* @param requiredType the type that the result object is expected to match
|
||||
* @return the result object of the required type, or {@code null} in case of SQL NULL
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not return
|
||||
* exactly one row, or does not return exactly one column in that row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws org.springframework.jdbc.IncorrectResultSetColumnCountException
|
||||
* if the query does not return a row containing a single column
|
||||
* @throws DataAccessException if the query fails
|
||||
* @see #queryForObject(String, Class)
|
||||
* @see java.sql.Types
|
||||
@@ -608,8 +611,10 @@ public interface JdbcOperations {
|
||||
* only the argument value but also the SQL type and optionally the scale
|
||||
* @param requiredType the type that the result object is expected to match
|
||||
* @return the result object of the required type, or {@code null} in case of SQL NULL
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not return
|
||||
* exactly one row, or does not return exactly one column in that row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws org.springframework.jdbc.IncorrectResultSetColumnCountException
|
||||
* if the query does not return a row containing a single column
|
||||
* @throws DataAccessException if the query fails
|
||||
* @see #queryForObject(String, Class)
|
||||
*/
|
||||
@@ -628,8 +633,10 @@ public interface JdbcOperations {
|
||||
* may also contain {@link SqlParameterValue} objects which indicate not
|
||||
* only the argument value but also the SQL type and optionally the scale
|
||||
* @return the result object of the required type, or {@code null} in case of SQL NULL
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not return
|
||||
* exactly one row, or does not return exactly one column in that row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws org.springframework.jdbc.IncorrectResultSetColumnCountException
|
||||
* if the query does not return a row containing a single column
|
||||
* @throws DataAccessException if the query fails
|
||||
* @since 3.0.1
|
||||
* @see #queryForObject(String, Class)
|
||||
@@ -647,8 +654,8 @@ public interface JdbcOperations {
|
||||
* @param argTypes the SQL types of the arguments
|
||||
* (constants from {@code java.sql.Types})
|
||||
* @return the result Map (one entry per column, with column name as key)
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not
|
||||
* return exactly one row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if the query fails
|
||||
* @see #queryForMap(String)
|
||||
* @see ColumnMapRowMapper
|
||||
@@ -671,8 +678,8 @@ public interface JdbcOperations {
|
||||
* only the argument value but also the SQL type and optionally the scale
|
||||
* @return the result Map (one entry for each column, using the
|
||||
* column name as the key)
|
||||
* @throws IncorrectResultSizeDataAccessException if the query does not
|
||||
* return exactly one row
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if the query fails
|
||||
* @see #queryForMap(String)
|
||||
* @see ColumnMapRowMapper
|
||||
|
||||
+10
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -236,8 +236,7 @@ public interface NamedParameterJdbcOperations {
|
||||
* @return the single mapped object (may be {@code null} if the given
|
||||
* {@link RowMapper} returned {@code} null)
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row, or does not return exactly
|
||||
* one column in that row
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if the query fails
|
||||
*/
|
||||
@Nullable
|
||||
@@ -255,8 +254,7 @@ public interface NamedParameterJdbcOperations {
|
||||
* @return the single mapped object (may be {@code null} if the given
|
||||
* {@link RowMapper} returned {@code} null)
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row, or does not return exactly
|
||||
* one column in that row
|
||||
* if the query does not return exactly one row
|
||||
* @throws DataAccessException if the query fails
|
||||
*/
|
||||
@Nullable
|
||||
@@ -273,10 +271,12 @@ public interface NamedParameterJdbcOperations {
|
||||
* @param requiredType the type that the result object is expected to match
|
||||
* @return the result object of the required type, or {@code null} in case of SQL NULL
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row, or does not return exactly
|
||||
* one column in that row
|
||||
* if the query does not return exactly one row
|
||||
* @throws org.springframework.jdbc.IncorrectResultSetColumnCountException
|
||||
* if the query does not return a row containing a single column
|
||||
* @throws DataAccessException if the query fails
|
||||
* @see org.springframework.jdbc.core.JdbcTemplate#queryForObject(String, Class)
|
||||
* @see org.springframework.jdbc.core.SingleColumnRowMapper
|
||||
*/
|
||||
@Nullable
|
||||
<T> T queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType)
|
||||
@@ -293,8 +293,9 @@ public interface NamedParameterJdbcOperations {
|
||||
* @param requiredType the type that the result object is expected to match
|
||||
* @return the result object of the required type, or {@code null} in case of SQL NULL
|
||||
* @throws org.springframework.dao.IncorrectResultSizeDataAccessException
|
||||
* if the query does not return exactly one row, or does not return exactly
|
||||
* one column in that row
|
||||
* if the query does not return exactly one row
|
||||
* @throws org.springframework.jdbc.IncorrectResultSetColumnCountException
|
||||
* if the query does not return a row containing a single column
|
||||
* @throws DataAccessException if the query fails
|
||||
* @see org.springframework.jdbc.core.JdbcTemplate#queryForObject(String, Class)
|
||||
*/
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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,8 +16,8 @@
|
||||
|
||||
package org.springframework.jms.listener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.jms.Connection;
|
||||
@@ -81,9 +81,9 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess
|
||||
|
||||
private boolean active = false;
|
||||
|
||||
private volatile boolean running = false;
|
||||
private volatile boolean running;
|
||||
|
||||
private final List<Object> pausedTasks = new LinkedList<>();
|
||||
private final List<Object> pausedTasks = new ArrayList<>();
|
||||
|
||||
protected final Object lifecycleMonitor = new Object();
|
||||
|
||||
@@ -582,13 +582,13 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess
|
||||
/**
|
||||
* Log a task that has been rejected by {@link #doRescheduleTask}.
|
||||
* <p>The default implementation simply logs a corresponding message
|
||||
* at debug level.
|
||||
* at warn level.
|
||||
* @param task the rejected task object
|
||||
* @param ex the exception thrown from {@link #doRescheduleTask}
|
||||
*/
|
||||
protected void logRejectedTask(Object task, RuntimeException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Listener container task [" + task + "] has been rejected and paused: " + ex);
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Listener container task [" + task + "] has been rejected and paused: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,6 +16,7 @@
|
||||
|
||||
package org.springframework.orm.hibernate5;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -349,7 +350,7 @@ public class LocalSessionFactoryBuilder extends Configuration {
|
||||
Resource[] resources = this.resourcePatternResolver.getResources(pattern);
|
||||
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
|
||||
for (Resource resource : resources) {
|
||||
if (resource.isReadable()) {
|
||||
try {
|
||||
MetadataReader reader = readerFactory.getMetadataReader(resource);
|
||||
String className = reader.getClassMetadata().getClassName();
|
||||
if (matchesEntityTypeFilter(reader, readerFactory)) {
|
||||
@@ -362,6 +363,9 @@ public class LocalSessionFactoryBuilder extends Configuration {
|
||||
packageNames.add(className.substring(0, className.length() - PACKAGE_INFO_SUFFIX.length()));
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
// Ignore non-readable resource
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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,12 +16,13 @@
|
||||
|
||||
package org.springframework.orm.jpa.persistenceunit;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -498,7 +499,7 @@ public class DefaultPersistenceUnitManager
|
||||
* as defined in the JPA specification.
|
||||
*/
|
||||
private List<SpringPersistenceUnitInfo> readPersistenceUnitInfos() {
|
||||
List<SpringPersistenceUnitInfo> infos = new LinkedList<>();
|
||||
List<SpringPersistenceUnitInfo> infos = new ArrayList<>(1);
|
||||
String defaultName = this.defaultPersistenceUnitName;
|
||||
boolean buildDefaultUnit = (this.packagesToScan != null || this.mappingResources != null);
|
||||
boolean foundDefaultUnit = false;
|
||||
@@ -585,7 +586,7 @@ public class DefaultPersistenceUnitManager
|
||||
Resource[] resources = this.resourcePatternResolver.getResources(pattern);
|
||||
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
|
||||
for (Resource resource : resources) {
|
||||
if (resource.isReadable()) {
|
||||
try {
|
||||
MetadataReader reader = readerFactory.getMetadataReader(resource);
|
||||
String className = reader.getClassMetadata().getClassName();
|
||||
if (matchesFilter(reader, readerFactory)) {
|
||||
@@ -602,6 +603,9 @@ public class DefaultPersistenceUnitManager
|
||||
className.substring(0, className.length() - PACKAGE_INFO_SUFFIX.length()));
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
// Ignore non-readable resource
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -393,7 +393,7 @@ public class PersistenceAnnotationBeanPostProcessor
|
||||
}
|
||||
|
||||
|
||||
private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
|
||||
private InjectionMetadata findPersistenceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
|
||||
// Fall back to class name as cache key, for backwards compatibility with custom callers.
|
||||
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
|
||||
// Quick check on the concurrent map first, with minimal locking.
|
||||
@@ -413,7 +413,7 @@ public class PersistenceAnnotationBeanPostProcessor
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private InjectionMetadata buildPersistenceMetadata(final Class<?> clazz) {
|
||||
private InjectionMetadata buildPersistenceMetadata(Class<?> clazz) {
|
||||
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(PersistenceContext.class, PersistenceUnit.class))) {
|
||||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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,7 +47,7 @@ public abstract class TestContextResourceUtils {
|
||||
|
||||
private static final String SLASH = "/";
|
||||
|
||||
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(".*\\$\\{[^}]+}.*");
|
||||
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(".*\\$\\{[^}]+\\}.*");
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -67,7 +67,7 @@ class TimedTransactionalSpringExtensionTests {
|
||||
event(test("WithExceededJUnitJupiterTimeout"),
|
||||
finishedWithFailure(
|
||||
instanceOf(TimeoutException.class),
|
||||
message(msg -> msg.endsWith("timed out after 50 milliseconds")))));
|
||||
message(msg -> msg.endsWith("timed out after 10 milliseconds")))));
|
||||
}
|
||||
|
||||
|
||||
@@ -83,10 +83,10 @@ class TimedTransactionalSpringExtensionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(value = 50, unit = TimeUnit.MILLISECONDS)
|
||||
@Timeout(value = 10, unit = TimeUnit.MILLISECONDS)
|
||||
void transactionalWithExceededJUnitJupiterTimeout() throws Exception {
|
||||
assertThatTransaction().isActive();
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,11 +97,11 @@ class TimedTransactionalSpringExtensionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(value = 50, unit = TimeUnit.MILLISECONDS)
|
||||
@Timeout(value = 10, unit = TimeUnit.MILLISECONDS)
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
void notTransactionalWithExceededJUnitJupiterTimeout() throws Exception {
|
||||
assertThatTransaction().isNotActive();
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -76,14 +76,14 @@ public class TimedSpringRunnerTests {
|
||||
}
|
||||
|
||||
// Should Fail due to timeout.
|
||||
@Test(timeout = 100)
|
||||
@Test(timeout = 10)
|
||||
public void jUnitTimeoutWithSleep() throws Exception {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
// Should Fail due to timeout.
|
||||
@Test
|
||||
@Timed(millis = 100)
|
||||
@Timed(millis = 10)
|
||||
public void springTimeoutWithSleep() throws Exception {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ public class TimedSpringRunnerTests {
|
||||
|
||||
// Should Fail due to timeout.
|
||||
@Test
|
||||
@MetaTimedWithOverride(millis = 100)
|
||||
@MetaTimedWithOverride(millis = 10)
|
||||
public void springTimeoutWithSleepAndMetaAnnotationAndOverride() throws Exception {
|
||||
Thread.sleep(200);
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class TimedSpringRunnerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Timed(millis = 100)
|
||||
@Timed(millis = 10)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private static @interface MetaTimed {
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -38,7 +38,6 @@ import org.springframework.util.Assert;
|
||||
* PersistenceExceptionTranslator} interface, which are subsequently asked to translate
|
||||
* candidate exceptions.
|
||||
*
|
||||
|
||||
* <p>All of Spring's applicable resource factories (e.g.
|
||||
* {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean})
|
||||
* implement the {@code PersistenceExceptionTranslator} interface out of the box.
|
||||
|
||||
+6
-1
@@ -17,6 +17,7 @@
|
||||
package org.springframework.http.server.reactive;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -36,6 +37,7 @@ import org.springframework.util.MultiValueMap;
|
||||
* {@code MultiValueMap} implementation for wrapping Undertow HTTP headers.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sam Brannen
|
||||
* @since 5.1.1
|
||||
*/
|
||||
class UndertowHeadersAdapter implements MultiValueMap<String, String> {
|
||||
@@ -131,7 +133,10 @@ class UndertowHeadersAdapter implements MultiValueMap<String, String> {
|
||||
@Nullable
|
||||
public List<String> remove(Object key) {
|
||||
if (key instanceof String) {
|
||||
this.headers.remove((String) key);
|
||||
Collection<String> removed = this.headers.remove((String) key);
|
||||
if (removed != null) {
|
||||
return new ArrayList<>(removed);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+9
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,14 +16,11 @@
|
||||
|
||||
package org.springframework.web.client;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -126,38 +123,26 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return error message with details from the response body, possibly truncated:
|
||||
* Return error message with details from the response body. For example:
|
||||
* <pre>
|
||||
* 404 Not Found: [{'id': 123, 'message': 'my very long... (500 bytes)]
|
||||
* 404 Not Found: [{'id': 123, 'message': 'my message'}]
|
||||
* </pre>
|
||||
*/
|
||||
private String getErrorMessage(
|
||||
int rawStatusCode, String statusText, @Nullable byte[] responseBody, @Nullable Charset charset) {
|
||||
|
||||
String preface = rawStatusCode + " " + statusText + ": ";
|
||||
|
||||
if (ObjectUtils.isEmpty(responseBody)) {
|
||||
return preface + "[no body]";
|
||||
}
|
||||
|
||||
charset = charset == null ? StandardCharsets.UTF_8 : charset;
|
||||
int maxChars = 200;
|
||||
charset = (charset != null ? charset : StandardCharsets.UTF_8);
|
||||
|
||||
if (responseBody.length < maxChars * 2) {
|
||||
return preface + "[" + new String(responseBody, charset) + "]";
|
||||
}
|
||||
String bodyText = new String(responseBody, charset);
|
||||
bodyText = LogFormatUtils.formatValue(bodyText, -1, true);
|
||||
|
||||
try {
|
||||
Reader reader = new InputStreamReader(new ByteArrayInputStream(responseBody), charset);
|
||||
CharBuffer buffer = CharBuffer.allocate(maxChars);
|
||||
reader.read(buffer);
|
||||
reader.close();
|
||||
buffer.flip();
|
||||
return preface + "[" + buffer.toString() + "... (" + responseBody.length + " bytes)]";
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// should never happen
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
return preface + bodyText;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -20,6 +20,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
@@ -70,7 +71,7 @@ public class ResponseStatusExceptionHandler implements WebExceptionHandler {
|
||||
// Mirrors AbstractHandlerExceptionResolver in spring-webmvc...
|
||||
String logPrefix = exchange.getLogPrefix();
|
||||
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
|
||||
this.warnLogger.warn(logPrefix + formatError(ex, exchange.getRequest()), ex);
|
||||
this.warnLogger.warn(logPrefix + formatError(ex, exchange.getRequest()));
|
||||
}
|
||||
else if (logger.isDebugEnabled()) {
|
||||
logger.debug(logPrefix + formatError(ex, exchange.getRequest()));
|
||||
@@ -81,9 +82,10 @@ public class ResponseStatusExceptionHandler implements WebExceptionHandler {
|
||||
|
||||
|
||||
private String formatError(Throwable ex, ServerHttpRequest request) {
|
||||
String reason = ex.getClass().getSimpleName() + ": " + ex.getMessage();
|
||||
String className = ex.getClass().getSimpleName();
|
||||
String message = LogFormatUtils.formatValue(ex.getMessage(), -1, true);
|
||||
String path = request.getURI().getRawPath();
|
||||
return "Resolved [" + reason + "] for HTTP " + request.getMethod() + " " + path;
|
||||
return "Resolved [" + className + ": " + message + "] for HTTP " + request.getMethod() + " " + path;
|
||||
}
|
||||
|
||||
private boolean updateResponse(ServerHttpResponse response, Throwable ex) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -48,7 +48,7 @@ import org.springframework.util.MultiValueMap;
|
||||
public abstract class UriComponents implements Serializable {
|
||||
|
||||
/** Captures URI template variable names. */
|
||||
private static final Pattern NAMES_PATTERN = Pattern.compile("\\{([^/]+?)}");
|
||||
private static final Pattern NAMES_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
|
||||
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -82,7 +82,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
|
||||
|
||||
private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")";
|
||||
|
||||
private static final String PORT_PATTERN = "(\\d*(?:\\{[^/]+?})?)";
|
||||
private static final String PORT_PATTERN = "(\\d*(?:\\{[^/]+?\\})?)";
|
||||
|
||||
private static final String PATH_PATTERN = "([^?#]*)";
|
||||
|
||||
|
||||
@@ -521,8 +521,8 @@ public class UrlPathHelper {
|
||||
return UriUtils.decode(source, enc);
|
||||
}
|
||||
catch (UnsupportedCharsetException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not decode request string [" + source + "] with encoding '" + enc +
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not decode request string [" + source + "] with encoding '" + enc +
|
||||
"': falling back to platform default encoding; exception message: " + ex.getMessage());
|
||||
}
|
||||
return URLDecoder.decode(source);
|
||||
|
||||
+10
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -83,17 +83,17 @@ class CaptureTheRestPathElement extends PathElement {
|
||||
}
|
||||
|
||||
private String pathToString(int fromSegment, List<Element> pathElements) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = fromSegment, max = pathElements.size(); i < max; i++) {
|
||||
Element element = pathElements.get(i);
|
||||
if (element instanceof PathSegment) {
|
||||
buf.append(((PathSegment)element).valueToMatch());
|
||||
sb.append(((PathSegment)element).valueToMatch());
|
||||
}
|
||||
else {
|
||||
buf.append(element.value());
|
||||
sb.append(element.value());
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,6 +101,11 @@ class CaptureTheRestPathElement extends PathElement {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return ("/{*" + this.variableName + "}").toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWildcardCount() {
|
||||
return 0;
|
||||
@@ -117,8 +122,4 @@ class CaptureTheRestPathElement extends PathElement {
|
||||
return "CaptureTheRest(/{*" + this.variableName + "})";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return ("/{*"+this.variableName+"}").toCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
+15
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -35,7 +35,7 @@ class CaptureVariablePathElement extends PathElement {
|
||||
private final String variableName;
|
||||
|
||||
@Nullable
|
||||
private Pattern constraintPattern;
|
||||
private final Pattern constraintPattern;
|
||||
|
||||
|
||||
/**
|
||||
@@ -55,6 +55,7 @@ class CaptureVariablePathElement extends PathElement {
|
||||
if (colon == -1) {
|
||||
// no constraint
|
||||
this.variableName = new String(captureDescriptor, 1, captureDescriptor.length - 2);
|
||||
this.constraintPattern = null;
|
||||
}
|
||||
else {
|
||||
this.variableName = new String(captureDescriptor, 1, colon - 1);
|
||||
@@ -134,6 +135,18 @@ class CaptureVariablePathElement extends PathElement {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('{');
|
||||
sb.append(this.variableName);
|
||||
if (this.constraintPattern != null) {
|
||||
sb.append(':').append(this.constraintPattern.pattern());
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString().toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWildcardCount() {
|
||||
return 0;
|
||||
@@ -156,16 +169,4 @@ class CaptureVariablePathElement extends PathElement {
|
||||
(this.constraintPattern != null ? ":" + this.constraintPattern.pattern() : "") + "})";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("{");
|
||||
b.append(this.variableName);
|
||||
if (this.constraintPattern != null) {
|
||||
b.append(":").append(this.constraintPattern.pattern());
|
||||
}
|
||||
b.append("}");
|
||||
return b.toString().toCharArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -26,14 +26,15 @@ import org.springframework.web.util.pattern.PathPattern.MatchingContext;
|
||||
* literal path elements 'foo', 'bar' and 'goo'.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 5.0
|
||||
*/
|
||||
class LiteralPathElement extends PathElement {
|
||||
|
||||
private char[] text;
|
||||
private final char[] text;
|
||||
|
||||
private int len;
|
||||
private final int len;
|
||||
|
||||
private boolean caseSensitive;
|
||||
private final boolean caseSensitive;
|
||||
|
||||
|
||||
public LiteralPathElement(int pos, char[] literalText, boolean caseSensitive, char separator) {
|
||||
@@ -69,7 +70,7 @@ class LiteralPathElement extends PathElement {
|
||||
return false;
|
||||
}
|
||||
|
||||
char[] data = ((PathContainer.PathSegment)element).valueToMatchAsChars();
|
||||
char[] data = ((PathContainer.PathSegment) element).valueToMatchAsChars();
|
||||
if (this.caseSensitive) {
|
||||
for (int i = 0; i < this.len; i++) {
|
||||
if (data[i] != this.text[i]) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -332,18 +332,18 @@ public class PathPattern implements Comparable<PathPattern> {
|
||||
PathContainer resultPath = null;
|
||||
if (multipleAdjacentSeparators) {
|
||||
// Need to rebuild the path without the duplicate adjacent separators
|
||||
StringBuilder buf = new StringBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int i = startIndex;
|
||||
while (i < endIndex) {
|
||||
Element e = pathElements.get(i++);
|
||||
buf.append(e.value());
|
||||
sb.append(e.value());
|
||||
if (e instanceof Separator) {
|
||||
while (i < endIndex && (pathElements.get(i) instanceof Separator)) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
resultPath = PathContainer.parsePath(buf.toString(), this.pathOptions);
|
||||
resultPath = PathContainer.parsePath(sb.toString(), this.pathOptions);
|
||||
}
|
||||
else if (startIndex >= endIndex) {
|
||||
resultPath = PathContainer.parsePath("");
|
||||
@@ -487,13 +487,13 @@ public class PathPattern implements Comparable<PathPattern> {
|
||||
* @return the string form of the pattern
|
||||
*/
|
||||
String computePatternString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
PathElement pe = this.head;
|
||||
while (pe != null) {
|
||||
buf.append(pe.getChars());
|
||||
sb.append(pe.getChars());
|
||||
pe = pe.next;
|
||||
}
|
||||
return buf.toString();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2021 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,14 +66,14 @@ public class PatternParseException extends IllegalArgumentException {
|
||||
* with a pointer to the error position, as well as the error message.
|
||||
*/
|
||||
public String toDetailedString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append(this.pattern).append('\n');
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.pattern).append('\n');
|
||||
for (int i = 0; i < this.position; i++) {
|
||||
buf.append(' ');
|
||||
sb.append(' ');
|
||||
}
|
||||
buf.append("^\n");
|
||||
buf.append(getMessage());
|
||||
return buf.toString();
|
||||
sb.append("^\n");
|
||||
sb.append(getMessage());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
|
||||
+18
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -35,7 +35,7 @@ import org.springframework.web.util.pattern.PathPattern.MatchingContext;
|
||||
*/
|
||||
class RegexPathElement extends PathElement {
|
||||
|
||||
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?}|[^/{}]|\\\\[{}])+?)}");
|
||||
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
|
||||
|
||||
private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
|
||||
|
||||
@@ -136,20 +136,19 @@ class RegexPathElement extends PathElement {
|
||||
if (matches) {
|
||||
if (isNoMorePattern()) {
|
||||
if (matchingContext.determineRemainingPath &&
|
||||
(this.variableNames.isEmpty() || textToMatch.length() > 0)) {
|
||||
(this.variableNames.isEmpty() || textToMatch.length() > 0)) {
|
||||
matchingContext.remainingPathIndex = pathIndex + 1;
|
||||
matches = true;
|
||||
}
|
||||
else {
|
||||
// No more pattern, is there more data?
|
||||
// If pattern is capturing variables there must be some actual data to bind to them
|
||||
matches = (pathIndex + 1) >= matchingContext.pathLength
|
||||
&& (this.variableNames.isEmpty() || textToMatch.length() > 0);
|
||||
matches = (pathIndex + 1 >= matchingContext.pathLength) &&
|
||||
(this.variableNames.isEmpty() || textToMatch.length() > 0);
|
||||
if (!matches && matchingContext.isMatchOptionalTrailingSeparator()) {
|
||||
matches = (this.variableNames.isEmpty()
|
||||
|| textToMatch.length() > 0)
|
||||
&& (pathIndex + 2) >= matchingContext.pathLength
|
||||
&& matchingContext.isSeparator(pathIndex + 1);
|
||||
matches = (this.variableNames.isEmpty() || textToMatch.length() > 0) &&
|
||||
(pathIndex + 2 >= matchingContext.pathLength) &&
|
||||
matchingContext.isSeparator(pathIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,11 +159,11 @@ class RegexPathElement extends PathElement {
|
||||
|
||||
if (matches && matchingContext.extractingVariables) {
|
||||
// Process captures
|
||||
if (this.variableNames.size() != matcher.groupCount()) { // SPR-8455
|
||||
throw new IllegalArgumentException("The number of capturing groups in the pattern segment "
|
||||
+ this.pattern + " does not match the number of URI template variables it defines, "
|
||||
+ "which can occur if capturing groups are used in a URI template regex. "
|
||||
+ "Use non-capturing groups instead.");
|
||||
if (this.variableNames.size() != matcher.groupCount()) { // SPR-8455
|
||||
throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
|
||||
this.pattern + " does not match the number of URI template variables it defines, " +
|
||||
"which can occur if capturing groups are used in a URI template regex. " +
|
||||
"Use non-capturing groups instead.");
|
||||
}
|
||||
for (int i = 1; i <= matcher.groupCount(); i++) {
|
||||
String name = this.variableNames.get(i - 1);
|
||||
@@ -187,6 +186,11 @@ class RegexPathElement extends PathElement {
|
||||
return (this.regex.length - varsLength - this.variableNames.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return this.regex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCaptureCount() {
|
||||
return this.variableNames.size();
|
||||
@@ -208,8 +212,4 @@ class RegexPathElement extends PathElement {
|
||||
return "Regex(" + String.valueOf(this.regex) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return this.regex;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -62,14 +62,15 @@ class SeparatorPathElement extends PathElement {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Separator(" + this.separator + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return new char[] {this.separator};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Separator(" + this.separator + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -74,7 +74,7 @@ class SingleCharWildcardedPathElement extends PathElement {
|
||||
return false;
|
||||
}
|
||||
|
||||
char[] data = ((PathSegment)element).valueToMatchAsChars();
|
||||
char[] data = ((PathSegment) element).valueToMatchAsChars();
|
||||
if (this.caseSensitive) {
|
||||
for (int i = 0; i < this.len; i++) {
|
||||
char ch = this.text[i];
|
||||
@@ -125,15 +125,15 @@ class SingleCharWildcardedPathElement extends PathElement {
|
||||
return this.len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SingleCharWildcarded(" + String.valueOf(this.text) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -86,6 +86,11 @@ class WildcardPathElement extends PathElement {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return new char[] {'*'};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWildcardCount() {
|
||||
return 1;
|
||||
@@ -102,8 +107,4 @@ class WildcardPathElement extends PathElement {
|
||||
return "Wildcard(*)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return new char[] {'*'};
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2021 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,11 @@ class WildcardTheRestPathElement extends PathElement {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return (this.separator + "**").toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWildcardCount() {
|
||||
return 1;
|
||||
@@ -58,8 +63,4 @@ class WildcardTheRestPathElement extends PathElement {
|
||||
return "WildcardTheRest(" + this.separator + "**)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getChars() {
|
||||
return (this.separator+"**").toCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,10 +19,8 @@ package org.springframework.web.client;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -75,25 +73,7 @@ public class DefaultResponseErrorHandlerTests {
|
||||
assertThatExceptionOfType(HttpClientErrorException.class)
|
||||
.isThrownBy(() -> handler.handleError(response))
|
||||
.satisfies(ex -> assertThat(ex.getResponseHeaders()).isSameAs(headers))
|
||||
.satisfies(ex -> assertThat(ex.getMessage()).isEqualTo("404 Not Found: [Hello World]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleErrorWithLongBody() throws Exception {
|
||||
|
||||
Function<Integer, String> bodyGenerator =
|
||||
size -> Flux.just("a").repeat(size-1).reduce((s, s2) -> s + s2).block();
|
||||
|
||||
given(response.getRawStatusCode()).willReturn(HttpStatus.NOT_FOUND.value());
|
||||
given(response.getStatusText()).willReturn("Not Found");
|
||||
given(response.getHeaders()).willReturn(new HttpHeaders());
|
||||
given(response.getBody()).willReturn(
|
||||
new ByteArrayInputStream(bodyGenerator.apply(500).getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
assertThatExceptionOfType(HttpClientErrorException.class)
|
||||
.isThrownBy(() -> handler.handleError(response))
|
||||
.satisfies(ex -> assertThat(ex.getMessage()).isEqualTo(
|
||||
"404 Not Found: [" + bodyGenerator.apply(200) + "... (500 bytes)]"));
|
||||
.withMessage("404 Not Found: \"Hello World\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-7
@@ -187,13 +187,7 @@ class DefaultClientResponse implements ClientResponse {
|
||||
|
||||
@Override
|
||||
public Mono<WebClientResponseException> createException() {
|
||||
return DataBufferUtils.join(body(BodyExtractors.toDataBuffers()))
|
||||
.map(dataBuffer -> {
|
||||
byte[] bytes = new byte[dataBuffer.readableByteCount()];
|
||||
dataBuffer.read(bytes);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
return bytes;
|
||||
})
|
||||
return bodyToMono(byte[].class)
|
||||
.defaultIfEmpty(new byte[0])
|
||||
.map(bodyBytes -> {
|
||||
HttpRequest request = this.requestSupplier.get();
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,7 +72,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Mono<Resourc
|
||||
|
||||
try {
|
||||
Resource resource = this.location.createRelative(path);
|
||||
if (resource.exists() && resource.isReadable() && isResourceUnderLocation(resource)) {
|
||||
if (resource.isReadable() && isResourceUnderLocation(resource)) {
|
||||
return Mono.just(resource);
|
||||
}
|
||||
else {
|
||||
|
||||
+9
-6
@@ -29,6 +29,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -119,11 +120,12 @@ public class PathResourceResolver extends AbstractResourceResolver {
|
||||
return Mono.just(resource);
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
Resource[] allowedLocations = getAllowedLocations();
|
||||
logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " +
|
||||
"but resource \"" + resource.getURL() + "\" is neither under the " +
|
||||
"current location \"" + location.getURL() + "\" nor under any of the " +
|
||||
"allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]"));
|
||||
Resource[] allowed = getAllowedLocations();
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Resource path \"" + resourcePath + "\" was successfully resolved " +
|
||||
"but resource \"" + resource.getURL() + "\" is neither under the " +
|
||||
"current location \"" + location.getURL() + "\" nor under any of the " +
|
||||
"allowed locations " + (allowed != null ? Arrays.asList(allowed) : "[]"), -1, true));
|
||||
}
|
||||
}
|
||||
return Mono.empty();
|
||||
@@ -199,7 +201,8 @@ public class PathResourceResolver extends AbstractResourceResolver {
|
||||
try {
|
||||
String decodedPath = URLDecoder.decode(resourcePath, "UTF-8");
|
||||
if (decodedPath.contains("../") || decodedPath.contains("..\\")) {
|
||||
logger.warn("Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath);
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath, -1, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -36,6 +36,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Hints;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -490,7 +491,8 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||
protected boolean isInvalidPath(String path) {
|
||||
if (path.contains("WEB-INF") || path.contains("META-INF")) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]");
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Path with \"WEB-INF\" or \"META-INF\": [" + path + "]", -1, true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -498,14 +500,16 @@ public class ResourceWebHandler implements WebHandler, InitializingBean {
|
||||
String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
|
||||
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Path represents URL or has \"url:\" prefix: [" + path + "]");
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Path represents URL or has \"url:\" prefix: [" + path + "]", -1, true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]");
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -53,7 +53,7 @@ import org.springframework.web.util.UriUtils;
|
||||
*/
|
||||
public class RedirectView extends AbstractUrlBasedView {
|
||||
|
||||
private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)}");
|
||||
private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
|
||||
|
||||
|
||||
private HttpStatus statusCode = HttpStatus.SEE_OTHER;
|
||||
|
||||
+29
@@ -30,6 +30,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.codec.ByteArrayDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBuffer;
|
||||
@@ -48,6 +49,7 @@ import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.web.reactive.function.BodyExtractors.toMono;
|
||||
@@ -340,6 +342,33 @@ public class DefaultClientResponseTests {
|
||||
assertThat(result.getHeaders().getContentType()).isEqualTo(MediaType.TEXT_PLAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createException() {
|
||||
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
|
||||
byte[] bytes = "foo".getBytes(StandardCharsets.UTF_8);
|
||||
DefaultDataBuffer dataBuffer = factory.wrap(ByteBuffer.wrap(bytes));
|
||||
Flux<DataBuffer> body = Flux.just(dataBuffer);
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
|
||||
given(mockResponse.getHeaders()).willReturn(httpHeaders);
|
||||
given(mockResponse.getStatusCode()).willReturn(HttpStatus.NOT_FOUND);
|
||||
given(mockResponse.getRawStatusCode()).willReturn(HttpStatus.NOT_FOUND.value());
|
||||
given(mockResponse.getBody()).willReturn(body);
|
||||
|
||||
List<HttpMessageReader<?>> messageReaders = Collections.singletonList(
|
||||
new DecoderHttpMessageReader<>(new ByteArrayDecoder()));
|
||||
given(mockExchangeStrategies.messageReaders()).willReturn(messageReaders);
|
||||
|
||||
Mono<WebClientResponseException> resultMono = defaultClientResponse.createException();
|
||||
WebClientResponseException exception = resultMono.block();
|
||||
assertThat(exception.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
assertThat(exception.getMessage()).isEqualTo("404 Not Found");
|
||||
assertThat(exception.getHeaders()).containsExactly(entry("Content-Type",
|
||||
Collections.singletonList("text/plain")));
|
||||
assertThat(exception.getResponseBodyAsByteArray()).isEqualTo(bytes);
|
||||
}
|
||||
|
||||
|
||||
private void mockTextPlainResponse(Flux<DataBuffer> body) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
|
||||
+1
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -61,9 +61,6 @@ public class FreeMarkerConfigurerBeanDefinitionParser extends AbstractSingleBean
|
||||
for (Element childElement : childElements) {
|
||||
locations.add(childElement.getAttribute("location"));
|
||||
}
|
||||
if (locations.isEmpty()) {
|
||||
locations.add("/WEB-INF/");
|
||||
}
|
||||
builder.addPropertyValue("templateLoaderPaths", StringUtils.toStringArray(locations));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -71,7 +71,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
|
||||
|
||||
try {
|
||||
Resource resource = this.location.createRelative(path);
|
||||
if (resource.exists() && resource.isReadable() && isResourceUnderLocation(resource)) {
|
||||
if (resource.isReadable() && isResourceUnderLocation(resource)) {
|
||||
return Optional.of(resource);
|
||||
}
|
||||
else {
|
||||
@@ -110,10 +110,7 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return path.contains("..") && StringUtils.cleanPath(path).contains("../");
|
||||
}
|
||||
|
||||
private boolean isResourceUnderLocation(Resource resource) throws IOException {
|
||||
@@ -144,10 +141,8 @@ class PathResourceLookupFunction implements Function<ServerRequest, Optional<Res
|
||||
if (!resourcePath.startsWith(locationPath)) {
|
||||
return false;
|
||||
}
|
||||
if (resourcePath.contains("%") && StringUtils.uriDecode(resourcePath, StandardCharsets.UTF_8).contains("../")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return !resourcePath.contains("%") ||
|
||||
!StringUtils.uriDecode(resourcePath, StandardCharsets.UTF_8).contains("../");
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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,6 +25,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
@@ -142,7 +143,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
|
||||
if (result != null) {
|
||||
// Print debug message when warn logger is not enabled.
|
||||
if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
|
||||
logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
|
||||
logger.debug(buildLogMessage(ex, request) + (result.isEmpty() ? "" : " to " + result));
|
||||
}
|
||||
// Explicitly configured warn logger in logException method.
|
||||
logException(ex, request);
|
||||
@@ -207,7 +208,7 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
|
||||
* @return the log message to use
|
||||
*/
|
||||
protected String buildLogMessage(Exception ex, HttpServletRequest request) {
|
||||
return "Resolved [" + ex + "]";
|
||||
return "Resolved [" + LogFormatUtils.formatValue(ex, -1, true) + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -67,9 +67,8 @@ public class ExtendedServletRequestDataBinder extends ServletRequestDataBinder {
|
||||
if (uriVars != null) {
|
||||
uriVars.forEach((name, value) -> {
|
||||
if (mpvs.contains(name)) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Skipping URI variable '" + name +
|
||||
"' because request contains bind value with same name.");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("URI variable '" + name + "' overridden by request bind value.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -193,7 +193,7 @@ class ReactiveTypeHandler {
|
||||
"-------------------------------\n" +
|
||||
"Controller:\t" + returnType.getContainingClass().getName() + "\n" +
|
||||
"Method:\t\t" + returnType.getMethod().getName() + "\n" +
|
||||
"Returning:\t" + ResolvableType.forMethodParameter(returnType).toString() + "\n" +
|
||||
"Returning:\t" + ResolvableType.forMethodParameter(returnType) + "\n" +
|
||||
"!!!");
|
||||
this.taskExecutorWarning = false;
|
||||
}
|
||||
|
||||
+9
-6
@@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.support.ServletContextResource;
|
||||
@@ -188,11 +189,12 @@ public class PathResourceResolver extends AbstractResourceResolver {
|
||||
return resource;
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
Resource[] allowedLocations = getAllowedLocations();
|
||||
logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " +
|
||||
"but resource \"" + resource.getURL() + "\" is neither under the " +
|
||||
"current location \"" + location.getURL() + "\" nor under any of the " +
|
||||
"allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]"));
|
||||
Resource[] allowed = getAllowedLocations();
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Resource path \"" + resourcePath + "\" was successfully resolved " +
|
||||
"but resource \"" + resource.getURL() + "\" is neither under " +
|
||||
"the current location \"" + location.getURL() + "\" nor under any of " +
|
||||
"the allowed locations " + (allowed != null ? Arrays.asList(allowed) : "[]"), -1, true));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -285,7 +287,8 @@ public class PathResourceResolver extends AbstractResourceResolver {
|
||||
try {
|
||||
String decodedPath = URLDecoder.decode(resourcePath, "UTF-8");
|
||||
if (decodedPath.contains("../") || decodedPath.contains("..\\")) {
|
||||
logger.warn("Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath);
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath, -1, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -40,6 +40,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
@@ -662,7 +663,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
protected boolean isInvalidPath(String path) {
|
||||
if (path.contains("WEB-INF") || path.contains("META-INF")) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]");
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Path with \"WEB-INF\" or \"META-INF\": [" + path + "]", -1, true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -670,14 +672,16 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
|
||||
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Path represents URL or has \"url:\" prefix: [" + path + "]");
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Path represents URL or has \"url:\" prefix: [" + path + "]", -1, true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]");
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]", -1, true));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+14
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -65,7 +65,7 @@ public class ResourceHandlerRegistryTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
public void setup() {
|
||||
GenericWebApplicationContext appContext = new GenericWebApplicationContext();
|
||||
appContext.refresh();
|
||||
|
||||
@@ -77,8 +77,14 @@ public class ResourceHandlerRegistryTests {
|
||||
this.response = new MockHttpServletResponse();
|
||||
}
|
||||
|
||||
private ResourceHttpRequestHandler getHandler(String pathPattern) {
|
||||
SimpleUrlHandlerMapping hm = (SimpleUrlHandlerMapping) this.registry.getHandlerMapping();
|
||||
return (ResourceHttpRequestHandler) hm.getUrlMap().get(pathPattern);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void noResourceHandlers() throws Exception {
|
||||
public void noResourceHandlers() {
|
||||
this.registry = new ResourceHandlerRegistry(new GenericWebApplicationContext(), new MockServletContext());
|
||||
assertThat((Object) this.registry.getHandlerMapping()).isNull();
|
||||
}
|
||||
@@ -127,7 +133,7 @@ public class ResourceHandlerRegistryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceChain() throws Exception {
|
||||
public void resourceChain() {
|
||||
ResourceResolver mockResolver = Mockito.mock(ResourceResolver.class);
|
||||
ResourceTransformer mockTransformer = Mockito.mock(ResourceTransformer.class);
|
||||
this.registration.resourceChain(true).addResolver(mockResolver).addTransformer(mockTransformer);
|
||||
@@ -149,7 +155,7 @@ public class ResourceHandlerRegistryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceChainWithoutCaching() throws Exception {
|
||||
public void resourceChainWithoutCaching() {
|
||||
this.registration.resourceChain(false);
|
||||
|
||||
ResourceHttpRequestHandler handler = getHandler("/resources/**");
|
||||
@@ -163,7 +169,7 @@ public class ResourceHandlerRegistryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceChainWithVersionResolver() throws Exception {
|
||||
public void resourceChainWithVersionResolver() {
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver()
|
||||
.addFixedVersionStrategy("fixed", "/**/*.js")
|
||||
.addContentVersionStrategy("/**");
|
||||
@@ -187,7 +193,7 @@ public class ResourceHandlerRegistryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceChainWithOverrides() throws Exception {
|
||||
public void resourceChainWithOverrides() {
|
||||
CachingResourceResolver cachingResolver = Mockito.mock(CachingResourceResolver.class);
|
||||
VersionResourceResolver versionResolver = Mockito.mock(VersionResourceResolver.class);
|
||||
WebJarsResourceResolver webjarsResolver = Mockito.mock(WebJarsResourceResolver.class);
|
||||
@@ -222,7 +228,7 @@ public class ResourceHandlerRegistryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void urlResourceWithCharset() throws Exception {
|
||||
public void urlResourceWithCharset() {
|
||||
this.registration.addResourceLocations("[charset=ISO-8859-1]file:///tmp");
|
||||
this.registration.resourceChain(true);
|
||||
|
||||
@@ -238,9 +244,4 @@ public class ResourceHandlerRegistryTests {
|
||||
assertThat(locationCharsets.values().iterator().next()).isEqualTo(StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
private ResourceHttpRequestHandler getHandler(String pathPattern) {
|
||||
SimpleUrlHandlerMapping hm = (SimpleUrlHandlerMapping) this.registry.getHandlerMapping();
|
||||
return (ResourceHttpRequestHandler) hm.getUrlMap().get(pathPattern);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -286,7 +286,6 @@ public class ResourceHttpRequestHandlerTests {
|
||||
|
||||
@Test // SPR-14368
|
||||
public void getResourceWithMediaTypeResolvedThroughServletContext() throws Exception {
|
||||
|
||||
MockServletContext servletContext = new MockServletContext() {
|
||||
@Override
|
||||
public String getMimeType(String filePath) {
|
||||
@@ -310,7 +309,6 @@ public class ResourceHttpRequestHandlerTests {
|
||||
|
||||
@Test
|
||||
public void testInvalidPath() throws Exception {
|
||||
|
||||
// Use mock ResourceResolver: i.e. we're only testing upfront validations...
|
||||
|
||||
Resource resource = mock(Resource.class);
|
||||
@@ -656,7 +654,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
assertThat(ranges[11]).isEqualTo("t.");
|
||||
}
|
||||
|
||||
@Test // gh-25976
|
||||
@Test // gh-25976
|
||||
public void partialContentByteRangeWithEncodedResource(GzipSupport.GzippedFiles gzippedFiles) throws Exception {
|
||||
String path = "js/foo.js";
|
||||
gzippedFiles.create(path);
|
||||
@@ -685,7 +683,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
assertThat(this.response.getHeaderValues("Vary")).containsExactly("Accept-Encoding");
|
||||
}
|
||||
|
||||
@Test // gh-25976
|
||||
@Test // gh-25976
|
||||
public void partialContentWithHttpHead() throws Exception {
|
||||
this.request.setMethod("HEAD");
|
||||
this.request.addHeader("Range", "bytes=0-1");
|
||||
|
||||
+8
-4
@@ -29,6 +29,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
@@ -293,7 +294,8 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life
|
||||
|
||||
protected void handleInvalidUpgradeHeader(ServerHttpRequest request, ServerHttpResponse response) throws IOException {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Handshake failed due to invalid Upgrade header: " + request.getHeaders().getUpgrade());
|
||||
logger.error(LogFormatUtils.formatValue(
|
||||
"Handshake failed due to invalid Upgrade header: " + request.getHeaders().getUpgrade(), -1, true));
|
||||
}
|
||||
response.setStatusCode(HttpStatus.BAD_REQUEST);
|
||||
response.getBody().write("Can \"Upgrade\" only to \"WebSocket\".".getBytes(StandardCharsets.UTF_8));
|
||||
@@ -301,7 +303,8 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life
|
||||
|
||||
protected void handleInvalidConnectHeader(ServerHttpRequest request, ServerHttpResponse response) throws IOException {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Handshake failed due to invalid Connection header " + request.getHeaders().getConnection());
|
||||
logger.error(LogFormatUtils.formatValue(
|
||||
"Handshake failed due to invalid Connection header" + request.getHeaders().getConnection(), -1, true));
|
||||
}
|
||||
response.setStatusCode(HttpStatus.BAD_REQUEST);
|
||||
response.getBody().write("\"Connection\" must be \"upgrade\".".getBytes(StandardCharsets.UTF_8));
|
||||
@@ -325,8 +328,9 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life
|
||||
protected void handleWebSocketVersionNotSupported(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
String version = request.getHeaders().getFirst("Sec-WebSocket-Version");
|
||||
logger.error("Handshake failed due to unsupported WebSocket version: " + version +
|
||||
". Supported versions: " + Arrays.toString(getSupportedVersions()));
|
||||
logger.error(LogFormatUtils.formatValue(
|
||||
"Handshake failed due to unsupported WebSocket version: " + version +
|
||||
". Supported versions: " + Arrays.toString(getSupportedVersions()), -1, true));
|
||||
}
|
||||
response.setStatusCode(HttpStatus.UPGRADE_REQUIRED);
|
||||
response.getHeaders().set(WebSocketHttpHeaders.SEC_WEBSOCKET_VERSION,
|
||||
|
||||
+5
-2
@@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -340,7 +341,8 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig
|
||||
|
||||
if (sockJsPath == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Expected SockJS path. Failing request: " + request.getURI());
|
||||
logger.warn(LogFormatUtils.formatValue(
|
||||
"Expected SockJS path. Failing request: " + request.getURI(), -1, true));
|
||||
}
|
||||
response.setStatusCode(HttpStatus.NOT_FOUND);
|
||||
return;
|
||||
@@ -405,7 +407,8 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig
|
||||
String[] pathSegments = StringUtils.tokenizeToStringArray(sockJsPath.substring(1), "/");
|
||||
if (pathSegments.length != 3) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Invalid SockJS path '" + sockJsPath + "' - required to have 3 path segments");
|
||||
logger.warn(LogFormatUtils.formatValue("Invalid SockJS path '" + sockJsPath + "' - " +
|
||||
"required to have 3 path segments", -1, true));
|
||||
}
|
||||
if (requestInfo != null) {
|
||||
logger.debug("Ignoring transport request: " + requestInfo);
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
@@ -234,7 +235,7 @@ public class TransportHandlingSockJsService extends AbstractSockJsService implem
|
||||
TransportType transportType = TransportType.fromValue(transport);
|
||||
if (transportType == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Unknown transport type for " + request.getURI());
|
||||
logger.warn(LogFormatUtils.formatValue("Unknown transport type for " + request.getURI(), -1, true));
|
||||
}
|
||||
response.setStatusCode(HttpStatus.NOT_FOUND);
|
||||
return;
|
||||
@@ -243,7 +244,7 @@ public class TransportHandlingSockJsService extends AbstractSockJsService implem
|
||||
TransportHandler transportHandler = this.handlers.get(transportType);
|
||||
if (transportHandler == null) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("No TransportHandler for " + request.getURI());
|
||||
logger.warn(LogFormatUtils.formatValue("No TransportHandler for " + request.getURI(), -1, true));
|
||||
}
|
||||
response.setStatusCode(HttpStatus.NOT_FOUND);
|
||||
return;
|
||||
|
||||
@@ -1024,7 +1024,7 @@ by type without help. Consider the following class:
|
||||
|
||||
class ExampleBean(
|
||||
private val years: Int, // Number of years to calculate the Ultimate Answer
|
||||
private val ultimateAnswer: String// The Answer to Life, the Universe, and Everything
|
||||
private val ultimateAnswer: String // The Answer to Life, the Universe, and Everything
|
||||
)
|
||||
----
|
||||
|
||||
@@ -1608,7 +1608,7 @@ listings shows how to use the `parent` attribute:
|
||||
----
|
||||
<!-- in the parent context -->
|
||||
<bean id="accountService" class="com.something.SimpleAccountService">
|
||||
<!-- insert dependencies as required as here -->
|
||||
<!-- insert dependencies as required here -->
|
||||
</bean>
|
||||
----
|
||||
|
||||
@@ -1690,7 +1690,7 @@ respectively. The following example shows how to use them:
|
||||
<property name="someMap">
|
||||
<map>
|
||||
<entry key="an entry" value="just some string"/>
|
||||
<entry key ="a ref" value-ref="myDataSource"/>
|
||||
<entry key="a ref" value-ref="myDataSource"/>
|
||||
</map>
|
||||
</property>
|
||||
<!-- results in a setSomeSet(java.util.Set) call -->
|
||||
@@ -1834,7 +1834,7 @@ class SomeClass {
|
||||
When the `accounts` property of the `something` bean is prepared for injection, the generics
|
||||
information about the element type of the strongly-typed `Map<String, Float>` is
|
||||
available by reflection. Thus, Spring's type conversion infrastructure recognizes the
|
||||
various value elements as being of type `Float`, and the string values (`9.99, 2.75`, and
|
||||
various value elements as being of type `Float`, and the string values (`9.99`, `2.75`, and
|
||||
`3.99`) are converted into an actual `Float` type.
|
||||
|
||||
|
||||
@@ -2028,7 +2028,7 @@ not commonly used since the plain order of declaration is usually sufficient the
|
||||
In practice, the constructor resolution
|
||||
<<beans-factory-ctor-arguments-resolution,mechanism>> is quite efficient in matching
|
||||
arguments, so unless you really need to, we recommend using the name notation
|
||||
through-out your configuration.
|
||||
throughout your configuration.
|
||||
|
||||
|
||||
[[beans-compound-property-names]]
|
||||
@@ -2503,13 +2503,13 @@ declared return type of the lookup method:
|
||||
public abstract class CommandManager {
|
||||
|
||||
public Object process(Object commandState) {
|
||||
MyCommand command = createCommand();
|
||||
Command command = createCommand();
|
||||
command.setState(commandState);
|
||||
return command.execute();
|
||||
}
|
||||
|
||||
@Lookup
|
||||
protected abstract MyCommand createCommand();
|
||||
protected abstract Command createCommand();
|
||||
}
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -2604,9 +2604,9 @@ interface provides the new method definition, as the following example shows:
|
||||
.Kotlin
|
||||
----
|
||||
/**
|
||||
* meant to be used to override the existing computeValue(String)
|
||||
* implementation in MyValueCalculator
|
||||
*/
|
||||
* meant to be used to override the existing computeValue(String)
|
||||
* implementation in MyValueCalculator
|
||||
*/
|
||||
class ReplacementComputeValue : MethodReplacer {
|
||||
|
||||
override fun reimplement(obj: Any, method: Method, args: Array<out Any>): Any {
|
||||
@@ -2806,7 +2806,7 @@ prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a
|
||||
prototype-scoped bean into your singleton bean, because that injection occurs only
|
||||
once, when the Spring container instantiates the singleton bean and resolves
|
||||
and injects its dependencies. If you need a new instance of a prototype bean at
|
||||
runtime more than once, see <<beans-factory-method-injection>>
|
||||
runtime more than once, see <<beans-factory-method-injection>>.
|
||||
|
||||
|
||||
|
||||
@@ -2984,7 +2984,7 @@ The Spring container creates a new instance of the `AppPreferences` bean by usin
|
||||
`appPreferences` bean is scoped at the `ServletContext` level and stored as a regular
|
||||
`ServletContext` attribute. This is somewhat similar to a Spring singleton bean but
|
||||
differs in two important ways: It is a singleton per `ServletContext`, not per Spring
|
||||
'ApplicationContext' (for which there may be several in any given web application),
|
||||
`ApplicationContext` (for which there may be several in any given web application),
|
||||
and it is actually exposed and therefore visible as a `ServletContext` attribute.
|
||||
|
||||
When using annotation-driven components or Java configuration, you can use the
|
||||
@@ -3039,7 +3039,7 @@ constructor or setter argument or autowired field) as `ObjectFactory<MyTargetBea
|
||||
allowing for a `getObject()` call to retrieve the current instance on demand every
|
||||
time it is needed -- without holding on to the instance or storing it separately.
|
||||
|
||||
As an extended variant, you may declare `ObjectProvider<MyTargetBean>`, which delivers
|
||||
As an extended variant, you may declare `ObjectProvider<MyTargetBean>` which delivers
|
||||
several additional access variants, including `getIfAvailable` and `getIfUnique`.
|
||||
|
||||
The JSR-330 variant of this is called `Provider` and is used with a `Provider<MyTargetBean>`
|
||||
@@ -3100,7 +3100,7 @@ to the HTTP `Session`-scoped bean (`userPreferences`). The salient point here is
|
||||
`userManager` bean is a singleton: it is instantiated exactly once per
|
||||
container, and its dependencies (in this case only one, the `userPreferences` bean) are
|
||||
also injected only once. This means that the `userManager` bean operates only on the
|
||||
exact same `userPreferences` object (that is, the one with which it was originally injected.
|
||||
exact same `userPreferences` object (that is, the one with which it was originally injected).
|
||||
|
||||
This is not the behavior you want when injecting a shorter-lived scoped bean into a
|
||||
longer-lived scoped bean (for example, injecting an HTTP `Session`-scoped collaborating
|
||||
@@ -3350,8 +3350,9 @@ of the scope. You can also do the `Scope` registration declaratively, by using t
|
||||
</beans>
|
||||
----
|
||||
|
||||
NOTE: When you place `<aop:scoped-proxy/>` in a `FactoryBean` implementation, it is the factory
|
||||
bean itself that is scoped, not the object returned from `getObject()`.
|
||||
NOTE: When you place `<aop:scoped-proxy/>` within a `<bean>` declaration for a
|
||||
`FactoryBean` implementation, it is the factory bean itself that is scoped, not the object
|
||||
returned from `getObject()`.
|
||||
|
||||
|
||||
|
||||
@@ -3927,7 +3928,7 @@ shows the definition of the BeanNameAware interface:
|
||||
----
|
||||
|
||||
The callback is invoked after population of normal bean properties but before an
|
||||
initialization callback such as `InitializingBean`, `afterPropertiesSet`, or a custom
|
||||
initialization callback such as `InitializingBean.afterPropertiesSet()` or a custom
|
||||
init-method.
|
||||
|
||||
|
||||
@@ -3959,7 +3960,7 @@ dependency type. The following table summarizes the most important `Aware` inter
|
||||
|
||||
| `BeanFactoryAware`
|
||||
| Declaring `BeanFactory`.
|
||||
| <<beans-factory-aware>>
|
||||
| <<beans-beanfactory>>
|
||||
|
||||
| `BeanNameAware`
|
||||
| Name of the declaring bean.
|
||||
@@ -4146,7 +4147,7 @@ or it may wrap a bean with a proxy. Some Spring AOP infrastructure classes are
|
||||
implemented as bean post-processors in order to provide proxy-wrapping logic.
|
||||
|
||||
An `ApplicationContext` automatically detects any beans that are defined in the
|
||||
configuration metadata that implements the `BeanPostProcessor` interface. The
|
||||
configuration metadata that implement the `BeanPostProcessor` interface. The
|
||||
`ApplicationContext` registers these beans as post-processors so that they can be called
|
||||
later, upon bean creation. Bean post-processors can be deployed in the container in the
|
||||
same fashion as any other beans.
|
||||
@@ -4544,22 +4545,22 @@ Java as opposed to a (potentially) verbose amount of XML, you can create your ow
|
||||
`FactoryBean`, write the complex initialization inside that class, and then plug your
|
||||
custom `FactoryBean` into the container.
|
||||
|
||||
The `FactoryBean` interface provides three methods:
|
||||
The `FactoryBean<T>` interface provides three methods:
|
||||
|
||||
* `Object getObject()`: Returns an instance of the object this factory creates. The
|
||||
* `T getObject()`: Returns an instance of the object this factory creates. The
|
||||
instance can possibly be shared, depending on whether this factory returns singletons
|
||||
or prototypes.
|
||||
* `boolean isSingleton()`: Returns `true` if this `FactoryBean` returns singletons or
|
||||
`false` otherwise.
|
||||
* `Class getObjectType()`: Returns the object type returned by the `getObject()` method
|
||||
`false` otherwise. The default implementation of this method returns `true`.
|
||||
* `Class<?> getObjectType()`: Returns the object type returned by the `getObject()` method
|
||||
or `null` if the type is not known in advance.
|
||||
|
||||
The `FactoryBean` concept and interface is used in a number of places within the Spring
|
||||
The `FactoryBean` concept and interface are used in a number of places within the Spring
|
||||
Framework. More than 50 implementations of the `FactoryBean` interface ship with Spring
|
||||
itself.
|
||||
|
||||
When you need to ask a container for an actual `FactoryBean` instance itself instead of
|
||||
the bean it produces, preface the bean's `id` with the ampersand symbol (`&`) when
|
||||
the bean it produces, prefix the bean's `id` with the ampersand symbol (`&`) when
|
||||
calling the `getBean()` method of the `ApplicationContext`. So, for a given `FactoryBean`
|
||||
with an `id` of `myBean`, invoking `getBean("myBean")` on the container returns the
|
||||
product of the `FactoryBean`, whereas invoking `getBean("&myBean")` returns the
|
||||
@@ -4677,11 +4678,11 @@ example:
|
||||
----
|
||||
class SimpleMovieLister {
|
||||
|
||||
@Required
|
||||
lateinit var movieFinder: MovieFinder
|
||||
@Required
|
||||
lateinit var movieFinder: MovieFinder
|
||||
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
This annotation indicates that the affected bean property must be populated at
|
||||
@@ -5377,7 +5378,7 @@ Letting qualifier values select against target bean names, within the type-match
|
||||
candidates, does not require a `@Qualifier` annotation at the injection point.
|
||||
If there is no other resolution indicator (such as a qualifier or a primary marker),
|
||||
for a non-unique dependency situation, Spring matches the injection point name
|
||||
(that is, the field name or parameter name) against the target bean names and choose the
|
||||
(that is, the field name or parameter name) against the target bean names and chooses the
|
||||
same-named candidate, if any.
|
||||
====
|
||||
|
||||
@@ -5392,7 +5393,7 @@ matching an `account` qualifier against beans marked with the same qualifier lab
|
||||
|
||||
For beans that are themselves defined as a collection, `Map`, or array type, `@Resource`
|
||||
is a fine solution, referring to the specific collection or array bean by unique name.
|
||||
That said, as of 4.3, collection, you can match `Map`, and array types through Spring's
|
||||
That said, as of 4.3, you can match collection, `Map`, and array types through Spring's
|
||||
`@Autowired` type matching algorithm as well, as long as the element type information
|
||||
is preserved in `@Bean` return type signatures or collection inheritance hierarchies.
|
||||
In this case, you can use qualifier values to select among same-typed collections,
|
||||
@@ -6046,14 +6047,14 @@ example shows:
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
@Bean
|
||||
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
}
|
||||
@Bean
|
||||
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
}
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
@@ -6079,7 +6080,7 @@ will get properties from `application.properties` and `application.yml` files.
|
||||
|
||||
Built-in converter support provided by Spring allows simple type conversion (to `Integer`
|
||||
or `int` for example) to be automatically handled. Multiple comma-separated values can be
|
||||
automatically converted to String array without extra effort.
|
||||
automatically converted to `String` array without extra effort.
|
||||
|
||||
It is possible to provide a default value as following:
|
||||
|
||||
@@ -6103,8 +6104,8 @@ It is possible to provide a default value as following:
|
||||
class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String)
|
||||
----
|
||||
|
||||
A Spring `BeanPostProcessor` uses a `ConversionService` behind the scene to handle the
|
||||
process for converting the String value in `@Value` to the target type. If you want to
|
||||
A Spring `BeanPostProcessor` uses a `ConversionService` behind the scenes to handle the
|
||||
process for converting the `String` value in `@Value` to the target type. If you want to
|
||||
provide conversion support for your own custom type, you can provide your own
|
||||
`ConversionService` bean instance as the following example shows:
|
||||
|
||||
@@ -6130,7 +6131,7 @@ provide conversion support for your own custom type, you can provide your own
|
||||
|
||||
@Bean
|
||||
fun conversionService(): ConversionService {
|
||||
return DefaultFormattingConversionService().apply {
|
||||
return DefaultFormattingConversionService().apply {
|
||||
addConverter(MyCustomConverter())
|
||||
}
|
||||
}
|
||||
@@ -6319,7 +6320,7 @@ is meta-annotated with `@Component`, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> The `Component` causes `@Service` to be treated in the same way as `@Component`.
|
||||
<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
@@ -6333,7 +6334,7 @@ is meta-annotated with `@Component`, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> The `Component` causes `@Service` to be treated in the same way as `@Component`.
|
||||
<1> The `@Component` causes `@Service` to be treated in the same way as `@Component`.
|
||||
|
||||
You can also combine meta-annotations to create "`composed annotations`". For example,
|
||||
the `@RestController` annotation from Spring MVC is composed of `@Controller` and
|
||||
@@ -6595,7 +6596,7 @@ and using "`stub`" repositories instead:
|
||||
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
|
||||
excludeFilters = @Filter(Repository.class))
|
||||
public class AppConfig {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
----
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -6679,9 +6680,11 @@ factory method and other bean definition properties, such as a qualifier value t
|
||||
the `@Qualifier` annotation. Other method-level annotations that can be specified are
|
||||
`@Scope`, `@Lazy`, and custom qualifier annotations.
|
||||
|
||||
TIP: In addition to its role for component initialization, you can also place the `@Lazy` annotation
|
||||
on injection points marked with `@Autowired` or `@Inject`. In this context, it
|
||||
leads to the injection of a lazy-resolution proxy.
|
||||
TIP: In addition to its role for component initialization, you can also place the `@Lazy`
|
||||
annotation on injection points marked with `@Autowired` or `@Inject`. In this context,
|
||||
it leads to the injection of a lazy-resolution proxy. However, such a proxy approach
|
||||
is rather limited. For sophisticated lazy interactions, in particular in combination
|
||||
with optional dependencies, we recommend `ObjectProvider<MyTargetBean>` instead.
|
||||
|
||||
Autowired fields and methods are supported, as previously discussed, with additional
|
||||
support for autowiring of `@Bean` methods. The following example shows how to do so:
|
||||
@@ -7161,7 +7164,7 @@ configuration, as shown in the following example:
|
||||
With Gradle 4.6 and later, the dependency should be declared in the `annotationProcessor`
|
||||
configuration, as shown in the following example:
|
||||
|
||||
[source,groovy,indent=0subs="verbatim,quotes,attributes"]
|
||||
[source,groovy,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
dependencies {
|
||||
annotationProcessor "org.springframework:spring-context-indexer:{spring-version}"
|
||||
@@ -7179,8 +7182,8 @@ TIP: The index is enabled automatically when a `META-INF/spring.components` file
|
||||
on the classpath. If an index is partially available for some libraries (or use cases)
|
||||
but could not be built for the whole application, you can fall back to a regular classpath
|
||||
arrangement (as though no index were present at all) by setting `spring.index.ignore` to
|
||||
`true`, either as a JVM system property or in a `spring.properties` file at the root of
|
||||
the classpath.
|
||||
`true`, either as a JVM system property or via the
|
||||
<<appendix.adoc#appendix-spring-properties,`SpringProperties`>> mechanism.
|
||||
|
||||
|
||||
|
||||
@@ -7635,7 +7638,7 @@ to reduce subtle bugs that can be hard to track down when operating in "`lite`"
|
||||
****
|
||||
|
||||
The `@Bean` and `@Configuration` annotations are discussed in depth in the following sections.
|
||||
First, however, we cover the various ways of creating a spring container using by
|
||||
First, however, we cover the various ways of creating a spring container by using
|
||||
Java-based configuration.
|
||||
|
||||
|
||||
@@ -7762,7 +7765,7 @@ To enable component scanning, you can annotate your `@Configuration` class as fo
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = "com.acme") // <1>
|
||||
public class AppConfig {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> This annotation enables component scanning.
|
||||
@@ -7896,6 +7899,7 @@ init-param):
|
||||
|
||||
`@Bean` is a method-level annotation and a direct analog of the XML `<bean/>` element.
|
||||
The annotation supports some of the attributes offered by `<bean/>`, such as:
|
||||
|
||||
* <<beans-factory-lifecycle-initializingbean, init-method>>
|
||||
* <<beans-factory-lifecycle-disposablebean, destroy-method>>
|
||||
* <<beans-factory-autowire,autowiring>>
|
||||
@@ -7984,7 +7988,7 @@ return type, as the following example shows:
|
||||
|
||||
However, this limits the visibility for advance type prediction to the specified
|
||||
interface type (`TransferService`). Then, with the full type (`TransferServiceImpl`)
|
||||
known to the container only once, the affected singleton bean has been instantiated.
|
||||
known to the container only once the affected singleton bean has been instantiated.
|
||||
Non-lazy singleton beans get instantiated according to their declaration order,
|
||||
so you may see different type matching results depending on when another component
|
||||
tries to match by a non-declared type (such as `@Autowired TransferServiceImpl`,
|
||||
@@ -8242,8 +8246,10 @@ Spring offers a convenient way of working with scoped dependencies through
|
||||
<<beans-factory-scopes-other-injection, scoped proxies>>. The easiest way to create
|
||||
such a proxy when using the XML configuration is the `<aop:scoped-proxy/>` element.
|
||||
Configuring your beans in Java with a `@Scope` annotation offers equivalent support
|
||||
with the `proxyMode` attribute. The default is no proxy (`ScopedProxyMode.NO`),
|
||||
but you can specify `ScopedProxyMode.TARGET_CLASS` or `ScopedProxyMode.INTERFACES`.
|
||||
with the `proxyMode` attribute. The default is `ScopedProxyMode.DEFAULT`, which
|
||||
typically indicates that no scoped proxy should be created unless a different default
|
||||
has been configured at the component-scan instruction level. You can specify
|
||||
`ScopedProxyMode.TARGET_CLASS`, `ScopedProxyMode.INTERFACES` or `ScopedProxyMode.NO`.
|
||||
|
||||
If you port the scoped proxy example from the XML reference documentation (see
|
||||
<<beans-factory-scopes-other-injection, scoped proxies>>) to our `@Bean` using Java,
|
||||
@@ -8297,7 +8303,7 @@ as the following example shows:
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
@Bean(name = "myThing")
|
||||
@Bean("myThing")
|
||||
public Thing thing() {
|
||||
return new Thing();
|
||||
}
|
||||
@@ -8390,7 +8396,7 @@ annotation, as the following example shows:
|
||||
=== Using the `@Configuration` annotation
|
||||
|
||||
`@Configuration` is a class-level annotation indicating that an object is a source of
|
||||
bean definitions. `@Configuration` classes declare beans through public `@Bean` annotated
|
||||
bean definitions. `@Configuration` classes declare beans through `@Bean`-annotated
|
||||
methods. Calls to `@Bean` methods on `@Configuration` classes can also be used to define
|
||||
inter-bean dependencies. See <<beans-java-basic-concepts>> for a general introduction.
|
||||
|
||||
@@ -9139,7 +9145,7 @@ method that returns `true` or `false`. For example, the following listing shows
|
||||
val attrs = metadata.getAllAnnotationAttributes(Profile::class.java.name)
|
||||
if (attrs != null) {
|
||||
for (value in attrs["value"]!!) {
|
||||
if (context.environment.acceptsProfiles(Profiles .of(*value as Array<String>))) {
|
||||
if (context.environment.acceptsProfiles(Profiles.of(*value as Array<String>))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -9903,7 +9909,7 @@ as a way to provide a default definition for one or more beans. If any
|
||||
profile is enabled, the default profile does not apply.
|
||||
|
||||
You can change the name of the default profile by using `setDefaultProfiles()` on
|
||||
the `Environment` or ,declaratively, by using the `spring.profiles.default` property.
|
||||
the `Environment` or, declaratively, by using the `spring.profiles.default` property.
|
||||
|
||||
|
||||
|
||||
@@ -10222,8 +10228,8 @@ bean with the same name. If it does, it uses that bean as the `MessageSource`. I
|
||||
`DelegatingMessageSource` is instantiated in order to be able to accept calls to the
|
||||
methods defined above.
|
||||
|
||||
Spring provides two `MessageSource` implementations, `ResourceBundleMessageSource` and
|
||||
`StaticMessageSource`. Both implement `HierarchicalMessageSource` in order to do nested
|
||||
Spring provides three `MessageSource` implementations, `ResourceBundleMessageSource`, `ReloadableResourceBundleMessageSource`
|
||||
and `StaticMessageSource`. All of them implement `HierarchicalMessageSource` in order to do nested
|
||||
messaging. The `StaticMessageSource` is rarely used but provides programmatic ways to
|
||||
add messages to the source. The following example shows `ResourceBundleMessageSource`:
|
||||
|
||||
@@ -10408,6 +10414,10 @@ You can also use the `MessageSourceAware` interface to acquire a reference to an
|
||||
`ApplicationContext` that implements the `MessageSourceAware` interface is injected with
|
||||
the application context's `MessageSource` when the bean is created and configured.
|
||||
|
||||
NOTE: Because Spring's `MessageSource` is based on Java's `ResourceBundle`, it does not merge
|
||||
bundles with the same base name, but will only use the first bundle found.
|
||||
Subsequent message bundles with the same base name are ignored.
|
||||
|
||||
NOTE: As an alternative to `ResourceBundleMessageSource`, Spring provides a
|
||||
`ReloadableResourceBundleMessageSource` class. This variant supports the same bundle
|
||||
file format but is more flexible than the standard JDK based
|
||||
@@ -10718,7 +10728,7 @@ following example shows how to do so:
|
||||
----
|
||||
|
||||
It is also possible to add additional runtime filtering by using the `condition` attribute
|
||||
of the annotation that defines a <<expressions, `SpEL` expression>> , which should match
|
||||
of the annotation that defines a <<expressions, `SpEL` expression>>, which should match
|
||||
to actually invoke the method for a particular event.
|
||||
|
||||
The following example shows how our notifier can be rewritten to be invoked only if the
|
||||
@@ -10830,10 +10840,12 @@ The following example shows how to do so:
|
||||
Be aware of the following limitations when using asynchronous events:
|
||||
|
||||
* If an asynchronous event listener throws an `Exception`, it is not propagated to the
|
||||
caller. See `AsyncUncaughtExceptionHandler` for more details.
|
||||
caller. See
|
||||
{api-spring-framework}/aop/interceptor/AsyncUncaughtExceptionHandler.html[`AsyncUncaughtExceptionHandler`]
|
||||
for more details.
|
||||
* Asynchronous event listener methods cannot publish a subsequent event by returning a
|
||||
value. If you need to publish another event as the result of the processing, inject an
|
||||
{api-spring-framework}/aop/interceptor/AsyncUncaughtExceptionHandler.html[`ApplicationEventPublisher`]
|
||||
{api-spring-framework}/context/ApplicationEventPublisher.html[`ApplicationEventPublisher`]
|
||||
to publish the event manually.
|
||||
|
||||
|
||||
@@ -11021,8 +11033,8 @@ For a simple deployment of a Spring ApplicationContext as a Java EE RAR file:
|
||||
. Package
|
||||
all application classes into a RAR file (which is a standard JAR file with a different
|
||||
file extension).
|
||||
.Add all required library JARs into the root of the RAR archive.
|
||||
.Add a
|
||||
. Add all required library JARs into the root of the RAR archive.
|
||||
. Add a
|
||||
`META-INF/ra.xml` deployment descriptor (as shown in the {api-spring-framework}/jca/context/SpringContextResourceAdapter.html[javadoc for `SpringContextResourceAdapter`])
|
||||
and the corresponding Spring XML bean definition file(s) (typically
|
||||
`META-INF/applicationContext.xml`).
|
||||
@@ -11116,7 +11128,7 @@ The following table lists features provided by the `BeanFactory` and
|
||||
| No
|
||||
| Yes
|
||||
|
||||
| Convenient `MessageSource` access (for internalization)
|
||||
| Convenient `MessageSource` access (for internationalization)
|
||||
| No
|
||||
| Yes
|
||||
|
||||
|
||||
@@ -6615,7 +6615,6 @@ has it applied automatically:
|
||||
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
|
||||
// do the actual work
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
@@ -6740,11 +6739,17 @@ seconds and one running every morning at 6 AM. To finalize everything, we need t
|
||||
</bean>
|
||||
----
|
||||
|
||||
More properties are available for the `SchedulerFactoryBean`, such as the calendars
|
||||
used by the job details, properties to customize Quartz with, and others. See the
|
||||
{api-spring-framework}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`]
|
||||
More properties are available for the `SchedulerFactoryBean`, such as the calendars used by the
|
||||
job details, properties to customize Quartz with, and a Spring-provided JDBC DataSource. See
|
||||
the {api-spring-framework}/scheduling/quartz/SchedulerFactoryBean.html[`SchedulerFactoryBean`]
|
||||
javadoc for more information.
|
||||
|
||||
NOTE: `SchedulerFactoryBean` also recognizes a `quartz.properties` file in the classpath,
|
||||
based on Quartz property keys, as with regular Quartz configuration. Please note that many
|
||||
`SchedulerFactoryBean` settings interact with common Quartz settings in the properties file;
|
||||
it is therefore not recommended to specify values at both levels. For example, do not set
|
||||
an "org.quartz.jobStore.class" property if you mean to rely on a Spring-provided DataSource.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6756,8 +6761,8 @@ an existing Spring application. Similar to the <<data-access.adoc#transaction, t
|
||||
support, the caching abstraction allows consistent use of various caching solutions with
|
||||
minimal impact on the code.
|
||||
|
||||
As from Spring 4.1, the cache abstraction has been significantly extended with the
|
||||
support of <<cache-jsr-107,JSR-107 annotations>> and more customization options.
|
||||
In Spring Framework 4.1, the cache abstraction was significantly extended with support
|
||||
for <<cache-jsr-107,JSR-107 annotations>> and more customization options.
|
||||
|
||||
|
||||
|
||||
@@ -6815,15 +6820,16 @@ compliant caches (such as Ehcache 3.x). See <<cache-plug>> for more information
|
||||
plugging in other cache stores and providers.
|
||||
|
||||
IMPORTANT: The caching abstraction has no special handling for multi-threaded and
|
||||
multi-process environments, as such features are handled by the cache implementation. .
|
||||
multi-process environments, as such features are handled by the cache implementation.
|
||||
|
||||
If you have a multi-process environment (that is, an application deployed on several nodes),
|
||||
you need to configure your cache provider accordingly. Depending on your use cases, a copy
|
||||
of the same data on several nodes can be enough. However, if you change the data during
|
||||
the course of the application, you may need to enable other propagation mechanisms.
|
||||
|
||||
Caching a particular item is a direct equivalent of the typical get-if-not-found-then-
|
||||
proceed-and-put-eventually code blocks found with programmatic cache interaction.
|
||||
Caching a particular item is a direct equivalent of the typical
|
||||
get-if-not-found-then-proceed-and-put-eventually code blocks
|
||||
found with programmatic cache interaction.
|
||||
No locks are applied, and several threads may try to load the same item concurrently.
|
||||
The same applies to eviction. If several threads are trying to update or evict data
|
||||
concurrently, you may use stale data. Certain cache providers offer advanced features
|
||||
@@ -6875,7 +6881,7 @@ method -- if at least one cache is hit, the associated value is returned.
|
||||
NOTE: All the other caches that do not contain the value are also updated, even though
|
||||
the cached method was not actually invoked.
|
||||
|
||||
The following example uses `@Cacheable` on the `findBook` method:
|
||||
The following example uses `@Cacheable` on the `findBook` method with multiple caches:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
@@ -7028,7 +7034,7 @@ regardless of the content of the annotation.
|
||||
|
||||
Similarly to `key` and `keyGenerator`, the `cacheManager` and `cacheResolver`
|
||||
parameters are mutually exclusive, and an operation specifying both
|
||||
results in an exception. as a custom `CacheManager` is ignored by the
|
||||
results in an exception, as a custom `CacheManager` is ignored by the
|
||||
`CacheResolver` implementation. This is probably not what you expect.
|
||||
====
|
||||
|
||||
@@ -7053,7 +7059,6 @@ is updated in the cache. The following example shows how to use the `sync` attri
|
||||
----
|
||||
<1> Using the `sync` attribute.
|
||||
|
||||
|
||||
NOTE: This is an optional feature, and your favorite cache library may not support it.
|
||||
All `CacheManager` implementations provided by the core framework support it. See the
|
||||
documentation of your cache provider for more details.
|
||||
@@ -7092,9 +7097,11 @@ want to cache paperback books, as the following example does:
|
||||
<1> Using the `unless` attribute to block hardbacks.
|
||||
|
||||
|
||||
The cache abstraction supports `java.util.Optional`, using its content as the cached value
|
||||
only if it is present. `#result` always refers to the business entity and never a
|
||||
supported wrapper, so the previous example can be rewritten as follows:
|
||||
The cache abstraction supports `java.util.Optional` return types. If an `Optional` value
|
||||
is _present_, it will be stored in the associated cache. If an `Optional` value is not
|
||||
present, `null` will be stored in the associated cache. `#result` always refers to the
|
||||
business entity and never a supported wrapper, so the previous example can be rewritten
|
||||
as follows:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
@@ -7103,8 +7110,8 @@ supported wrapper, so the previous example can be rewritten as follows:
|
||||
public Optional<Book> findBook(String name)
|
||||
----
|
||||
|
||||
Note that `result` still refers to `Book` and not `Optional`. As it might be `null`, we
|
||||
should use the safe navigation operator.
|
||||
Note that `#result` still refers to `Book` and not `Optional<Book>`. Since it might be
|
||||
`null`, we use SpEL's <<core.adoc#expressions-operator-safe-navigation, safe navigation operator>>.
|
||||
|
||||
[[cache-spel-context]]
|
||||
===== Available Caching SpEL Evaluation Context
|
||||
@@ -7214,7 +7221,6 @@ all entries from the `books` cache:
|
||||
----
|
||||
<1> Using the `allEntries` attribute to evict all entries from the cache.
|
||||
|
||||
|
||||
This option comes in handy when an entire cache region needs to be cleared out.
|
||||
Rather than evicting each entry (which would take a long time, since it is inefficient),
|
||||
all the entries are removed in one operation, as the preceding example shows.
|
||||
@@ -7275,7 +7281,6 @@ comes into play. The following examples uses `@CacheConfig` to set the name of t
|
||||
----
|
||||
<1> Using `@CacheConfig` to set the name of the cache.
|
||||
|
||||
|
||||
`@CacheConfig` is a class-level annotation that allows sharing the cache names,
|
||||
the custom `KeyGenerator`, the custom `CacheManager`, and the custom `CacheResolver`.
|
||||
Placing this annotation on the class does not turn on any caching operation.
|
||||
@@ -7418,13 +7423,11 @@ if you need to annotate non-public methods, as it changes the bytecode itself.
|
||||
****
|
||||
|
||||
TIP: Spring recommends that you only annotate concrete classes (and methods of concrete
|
||||
classes) with the `@Cache{asterisk}` annotation, as opposed to annotating interfaces.
|
||||
You certainly can place the `@Cache{asterisk}` annotation on an interface (or an interface
|
||||
method), but this works only as you would expect it to if you use interface-based proxies.
|
||||
The fact that Java annotations are not inherited from interfaces means that, if you use
|
||||
class-based proxies (`proxy-target-class="true"`) or the weaving-based aspect
|
||||
(`mode="aspectj"`), the caching settings are not recognized by the proxying and weaving
|
||||
infrastructure, and the object is not wrapped in a caching proxy.
|
||||
classes) with the `@Cache{asterisk}` annotations, as opposed to annotating interfaces.
|
||||
You certainly can place an `@Cache{asterisk}` annotation on an interface (or an interface
|
||||
method), but this works only if you use the proxy mode (`mode="proxy"`). If you use the
|
||||
weaving-based aspect (`mode="aspectj"`), the caching settings are not recognized on
|
||||
interface-level declarations by the weaving infrastructure.
|
||||
|
||||
NOTE: In proxy mode (the default), only external method calls coming in through the
|
||||
proxy are intercepted. This means that self-invocation (in effect, a method within the
|
||||
@@ -7499,7 +7502,7 @@ up its declaration at runtime and understands its meaning. Note that, as mention
|
||||
=== JCache (JSR-107) Annotations
|
||||
|
||||
Since version 4.1, Spring's caching abstraction fully supports the JCache standard
|
||||
annotations: `@CacheResult`, `@CachePut`, `@CacheRemove`, and `@CacheRemoveAll`
|
||||
(JSR-107) annotations: `@CacheResult`, `@CachePut`, `@CacheRemove`, and `@CacheRemoveAll`
|
||||
as well as the `@CacheDefaults`, `@CacheKey`, and `@CacheValue` companions.
|
||||
You can use these annotations even without migrating your cache store to JSR-107.
|
||||
The internal implementation uses Spring's caching abstraction and provides default
|
||||
@@ -7513,8 +7516,8 @@ you can switch to these standard annotations without changing your cache storage
|
||||
==== Feature Summary
|
||||
|
||||
For those who are familiar with Spring's caching annotations, the following table
|
||||
describes the main differences between the Spring annotations and the JSR-107
|
||||
counterpart:
|
||||
describes the main differences between the Spring annotations and their JSR-107
|
||||
counterparts:
|
||||
|
||||
.Spring vs. JSR-107 caching annotations
|
||||
[cols="1,1,3"]
|
||||
@@ -7565,7 +7568,6 @@ to customize the factory for each cache operation, as the following example show
|
||||
----
|
||||
<1> Customizing the factory for this operation.
|
||||
|
||||
|
||||
NOTE: For all referenced classes, Spring tries to locate a bean with the given type.
|
||||
If more than one match exists, a new instance is created and can use the regular
|
||||
bean lifecycle callbacks, such as dependency injection.
|
||||
@@ -7609,16 +7611,15 @@ invoking the method again:
|
||||
|
||||
==== Enabling JSR-107 Support
|
||||
|
||||
You need do nothing specific to enable the JSR-107 support alongside Spring's
|
||||
declarative annotation support. Both `@EnableCaching` and the
|
||||
`cache:annotation-driven` element automatically enable the JCache support
|
||||
if both the JSR-107 API and the `spring-context-support` module are present
|
||||
in the classpath.
|
||||
You do not need to do anything specific to enable the JSR-107 support alongside Spring's
|
||||
declarative annotation support. Both `@EnableCaching` and the `cache:annotation-driven`
|
||||
XML element automatically enable the JCache support if both the JSR-107 API and the
|
||||
`spring-context-support` module are present in the classpath.
|
||||
|
||||
NOTE: Depending on your use case, the choice is basically yours. You can even
|
||||
mix and match services by using the JSR-107 API on some and using Spring's own
|
||||
annotations on others. However, if these services impact the same caches,
|
||||
you should use a consistent and identical key generation implementation.
|
||||
NOTE: Depending on your use case, the choice is basically yours. You can even mix and
|
||||
match services by using the JSR-107 API on some and using Spring's own annotations on
|
||||
others. However, if these services impact the same caches, you should use a consistent
|
||||
and identical key generation implementation.
|
||||
|
||||
|
||||
|
||||
@@ -7848,15 +7849,13 @@ invoked every time.
|
||||
=== Plugging-in Different Back-end Caches
|
||||
|
||||
Clearly, there are plenty of caching products out there that you can use as a backing
|
||||
store. To plug them in, you need to provide a `CacheManager` and a `Cache` implementation,
|
||||
since, unfortunately, there is no available standard that we can use instead.
|
||||
This may sound harder than it is, since, in practice, the classes tend to be simple
|
||||
https://en.wikipedia.org/wiki/Adapter_pattern[adapters] that map the caching abstraction
|
||||
framework on top of the storage API, as the `ehcache` classes do. Most `CacheManager`
|
||||
classes can use the classes in the `org.springframework.cache.support` package
|
||||
(such as `AbstractCacheManager` which takes care of the boiler-plate code,
|
||||
leaving only the actual mapping to be completed). We hope that, in time, the libraries
|
||||
that provide integration with Spring can fill in this small configuration gap.
|
||||
store. For those that do not support JSR-107 you need to provide a `CacheManager` and a
|
||||
`Cache` implementation. This may sound harder than it is, since, in practice, the classes
|
||||
tend to be simple https://en.wikipedia.org/wiki/Adapter_pattern[adapters] that map the
|
||||
caching abstraction framework on top of the storage API, as the `ehcache` classes do.
|
||||
Most `CacheManager` classes can use the classes in the
|
||||
`org.springframework.cache.support` package (such as `AbstractCacheManager` which takes
|
||||
care of the boiler-plate code, leaving only the actual mapping to be completed).
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user