Only proxy supported beans in BeanNameAutoProxyCreator

Prior to this commit, if a BeanNameAutoProxyCreator was configured with
a custom TargetSourceCreator, the TargetSourceCreator was applied to
all beans in the ApplicationContext. Thus, the list of supported
beanNames was effectively ignored when applying any
TargetSourceCreator. Consequently, if a TargetSourceCreator returned a
non-null TargetSource for a given bean, the BeanNameAutoProxyCreator
proxied the bean even if the bean name had not been configured in the
beanNames list.

This commit addresses this issue by ensuring that a custom
TargetSourceCreator is only applied to beans whose names match the
configured beanNames list in a BeanNameAutoProxyCreator.

Closes gh-24915
This commit is contained in:
Sam Brannen 2020-05-11 19:48:43 +02:00
parent a07dc80d72
commit 3c3e8e6a8b
3 changed files with 80 additions and 13 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2017 the original author or authors. * Copyright 2002-2020 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -37,6 +37,7 @@ import org.springframework.util.StringUtils;
* "interceptorNames" property. * "interceptorNames" property.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen
* @since 10.10.2003 * @since 10.10.2003
* @see #setBeanNames * @see #setBeanNames
* @see #isMatch * @see #isMatch
@ -46,6 +47,8 @@ import org.springframework.util.StringUtils;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator { public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator {
private static final String[] NO_ALIASES = new String[0];
@Nullable @Nullable
private List<String> beanNames; private List<String> beanNames;
@ -72,40 +75,70 @@ public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator {
/** /**
* Identify as bean to proxy if the bean name is in the configured list of names. * Delegate to {@link AbstractAutoProxyCreator#getCustomTargetSource(Class, String)}
* if the bean name matches one of the names in the configured list of supported
* names, returning {@code null} otherwise.
* @since 5.3
* @see #setBeanNames(String...)
*/
@Override
protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
return (isSupportedBeanName(beanClass, beanName) ?
super.getCustomTargetSource(beanClass, beanName) : null);
}
/**
* Identify as a bean to proxy if the bean name matches one of the names in
* the configured list of supported names.
* @see #setBeanNames(String...)
*/ */
@Override @Override
@Nullable @Nullable
protected Object[] getAdvicesAndAdvisorsForBean( protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
return (isSupportedBeanName(beanClass, beanName) ?
PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS : DO_NOT_PROXY);
}
/**
* Determine if the bean name for the given bean class matches one of the names
* in the configured list of supported names.
* @param beanClass the class of the bean to advise
* @param beanName the name of the bean
* @return {@code true} if the given bean name is supported
* @see #setBeanNames(String...)
*/
private boolean isSupportedBeanName(Class<?> beanClass, String beanName) {
if (this.beanNames != null) { if (this.beanNames != null) {
boolean isFactoryBean = FactoryBean.class.isAssignableFrom(beanClass);
for (String mappedName : this.beanNames) { for (String mappedName : this.beanNames) {
if (FactoryBean.class.isAssignableFrom(beanClass)) { if (isFactoryBean) {
if (!mappedName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { if (!mappedName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
continue; continue;
} }
mappedName = mappedName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); mappedName = mappedName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
} }
if (isMatch(beanName, mappedName)) { if (isMatch(beanName, mappedName)) {
return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; return true;
} }
BeanFactory beanFactory = getBeanFactory(); }
if (beanFactory != null) {
String[] aliases = beanFactory.getAliases(beanName); BeanFactory beanFactory = getBeanFactory();
for (String alias : aliases) { String[] aliases = (beanFactory != null ? beanFactory.getAliases(beanName) : NO_ALIASES);
if (isMatch(alias, mappedName)) { for (String alias : aliases) {
return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; for (String mappedName : this.beanNames) {
} if (isMatch(alias, mappedName)) {
return true;
} }
} }
} }
} }
return DO_NOT_PROXY; return false;
} }
/** /**
* Return if the given bean name matches the mapped name. * Determine if the given bean name matches the mapped name.
* <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches,
* as well as direct equality. Can be overridden in subclasses. * as well as direct equality. Can be overridden in subclasses.
* @param beanName the bean name to check * @param beanName the bean name to check

View File

@ -155,6 +155,16 @@ class BeanNameAutoProxyCreatorTests {
assertThat(((Advised)testBean).isFrozen()).isTrue(); assertThat(((Advised)testBean).isFrozen()).isTrue();
} }
@Test
void customTargetSourceCreatorsApplyOnlyToConfiguredBeanNames() {
ITestBean lazy1 = beanFactory.getBean("lazy1", ITestBean.class);
ITestBean alias1 = beanFactory.getBean("lazy1alias", ITestBean.class);
ITestBean lazy2 = beanFactory.getBean("lazy2", ITestBean.class);
assertThat(AopUtils.isAopProxy(lazy1)).isTrue();
assertThat(AopUtils.isAopProxy(alias1)).isTrue();
assertThat(AopUtils.isAopProxy(lazy2)).isFalse();
}
private void jdkAssertions(ITestBean tb, int nopInterceptorCount) { private void jdkAssertions(ITestBean tb, int nopInterceptorCount) {
NopInterceptor nop = (NopInterceptor) beanFactory.getBean("nopInterceptor"); NopInterceptor nop = (NopInterceptor) beanFactory.getBean("nopInterceptor");

View File

@ -104,4 +104,28 @@
<property name="name" value="noproxy" /> <property name="name" value="noproxy" />
</bean> </bean>
<bean id="lazy1" class="org.springframework.beans.testfixture.beans.TestBean" lazy-init="true">
<property name="name" value="lazy1" />
</bean>
<alias name="lazy1" alias="lazy1alias"/>
<bean id="lazy2" class="org.springframework.beans.testfixture.beans.TestBean" lazy-init="true">
<property name="name" value="lazy2" />
</bean>
<bean id="lazyBeanNameAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="lazy1" />
<property name="customTargetSourceCreators">
<bean class="org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator" />
</property>
</bean>
<bean id="lazyAliasBeanNameAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="lazy1alias" />
<property name="customTargetSourceCreators">
<bean class="org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator" />
</property>
</bean>
</beans> </beans>