Merge branch '5.3.x'

# Conflicts:
#	build.gradle
#	src/docs/asciidoc/integration.adoc
This commit is contained in:
Juergen Hoeller 2021-12-14 16:51:00 +01:00
commit bfdb93b406
15 changed files with 198 additions and 37 deletions

View File

@ -45,9 +45,10 @@ import org.springframework.core.NativeDetector;
* @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 {

View File

@ -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;
}
}

View File

@ -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.
@ -353,9 +353,31 @@ public interface ListableBeanFactory extends BeanFactory {
* @since 3.0
* @see #getBeanNamesForAnnotation
* @see #getBeansWithAnnotation
* @see #getType(String)
*/
@Nullable
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException;
/**
* Find an {@link Annotation} of {@code annotationType} on the specified bean,
* traversing its interfaces and super classes if no annotation can be found on
* the given class itself, as well as checking the bean's factory method (if any).
* @param beanName the name of the bean to look for annotations on
* @param annotationType the type of annotation to look for
* (at class, interface or factory method level of the specified bean)
* @param allowFactoryBeanInit whether a {@code FactoryBean} may get initialized
* just for the purpose of determining its object type
* @return the annotation of the given type if found, or {@code null} otherwise
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* @since 5.3.14
* @see #getBeanNamesForAnnotation
* @see #getBeansWithAnnotation
* @see #getType(String, boolean)
*/
@Nullable
<A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException;
}

View File

@ -719,14 +719,23 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException {
return findMergedAnnotationOnBean(beanName, annotationType)
return findAnnotationOnBean(beanName, annotationType, true);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
return findMergedAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit)
.synthesize(MergedAnnotation::isPresent).orElse(null);
}
private <A extends Annotation> MergedAnnotation<A> findMergedAnnotationOnBean(
String beanName, Class<A> annotationType) {
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit) {
Class<?> beanType = getType(beanName);
Class<?> beanType = getType(beanName, allowFactoryBeanInit);
if (beanType != null) {
MergedAnnotation<A> annotation =
MergedAnnotations.from(beanType, SearchStrategy.TYPE_HIERARCHY).get(annotationType);

View File

@ -459,7 +459,16 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException {
Class<?> beanType = getType(beanName);
return findAnnotationOnBean(beanName, annotationType, true);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
Class<?> beanType = getType(beanName, allowFactoryBeanInit);
return (beanType != null ? AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType) : null);
}

View File

@ -39,6 +39,7 @@ import org.springframework.lang.Nullable;
* Subclass of Quartz's {@link JobStoreCMT} class that delegates to a Spring-managed
* {@link DataSource} instead of using a Quartz-managed JDBC connection pool.
* This JobStore will be used if SchedulerFactoryBean's "dataSource" property is set.
* You may also configure it explicitly, possibly as a custom subclass of this class.
*
* <p>Supports both transactional and non-transactional DataSource access.
* With a non-XA DataSource and local Spring transactions, a single DataSource
@ -58,6 +59,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
*/

View File

@ -310,9 +310,11 @@ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBe
/**
* Set the default {@link DataSource} to be used by the Scheduler.
* 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.
* (You may explicitly define Spring's {@link LocalDataSourceJobStore}
* but that's the default when using this method anyway.)
* <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.

View File

@ -1309,6 +1309,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return getBeanFactory().findAnnotationOnBean(beanName, annotationType);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
assertBeanFactoryActive();
return getBeanFactory().findAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit);
}
//---------------------------------------------------------------------
// Implementation of HierarchicalBeanFactory interface

View File

@ -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 {
@ -633,20 +633,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.getAdvisorCount()).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.getAdvisorCount()).isEqualTo(1);
}
/**
* Fires only on void methods. Saves list of methods intercepted.

View File

@ -96,6 +96,19 @@ public class EnableCachingIntegrationTests {
assertCacheHit(key, value, cache);
}
@Test
public void barServiceWithCacheableInterfaceCglib() {
this.context = new AnnotationConfigApplicationContext(BarConfigCglib.class);
BarService service = this.context.getBean(BarService.class);
Cache cache = getCache();
Object key = new Object();
assertCacheMiss(key, cache);
Object value = service.getSimple(key);
assertCacheHit(key, value, cache);
}
@Test
public void beanConditionOff() {
this.context = new AnnotationConfigApplicationContext(BeanConditionConfig.class);
@ -223,6 +236,36 @@ public class EnableCachingIntegrationTests {
}
}
@Configuration
@Import(SharedConfig.class)
@EnableCaching(proxyTargetClass = true)
static class BarConfigCglib {
@Bean
public BarService barService() {
return new BarServiceImpl();
}
}
interface BarService {
@Cacheable(cacheNames = "testCache")
Object getSimple(Object key);
}
static class BarServiceImpl implements BarService {
private final AtomicLong counter = new AtomicLong();
@Override
public Object getSimple(Object key) {
return this.counter.getAndIncrement();
}
}
@Configuration
@Import(FooConfig.class)
@EnableCaching

View File

@ -76,6 +76,7 @@ class ThreadPoolExecutorFactoryBeanTests {
verify(threadPoolExecutor).prestartAllCoreThreads();
}
@Configuration
static class ExecutorConfig {
@ -83,9 +84,10 @@ class ThreadPoolExecutorFactoryBeanTests {
ThreadPoolExecutorFactoryBean executor() {
return new ThreadPoolExecutorFactoryBean();
}
}
@SuppressWarnings("serial")
private static class TestThreadPoolExecutorFactoryBean extends ThreadPoolExecutorFactoryBean {
@Override

View File

@ -22,6 +22,7 @@ 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.
@ -71,10 +72,10 @@ public abstract class LogFormatUtils {
}
String result;
try {
result = value.toString();
result = ObjectUtils.nullSafeToString(value);
}
catch (Throwable ex) {
result = ex.toString();
result = ObjectUtils.nullSafeToString(ex);
}
if (maxLength != -1) {
result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result);

View File

@ -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.
@ -1159,6 +1159,20 @@ class ResolvableTypeTests {
assertThatResolvableType(complex4).isNotAssignableFrom(complex3);
}
@Test
void identifyTypeVariable() throws Exception {
Method method = ClassArguments.class.getMethod("typedArgumentFirst", Class.class, Class.class, Class.class);
ResolvableType returnType = ResolvableType.forMethodReturnType(method, ClassArguments.class);
ResolvableType arg0 = ResolvableType.forMethodParameter(method, 0, ClassArguments.class);
ResolvableType arg1 = ResolvableType.forMethodParameter(method, 1, ClassArguments.class);
ResolvableType arg2 = ResolvableType.forMethodParameter(method, 2, ClassArguments.class);
assertThat(returnType.getType().equals(arg0.as(Class.class).getGeneric(0).getType())).isTrue();
assertThat(returnType.getType().equals(arg1.as(Class.class).getGeneric(0).getType())).isFalse();
assertThat(returnType.getType().equals(arg2.as(Class.class).getGeneric(0).getType())).isFalse();
}
@Test
void hashCodeAndEquals() throws Exception {
ResolvableType forClass = ResolvableType.forClass(List.class);
@ -1427,6 +1441,10 @@ class ResolvableTypeTests {
}
interface TypedMethods extends Methods<String> {
}
static class AssignmentBase<O, C, S> {
public O o;
@ -1479,7 +1497,9 @@ class ResolvableTypeTests {
}
interface TypedMethods extends Methods<String> {
interface ClassArguments {
<T> T typedArgumentFirst(Class<T> arg0, Class<?> arg1, Class<Object> arg2);
}

View File

@ -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.
@ -314,6 +314,15 @@ class StubWebApplicationContext implements WebApplicationContext {
return this.beanFactory.findAnnotationOnBean(beanName, annotationType);
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(
String beanName, Class<A> annotationType, boolean allowFactoryBeanInit)
throws NoSuchBeanDefinitionException {
return this.beanFactory.findAnnotationOnBean(beanName, annotationType, allowFactoryBeanInit);
}
//---------------------------------------------------------------------
// Implementation of HierarchicalBeanFactory interface

View File

@ -5385,6 +5385,7 @@ You can use these macros instead of the six-digit value, thus: `@Scheduled(cron
|===
[[scheduling-quartz]]
=== Using the Quartz Scheduler
@ -5440,7 +5441,6 @@ has it applied automatically:
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
// do the actual work
}
}
----
@ -5560,11 +5560,19 @@ 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,
or specify an `org.springframework.scheduling.quartz.LocalDataSourceJobStore` variant which
is a full-fledged replacement for the standard `org.quartz.impl.jdbcjobstore.JobStoreTX`.
@ -5866,7 +5874,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.
@ -6024,7 +6031,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.
@ -6083,7 +6089,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.
@ -6224,11 +6229,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 the proxy mode (`mode="proxy"`).
If you use the weaving-based aspect (`mode="aspectj"`), the caching settings are not
recognized by weaving infrastructure.
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
@ -6365,7 +6370,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.