+ Added 'proxyMode' attribute to @Scope annotation
+ Eliminated @ScopedProxy in favor of @Scope(proxyMode=NO|INTERFACES|TARGET_CLASS)
This commit is contained in:
parent
9735c8024c
commit
3231f458c8
|
|
@ -27,6 +27,7 @@ import org.springframework.beans.factory.parsing.Location;
|
|||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
|
@ -128,12 +129,11 @@ public final class BeanMethod {
|
|||
if (Modifier.isFinal(getModifiers()))
|
||||
problemReporter.error(new FinalMethodError());
|
||||
|
||||
if (this.getAnnotation(ScopedProxy.class) == null)
|
||||
return;
|
||||
|
||||
Scope scope = this.getAnnotation(Scope.class);
|
||||
if(scope == null || scope.equals(SINGLETON) || scope.equals(PROTOTYPE))
|
||||
problemReporter.error(new InvalidScopedProxyDeclarationError(this));
|
||||
if(scope != null
|
||||
&& scope.proxyMode() != ScopedProxyMode.NO
|
||||
&& (scope.value().equals(SINGLETON) || scope.value().equals(PROTOTYPE)))
|
||||
problemReporter.error(new InvalidScopedProxyDeclarationError(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -197,4 +197,15 @@ public final class BeanMethod {
|
|||
}
|
||||
}
|
||||
|
||||
public class InvalidScopedProxyDeclarationError extends Problem {
|
||||
public InvalidScopedProxyDeclarationError(BeanMethod method) {
|
||||
super(
|
||||
String.format("method %s contains an invalid annotation declaration: scoped proxies "
|
||||
+ "cannot be created for singleton/prototype beans", method.getName()),
|
||||
new Location(new FileSystemResource("/dev/null"))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,7 +18,9 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
// TODO: SJC-242 document BeanHandler
|
||||
|
|
@ -27,6 +29,9 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
|
|||
|
||||
private static final Log logger = LogFactory.getLog(BeanRegistrar.class);
|
||||
|
||||
/** Prefix used when registering the target object for a scoped proxy. */
|
||||
private static final String TARGET_NAME_PREFIX = "scopedTarget.";
|
||||
|
||||
/**
|
||||
* Ensures that <var>member</var> is a method and is annotated (directly or indirectly)
|
||||
* with {@link Bean @Bean}.
|
||||
|
|
@ -110,18 +115,17 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
|
|||
if (hasText(destroyMethodName))
|
||||
beanDef.setDestroyMethodName(destroyMethodName);
|
||||
|
||||
// is this method annotated with @ScopedProxy?
|
||||
ScopedProxy scopedProxy = method.getAnnotation(ScopedProxy.class);
|
||||
if (scopedProxy != null) {
|
||||
// is this method annotated with @Scope(scopedProxy=...)?
|
||||
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
|
||||
RootBeanDefinition targetDef = beanDef;
|
||||
//
|
||||
// Create a scoped proxy definition for the original bean name,
|
||||
// "hiding" the target bean in an internal target definition.
|
||||
String targetBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
|
||||
String targetBeanName = resolveHiddenScopedProxyBeanName(beanName);
|
||||
RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
|
||||
scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
|
||||
|
||||
if (scopedProxy.proxyTargetClass())
|
||||
if (scope.proxyMode() == ScopedProxyMode.TARGET_CLASS)
|
||||
targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
|
||||
// ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
|
||||
// don't need to set it explicitly here.
|
||||
|
|
@ -191,6 +195,19 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
|
|||
+ "could be found in %s or its ancestry", beanName, registry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <i>hidden</i> name based on a scoped proxy bean name.
|
||||
*
|
||||
* @param originalBeanName the scope proxy bean name as declared in the
|
||||
* Configuration-annotated class
|
||||
*
|
||||
* @return the internally-used <i>hidden</i> bean name
|
||||
*/
|
||||
public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
|
||||
Assert.hasText(originalBeanName);
|
||||
return TARGET_NAME_PREFIX.concat(originalBeanName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.config.java;
|
||||
|
||||
import org.springframework.beans.factory.parsing.Location;
|
||||
import org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
|
||||
|
||||
public class InvalidScopedProxyDeclarationError extends Problem {
|
||||
public InvalidScopedProxyDeclarationError(BeanMethod method) {
|
||||
super(
|
||||
String.format("method %s contains an invalid annotation declaration: @%s "
|
||||
+ "cannot be used on a singleton/prototype bean", method.getName(), ScopedProxy.class
|
||||
.getSimpleName()),
|
||||
new Location(new FileSystemResource("/dev/null"))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.config.java;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Annotation identical in functionality with <aop:scoped-proxy/> tag. Provides a smart
|
||||
* proxy backed by a scoped bean, which can be injected into object instances (usually singletons)
|
||||
* allowing the same reference to be held while delegating method invocations to the backing, scoped
|
||||
* beans.
|
||||
*
|
||||
* <p/>Used with scoped beans (non-singleton and non-prototype).</p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class ScopedConfig {
|
||||
*
|
||||
* @Bean(scope = "myScope")
|
||||
* @ScopedProxy
|
||||
* public SomeBean someBean() {
|
||||
* return new SomeBean();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public SomeOtherBean() {
|
||||
* return new AnotherBean(someBean());
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>See Spring reference <a href="http://static.springframework.org/spring/docs/2.5.x/reference/">
|
||||
* documentation</a> for more <a
|
||||
* href="http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes-other-injection">
|
||||
* details</a>.</p>
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ScopedProxy {
|
||||
|
||||
/**
|
||||
* Use CGLib-based class proxies (true) or JDK interface-based (false).
|
||||
*
|
||||
* Default is CGLib (true).
|
||||
* @return
|
||||
*/
|
||||
boolean proxyTargetClass() default true;
|
||||
|
||||
public static class Util {
|
||||
|
||||
private static final String TARGET_NAME_PREFIX = "scopedTarget.";
|
||||
|
||||
/**
|
||||
* Return the <i>hidden</i> name based on a scoped proxy bean name.
|
||||
*
|
||||
* @param originalBeanName the scope proxy bean name as declared in the
|
||||
* Configuration-annotated class
|
||||
*
|
||||
* @return the internally-used <i>hidden</i> bean name
|
||||
*/
|
||||
public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
|
||||
Assert.hasText(originalBeanName);
|
||||
return TARGET_NAME_PREFIX.concat(originalBeanName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,8 @@ import net.sf.cglib.proxy.MethodProxy;
|
|||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.config.java.Bean;
|
||||
import org.springframework.config.java.BeanRegistrar;
|
||||
import org.springframework.config.java.ScopedProxy;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
|
||||
|
|
@ -46,11 +47,11 @@ class BeanMethodInterceptor extends AbstractMethodInterceptor {
|
|||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
String beanName = getBeanName(method);
|
||||
|
||||
boolean isScopedProxy =
|
||||
(AnnotationUtils.findAnnotation(method, ScopedProxy.class) != null);
|
||||
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
||||
boolean isScopedProxy = (scope != null && scope.proxyMode() != ScopedProxyMode.NO);
|
||||
|
||||
String scopedBeanName =
|
||||
ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
|
||||
BeanRegistrar.resolveHiddenScopedProxyBeanName(beanName);
|
||||
if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
|
||||
beanName = scopedBeanName;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,14 +23,15 @@ import org.junit.After;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.scope.ScopedObject;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.config.java.Bean;
|
||||
import org.springframework.config.java.BeanRegistrar;
|
||||
import org.springframework.config.java.Configuration;
|
||||
import org.springframework.config.java.ScopedProxy;
|
||||
import org.springframework.config.java.StandardScopes;
|
||||
import org.springframework.config.java.support.ConfigurationClassPostProcessor;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
|
|
@ -40,8 +41,8 @@ import test.common.scope.CustomScope;
|
|||
|
||||
|
||||
/**
|
||||
* Tests that scopes are properly supported by using a custom {@link Scope} and
|
||||
* {@link ScopedProxy} declarations.
|
||||
* Tests that scopes are properly supported by using a custom Scope implementations
|
||||
* and scoped proxy {@link Bean} declarations.
|
||||
*
|
||||
* @see ScopeIntegrationTests
|
||||
* @author Costin Leau
|
||||
|
|
@ -68,7 +69,7 @@ public class ScopingTests {
|
|||
customScope = null;
|
||||
}
|
||||
|
||||
private GenericApplicationContext createContext(Scope customScope, Class<?> configClass) {
|
||||
private GenericApplicationContext createContext(org.springframework.beans.factory.config.Scope customScope, Class<?> configClass) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
if(customScope != null)
|
||||
beanFactory.registerScope(SCOPE, customScope);
|
||||
|
|
@ -112,13 +113,13 @@ public class ScopingTests {
|
|||
|
||||
|
||||
@Test
|
||||
public void testScopedProxyOnNonBeanAnnotatedMethod() throws Exception {
|
||||
// should throw - @ScopedProxy should not be applied on singleton/prototype beans
|
||||
public void testScopedProxyOnSingletonBeanMethod() throws Exception {
|
||||
// should throw - scoped proxies should not be applied on singleton/prototype beans
|
||||
try {
|
||||
createContext(null, InvalidProxyOnPredefinedScopesConfiguration.class);
|
||||
fail("exception expected");
|
||||
} catch (BeanDefinitionParsingException ex) {
|
||||
assertTrue(ex.getMessage().contains("cannot be used on a singleton/prototype bean"));
|
||||
assertTrue(ex.getMessage().contains("scoped proxies cannot be created for singleton/prototype beans"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +145,7 @@ public class ScopingTests {
|
|||
|
||||
String beanName = "scopedProxyInterface";
|
||||
|
||||
String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
|
||||
String scopedBeanName = BeanRegistrar.resolveHiddenScopedProxyBeanName(beanName);
|
||||
|
||||
// get hidden bean
|
||||
assertEquals(flag, spouse.getName());
|
||||
|
|
@ -178,7 +179,7 @@ public class ScopingTests {
|
|||
|
||||
String beanName = "scopedProxyClass";
|
||||
|
||||
String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
|
||||
String scopedBeanName = BeanRegistrar.resolveHiddenScopedProxyBeanName(beanName);
|
||||
|
||||
// get hidden bean
|
||||
assertEquals(flag, spouse.getName());
|
||||
|
|
@ -208,7 +209,7 @@ public class ScopingTests {
|
|||
public void testScopedConfigurationBeanDefinitionCount() throws Exception {
|
||||
|
||||
// count the beans
|
||||
// 6 @Beans + 1 Configuration + 2 @ScopedProxy
|
||||
// 6 @Beans + 1 Configuration + 2 scoped proxy
|
||||
assertThat(ctx.getBeanDefinitionCount(), equalTo(9));
|
||||
}
|
||||
|
||||
|
|
@ -241,8 +242,7 @@ public class ScopingTests {
|
|||
static class ScopeTestConfiguration {
|
||||
|
||||
@Bean
|
||||
@org.springframework.context.annotation.Scope(StandardScopes.SESSION)
|
||||
@ScopedProxy
|
||||
@Scope(value=StandardScopes.SESSION, proxyMode=ScopedProxyMode.INTERFACES)
|
||||
public Foo foo() {
|
||||
return new Foo();
|
||||
}
|
||||
|
|
@ -312,22 +312,16 @@ public class ScopingTests {
|
|||
assertNotSame(message, newBean2, newBean3);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class InvalidProxyObjectConfiguration {
|
||||
@ScopedProxy
|
||||
public Object invalidProxyObject() { return new Object(); }
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class InvalidProxyOnPredefinedScopesConfiguration {
|
||||
@ScopedProxy @Bean
|
||||
@Bean @Scope(proxyMode=ScopedProxyMode.INTERFACES)
|
||||
public Object invalidProxyOnPredefinedScopes() { return new Object(); }
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ScopedConfigurationClass {
|
||||
@Bean
|
||||
@org.springframework.context.annotation.Scope(SCOPE)
|
||||
@Scope(SCOPE)
|
||||
public TestBean scopedClass() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName(flag);
|
||||
|
|
@ -335,7 +329,7 @@ public class ScopingTests {
|
|||
}
|
||||
|
||||
@Bean
|
||||
@org.springframework.context.annotation.Scope(SCOPE)
|
||||
@Scope(SCOPE)
|
||||
public ITestBean scopedInterface() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName(flag);
|
||||
|
|
@ -343,17 +337,15 @@ public class ScopingTests {
|
|||
}
|
||||
|
||||
@Bean
|
||||
@org.springframework.context.annotation.Scope(SCOPE)
|
||||
@ScopedProxy(proxyTargetClass = false)
|
||||
@Scope(value=SCOPE, proxyMode=ScopedProxyMode.TARGET_CLASS)
|
||||
public ITestBean scopedProxyInterface() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName(flag);
|
||||
return tb;
|
||||
}
|
||||
|
||||
@ScopedProxy
|
||||
@Bean
|
||||
@org.springframework.context.annotation.Scope(SCOPE)
|
||||
@Scope(value=SCOPE, proxyMode=ScopedProxyMode.TARGET_CLASS)
|
||||
public TestBean scopedProxyClass() {
|
||||
TestBean tb = new TestBean();
|
||||
tb.setName(flag);
|
||||
|
|
|
|||
|
|
@ -45,4 +45,16 @@ public @interface Scope {
|
|||
*/
|
||||
String value() default BeanDefinition.SCOPE_SINGLETON;
|
||||
|
||||
/**
|
||||
* Specifies whether a component should be configured as a scoped proxy
|
||||
* and if so, whether the proxy should be interface-based or subclass-based.
|
||||
*
|
||||
* <p>Defaults to {@link ScopedProxyMode#NO}, indicating no scoped proxy
|
||||
* should be created.
|
||||
*
|
||||
* <p>Analogous to {@literal <aop:scoped-proxy/>} support in XML. Valid
|
||||
* only in conjunction with a non-singleton, non-prototype {@link #value()}.
|
||||
*/
|
||||
ScopedProxyMode proxyMode() default ScopedProxyMode.NO;
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue