custom stereotype annotations can be meta-annotated with @Service, @Controller etc as well; @Scope and @Transactional are now supported as meta-annotations on custom annotations
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1065 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
72a131cf34
commit
906adab5a4
|
|
@ -19,7 +19,6 @@ package org.springframework.context.annotation;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
|
|
@ -318,17 +317,4 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inner factory class used to just introduce an AOP framework dependency
|
|
||||||
* when actually creating a scoped proxy.
|
|
||||||
*/
|
|
||||||
private static class ScopedProxyCreator {
|
|
||||||
|
|
||||||
public static BeanDefinitionHolder createScopedProxy(
|
|
||||||
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
|
|
||||||
|
|
||||||
return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import java.util.Set;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowire;
|
import org.springframework.beans.factory.annotation.Autowire;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
|
|
@ -184,7 +183,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
// replace the original bean definition with the target one, if necessary
|
// replace the original bean definition with the target one, if necessary
|
||||||
BeanDefinition beanDefToRegister = beanDef;
|
BeanDefinition beanDefToRegister = beanDef;
|
||||||
if (proxyMode != ScopedProxyMode.NO) {
|
if (proxyMode != ScopedProxyMode.NO) {
|
||||||
BeanDefinitionHolder proxyDef = ScopedProxyUtils.createScopedProxy(
|
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
|
||||||
new BeanDefinitionHolder(beanDef, beanName), registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
|
new BeanDefinitionHolder(beanDef, beanName), registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
|
||||||
beanDefToRegister = proxyDef.getBeanDefinition();
|
beanDefToRegister = proxyDef.getBeanDefinition();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,17 @@ class ConfigurationClassEnhancer {
|
||||||
*/
|
*/
|
||||||
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
|
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
|
||||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||||
|
|
||||||
this.callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
this.callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
||||||
this.callbackInstances.add(NoOp.INSTANCE);
|
this.callbackInstances.add(NoOp.INSTANCE);
|
||||||
|
|
||||||
for (Callback callback : this.callbackInstances) {
|
for (Callback callback : this.callbackInstances) {
|
||||||
this.callbackTypes.add(callback.getClass());
|
this.callbackTypes.add(callback.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the callback filter to return the index of the BeanMethodInterceptor when
|
// Set up the callback filter to return the index of the BeanMethodInterceptor when
|
||||||
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
||||||
this.callbackFilter = new CallbackFilter() {
|
callbackFilter = new CallbackFilter() {
|
||||||
public int accept(Method candidateMethod) {
|
public int accept(Method candidateMethod) {
|
||||||
return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null) ? 0 : 1;
|
return (AnnotationUtils.findAnnotation(candidateMethod, Bean.class) != null) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
@ -85,8 +88,8 @@ class ConfigurationClassEnhancer {
|
||||||
logger.debug("Enhancing " + configClass.getName());
|
logger.debug("Enhancing " + configClass.getName());
|
||||||
}
|
}
|
||||||
Class<?> enhancedClass = createClass(newEnhancer(configClass));
|
Class<?> enhancedClass = createClass(newEnhancer(configClass));
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
logger.info(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
||||||
configClass.getName(), enhancedClass.getName()));
|
configClass.getName(), enhancedClass.getName()));
|
||||||
}
|
}
|
||||||
return enhancedClass;
|
return enhancedClass;
|
||||||
|
|
@ -151,6 +154,7 @@ class ConfigurationClassEnhancer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine whether this bean is a scoped-proxy
|
// determine whether this bean is a scoped-proxy
|
||||||
|
// TODO: remove hard ScopedProxyUtils dependency
|
||||||
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
|
||||||
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
|
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
|
||||||
String scopedBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
|
String scopedBeanName = ScopedProxyUtils.getTargetBeanName(beanName);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
|
@ -121,7 +123,7 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||||
* Build and validate a configuration model based on the registry of
|
* Build and validate a configuration model based on the registry of
|
||||||
* {@link Configuration} classes.
|
* {@link Configuration} classes.
|
||||||
*/
|
*/
|
||||||
protected final void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
protected void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
|
||||||
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
|
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
|
||||||
for (String beanName : registry.getBeanDefinitionNames()) {
|
for (String beanName : registry.getBeanDefinitionNames()) {
|
||||||
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
||||||
|
|
@ -157,14 +159,19 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||||
new ConfigurationClassBeanDefinitionReader(registry).loadBeanDefinitions(parser.getModel());
|
new ConfigurationClassBeanDefinitionReader(registry).loadBeanDefinitions(parser.getModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkConfigurationClassCandidate(BeanDefinition beanDef) {
|
/**
|
||||||
|
* Check whether the given bean definition is a candidate for a configuration class,
|
||||||
|
* and mark it accordingly.
|
||||||
|
* @param beanDef the bean definition to check
|
||||||
|
* @return whether the candidate qualifies as (any kind of) configuration class
|
||||||
|
*/
|
||||||
|
protected boolean checkConfigurationClassCandidate(BeanDefinition beanDef) {
|
||||||
AnnotationMetadata metadata;
|
AnnotationMetadata metadata;
|
||||||
|
|
||||||
// Check already loaded Class if present...
|
// Check already loaded Class if present...
|
||||||
// since we possibly can't even load the class file for this Class.
|
// since we possibly can't even load the class file for this Class.
|
||||||
if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
|
if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
|
||||||
Class beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
|
metadata = new StandardAnnotationMetadata(((AbstractBeanDefinition) beanDef).getBeanClass());
|
||||||
metadata = new StandardAnnotationMetadata(beanClass);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String className = beanDef.getBeanClassName();
|
String className = beanDef.getBeanClassName();
|
||||||
|
|
@ -199,12 +206,16 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||||
* Candidate status is determined by BeanDefinition attribute metadata.
|
* Candidate status is determined by BeanDefinition attribute metadata.
|
||||||
* @see ConfigurationClassEnhancer
|
* @see ConfigurationClassEnhancer
|
||||||
*/
|
*/
|
||||||
private void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
|
protected void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
|
||||||
Set<BeanDefinitionHolder> configBeanDefs = new LinkedHashSet<BeanDefinitionHolder>();
|
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<String, AbstractBeanDefinition>();
|
||||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||||
if (CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE))) {
|
if (CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE))) {
|
||||||
configBeanDefs.add(new BeanDefinitionHolder(beanDef, beanName));
|
if (!(beanDef instanceof AbstractBeanDefinition)) {
|
||||||
|
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
|
||||||
|
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
|
||||||
|
}
|
||||||
|
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (configBeanDefs.isEmpty()) {
|
if (configBeanDefs.isEmpty()) {
|
||||||
|
|
@ -212,27 +223,23 @@ public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!cglibAvailable) {
|
if (!cglibAvailable) {
|
||||||
Set<String> beanNames = new LinkedHashSet<String>();
|
|
||||||
for (BeanDefinitionHolder holder : configBeanDefs) {
|
|
||||||
beanNames.add(holder.getBeanName());
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("CGLIB is required to process @Configuration classes. " +
|
throw new IllegalStateException("CGLIB is required to process @Configuration classes. " +
|
||||||
"Either add CGLIB to the classpath or remove the following @Configuration bean definitions: " +
|
"Either add CGLIB to the classpath or remove the following @Configuration bean definitions: " +
|
||||||
beanNames);
|
configBeanDefs.keySet());
|
||||||
}
|
}
|
||||||
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(beanFactory);
|
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer(beanFactory);
|
||||||
for (BeanDefinitionHolder holder : configBeanDefs) {
|
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
|
||||||
AbstractBeanDefinition beanDef = (AbstractBeanDefinition) holder.getBeanDefinition();
|
AbstractBeanDefinition beanDef = entry.getValue();
|
||||||
try {
|
try {
|
||||||
Class configClass = beanDef.resolveBeanClass(this.beanClassLoader);
|
Class configClass = beanDef.resolveBeanClass(this.beanClassLoader);
|
||||||
Class enhancedClass = enhancer.enhance(configClass);
|
Class enhancedClass = enhancer.enhance(configClass);
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' " +
|
logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' " +
|
||||||
"with enhanced class name '%s'", holder.getBeanName(), configClass.getName(), enhancedClass.getName()));
|
"with enhanced class name '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
|
||||||
}
|
}
|
||||||
beanDef.setBeanClass(enhancedClass);
|
beanDef.setBeanClass(enhancedClass);
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException ex) {
|
catch (Throwable ex) {
|
||||||
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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.context.annotation;
|
||||||
|
|
||||||
|
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate factory class used to just introduce an AOP framework dependency
|
||||||
|
* when actually creating a scoped proxy.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 3.0
|
||||||
|
* @see org.springframework.aop.scope.ScopedProxyUtils#createScopedProxy
|
||||||
|
*/
|
||||||
|
class ScopedProxyCreator {
|
||||||
|
|
||||||
|
public static BeanDefinitionHolder createScopedProxy(
|
||||||
|
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
|
||||||
|
|
||||||
|
return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 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.
|
||||||
|
|
@ -21,14 +21,16 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.context.annotation.Scope;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Component
|
@Service
|
||||||
|
@Scope("prototype")
|
||||||
public @interface CustomStereotype {
|
public @interface CustomStereotype {
|
||||||
|
|
||||||
String value() default "thoreau";
|
String value() default "thoreau";
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ public class ClassPathBeanDefinitionScannerTests {
|
||||||
FooServiceImpl service = context.getBean("fooServiceImpl", FooServiceImpl.class);
|
FooServiceImpl service = context.getBean("fooServiceImpl", FooServiceImpl.class);
|
||||||
assertTrue(context.getDefaultListableBeanFactory().containsSingleton("myNamedComponent"));
|
assertTrue(context.getDefaultListableBeanFactory().containsSingleton("myNamedComponent"));
|
||||||
assertEquals("bar", service.foo(1));
|
assertEquals("bar", service.foo(1));
|
||||||
|
assertTrue(context.isPrototype("thoreau"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2009 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.
|
||||||
|
|
@ -16,23 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.context.annotation;
|
package org.springframework.context.annotation;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
|
||||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
|
||||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
|
||||||
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
import example.scannable.FooDao;
|
import example.scannable.FooDao;
|
||||||
import example.scannable.FooService;
|
import example.scannable.FooService;
|
||||||
import example.scannable.FooServiceImpl;
|
import example.scannable.FooServiceImpl;
|
||||||
|
|
@ -41,6 +28,18 @@ import example.scannable.NamedComponent;
|
||||||
import example.scannable.NamedStubDao;
|
import example.scannable.NamedStubDao;
|
||||||
import example.scannable.ServiceInvocationCounter;
|
import example.scannable.ServiceInvocationCounter;
|
||||||
import example.scannable.StubFooDao;
|
import example.scannable.StubFooDao;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||||
|
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||||
|
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
|
|
@ -95,7 +94,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
||||||
provider.addExcludeFilter(new AnnotationTypeFilter(Service.class));
|
provider.addExcludeFilter(new AnnotationTypeFilter(Service.class));
|
||||||
provider.addExcludeFilter(new AnnotationTypeFilter(Controller.class));
|
provider.addExcludeFilter(new AnnotationTypeFilter(Controller.class));
|
||||||
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
|
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
|
||||||
assertEquals(3, candidates.size());
|
assertEquals(2, candidates.size());
|
||||||
assertTrue(containsBeanClass(candidates, NamedComponent.class));
|
assertTrue(containsBeanClass(candidates, NamedComponent.class));
|
||||||
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
||||||
assertFalse(containsBeanClass(candidates, FooServiceImpl.class));
|
assertFalse(containsBeanClass(candidates, FooServiceImpl.class));
|
||||||
|
|
@ -107,8 +106,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
|
||||||
@Test
|
@Test
|
||||||
public void testWithAspectAnnotationOnly() throws Exception {
|
public void testWithAspectAnnotationOnly() throws Exception {
|
||||||
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
|
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
|
||||||
provider.addIncludeFilter(new AnnotationTypeFilter(
|
provider.addIncludeFilter(new AnnotationTypeFilter(Aspect.class));
|
||||||
ClassUtils.forName("org.aspectj.lang.annotation.Aspect")));
|
|
||||||
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
|
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
|
||||||
assertEquals(1, candidates.size());
|
assertEquals(1, candidates.size());
|
||||||
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,12 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.annotation.StandardScopes;
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
|
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
|
||||||
|
|
@ -64,14 +65,20 @@ public class ConfigurationClassProcessingTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customBeanNameIsRespected() {
|
public void customBeanNameIsRespected() {
|
||||||
BeanFactory factory = initBeanFactory(ConfigWithBeanWithCustomName.class);
|
GenericApplicationContext ac = new GenericApplicationContext();
|
||||||
assertSame(factory.getBean("customName"), ConfigWithBeanWithCustomName.testBean);
|
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
|
||||||
|
ac.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithBeanWithCustomName.class));
|
||||||
|
ac.refresh();
|
||||||
|
assertSame(ac.getBean("customName"), ConfigWithBeanWithCustomName.testBean);
|
||||||
|
|
||||||
// method name should not be registered
|
// method name should not be registered
|
||||||
try {
|
try {
|
||||||
factory.getBean("methodName");
|
ac.getBean("methodName");
|
||||||
fail("bean should not have been registered with 'methodName'");
|
fail("bean should not have been registered with 'methodName'");
|
||||||
} catch (NoSuchBeanDefinitionException ex) { /* expected */ }
|
}
|
||||||
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -165,7 +172,7 @@ public class ConfigurationClassProcessingTests {
|
||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean @Scope(StandardScopes.PROTOTYPE)
|
@Bean @Scope("prototype")
|
||||||
public TestBean baz() {
|
public TestBean baz() {
|
||||||
return new TestBean("bar");
|
return new TestBean("bar");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import test.beans.TestBean;
|
||||||
|
|
||||||
import org.springframework.aop.scope.ScopedObject;
|
import org.springframework.aop.scope.ScopedObject;
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
|
||||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
@ -37,7 +36,6 @@ import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.annotation.ScopedProxyMode;
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
import org.springframework.context.annotation.StandardScopes;
|
|
||||||
import org.springframework.context.support.GenericApplicationContext;
|
import org.springframework.context.support.GenericApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -52,7 +50,9 @@ public class ScopingTests {
|
||||||
public static String flag = "1";
|
public static String flag = "1";
|
||||||
|
|
||||||
private static final String SCOPE = "my scope";
|
private static final String SCOPE = "my scope";
|
||||||
|
|
||||||
private CustomScope customScope;
|
private CustomScope customScope;
|
||||||
|
|
||||||
private GenericApplicationContext ctx;
|
private GenericApplicationContext ctx;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|
@ -117,10 +117,8 @@ public class ScopingTests {
|
||||||
assertFalse(bean instanceof ScopedObject);
|
assertFalse(bean instanceof ScopedObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testScopedProxyConfiguration() throws Exception {
|
public void testScopedProxyConfiguration() throws Exception {
|
||||||
|
|
||||||
TestBean singleton = (TestBean) ctx.getBean("singletonWithScopedInterfaceDep");
|
TestBean singleton = (TestBean) ctx.getBean("singletonWithScopedInterfaceDep");
|
||||||
ITestBean spouse = singleton.getSpouse();
|
ITestBean spouse = singleton.getSpouse();
|
||||||
assertTrue("scoped bean is not wrapped by the scoped-proxy", spouse instanceof ScopedObject);
|
assertTrue("scoped bean is not wrapped by the scoped-proxy", spouse instanceof ScopedObject);
|
||||||
|
|
@ -224,7 +222,7 @@ public class ScopingTests {
|
||||||
static class ScopeTestConfiguration {
|
static class ScopeTestConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Scope(value=StandardScopes.SESSION, proxyMode=ScopedProxyMode.INTERFACES)
|
@Scope(value="session", proxyMode=ScopedProxyMode.INTERFACES)
|
||||||
public Foo foo() {
|
public Foo foo() {
|
||||||
return new Foo();
|
return new Foo();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,14 @@ public abstract class AnnotationUtils {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!Annotation.class.isAssignableFrom(clazz)) {
|
||||||
|
for (Annotation ann : clazz.getAnnotations()) {
|
||||||
|
annotation = findAnnotation(ann.annotationType(), annotationType);
|
||||||
|
if (annotation != null) {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Class<?> superClass = clazz.getSuperclass();
|
Class<?> superClass = clazz.getSuperclass();
|
||||||
if (superClass == null || superClass.equals(Object.class)) {
|
if (superClass == null || superClass.equals(Object.class)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,14 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||||
*/
|
*/
|
||||||
Set<String> getAnnotationTypes();
|
Set<String> getAnnotationTypes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the names of all meta-annotation types defined on the
|
||||||
|
* given annotation type of the underlying class.
|
||||||
|
* @param annotationType the meta-annotation type to look for
|
||||||
|
* @return the meta-annotation type names
|
||||||
|
*/
|
||||||
|
Set<String> getMetaAnnotationTypes(String annotationType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the underlying class has an annotation of the given
|
* Determine whether the underlying class has an annotation of the given
|
||||||
* type defined.
|
* type defined.
|
||||||
|
|
@ -45,6 +53,14 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||||
*/
|
*/
|
||||||
boolean hasAnnotation(String annotationType);
|
boolean hasAnnotation(String annotationType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the underlying class has an annotation that
|
||||||
|
* is itself annotated with the meta-annotation of the given type.
|
||||||
|
* @param metaAnnotationType the meta-annotation type to look for
|
||||||
|
* @return whether a matching meta-annotation is defined
|
||||||
|
*/
|
||||||
|
boolean hasMetaAnnotation(String metaAnnotationType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the attributes of the annotation of the given type,
|
* Retrieve the attributes of the annotation of the given type,
|
||||||
* if any (i.e. if defined on the underlying class).
|
* if any (i.e. if defined on the underlying class).
|
||||||
|
|
@ -55,22 +71,6 @@ public interface AnnotationMetadata extends ClassMetadata {
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getAnnotationAttributes(String annotationType);
|
Map<String, Object> getAnnotationAttributes(String annotationType);
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the names of all meta-annotation types defined on the
|
|
||||||
* given annotation type of the underlying class.
|
|
||||||
* @param annotationType the meta-annotation type to look for
|
|
||||||
* @return the meta-annotation type names
|
|
||||||
*/
|
|
||||||
Set<String> getMetaAnnotationTypes(String annotationType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the underlying class has an annotation that
|
|
||||||
* is itself annotated with the meta-annotation of the given type.
|
|
||||||
* @param metaAnnotationType the meta-annotation type to look for
|
|
||||||
* @return whether a matching meta-annotation is defined
|
|
||||||
*/
|
|
||||||
boolean hasMetaAnnotation(String metaAnnotationType);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the method metadata for all methods that are annotated
|
* Retrieve the method metadata for all methods that are annotated
|
||||||
* with at least one annotation type.
|
* with at least one annotation type.
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package org.springframework.core.type;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -45,7 +44,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
|
|
||||||
|
|
||||||
public Set<String> getAnnotationTypes() {
|
public Set<String> getAnnotationTypes() {
|
||||||
Set<String> types = new HashSet<String>();
|
Set<String> types = new LinkedHashSet<String>();
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (Annotation ann : anns) {
|
for (Annotation ann : anns) {
|
||||||
types.add(ann.annotationType().getName());
|
types.add(ann.annotationType().getName());
|
||||||
|
|
@ -53,6 +52,24 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||||
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
|
for (Annotation ann : anns) {
|
||||||
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
|
Set<String> types = new LinkedHashSet<String>();
|
||||||
|
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
||||||
|
for (Annotation metaAnn : metaAnns) {
|
||||||
|
types.add(metaAnn.annotationType().getName());
|
||||||
|
for (Annotation metaMetaAnn : metaAnn.annotationType().getAnnotations()) {
|
||||||
|
types.add(metaMetaAnn.annotationType().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasAnnotation(String annotationType) {
|
public boolean hasAnnotation(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (Annotation ann : anns) {
|
for (Annotation ann : anns) {
|
||||||
|
|
@ -63,42 +80,37 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasMetaAnnotation(String annotationType) {
|
||||||
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
|
for (Annotation ann : anns) {
|
||||||
|
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
||||||
|
for (Annotation metaAnn : metaAnns) {
|
||||||
|
if (metaAnn.annotationType().getName().equals(annotationType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (Annotation metaMetaAnn : metaAnn.annotationType().getAnnotations()) {
|
||||||
|
if (metaMetaAnn.annotationType().getName().equals(annotationType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
||||||
for (Annotation ann : anns) {
|
for (Annotation ann : anns) {
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
if (ann.annotationType().getName().equals(annotationType)) {
|
||||||
return AnnotationUtils.getAnnotationAttributes(ann, true);
|
return AnnotationUtils.getAnnotationAttributes(ann, true);
|
||||||
}
|
}
|
||||||
}
|
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
|
||||||
return null;
|
if (metaAnn.annotationType().getName().equals(annotationType)) {
|
||||||
}
|
return AnnotationUtils.getAnnotationAttributes(metaAnn, true);
|
||||||
|
|
||||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
|
||||||
for (Annotation ann : anns) {
|
|
||||||
if (ann.annotationType().getName().equals(annotationType)) {
|
|
||||||
Set<String> types = new HashSet<String>();
|
|
||||||
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
|
||||||
for (Annotation meta : metaAnns) {
|
|
||||||
types.add(meta.annotationType().getName());
|
|
||||||
}
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasMetaAnnotation(String annotationType) {
|
|
||||||
Annotation[] anns = getIntrospectedClass().getAnnotations();
|
|
||||||
for (Annotation ann : anns) {
|
|
||||||
Annotation[] metaAnns = ann.annotationType().getAnnotations();
|
|
||||||
for (Annotation meta : metaAnns) {
|
|
||||||
if (meta.annotationType().getName().equals(annotationType)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<MethodMetadata> getAnnotatedMethods() {
|
public Set<MethodMetadata> getAnnotatedMethods() {
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -30,6 +30,7 @@ import org.springframework.asm.Type;
|
||||||
import org.springframework.asm.commons.EmptyVisitor;
|
import org.springframework.asm.commons.EmptyVisitor;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM visitor which looks for the annotations defined on a class or method.
|
* ASM visitor which looks for the annotations defined on a class or method.
|
||||||
|
|
@ -47,7 +48,7 @@ final class AnnotationAttributesReadingVisitor implements AnnotationVisitor {
|
||||||
|
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>();
|
||||||
|
|
||||||
|
|
||||||
public AnnotationAttributesReadingVisitor(
|
public AnnotationAttributesReadingVisitor(
|
||||||
|
|
@ -120,6 +121,7 @@ final class AnnotationAttributesReadingVisitor implements AnnotationVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
|
this.annotationMap.put(this.annotationType, this.attributes);
|
||||||
try {
|
try {
|
||||||
Class<?> annotationClass = this.classLoader.loadClass(this.annotationType);
|
Class<?> annotationClass = this.classLoader.loadClass(this.annotationType);
|
||||||
// Check declared default values of attributes in the annotation type.
|
// Check declared default values of attributes in the annotation type.
|
||||||
|
|
@ -133,10 +135,16 @@ final class AnnotationAttributesReadingVisitor implements AnnotationVisitor {
|
||||||
}
|
}
|
||||||
// Register annotations that the annotation type is annotated with.
|
// Register annotations that the annotation type is annotated with.
|
||||||
if (this.metaAnnotationMap != null) {
|
if (this.metaAnnotationMap != null) {
|
||||||
Annotation[] metaAnnotations = annotationClass.getAnnotations();
|
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>();
|
||||||
Set<String> metaAnnotationTypeNames = new HashSet<String>();
|
for (Annotation metaAnnotation : annotationClass.getAnnotations()) {
|
||||||
for (Annotation metaAnnotation : metaAnnotations) {
|
|
||||||
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName());
|
||||||
|
if (!this.annotationMap.containsKey(metaAnnotation.annotationType().getName())) {
|
||||||
|
this.annotationMap.put(metaAnnotation.annotationType().getName(),
|
||||||
|
AnnotationUtils.getAnnotationAttributes(metaAnnotation, true));
|
||||||
|
}
|
||||||
|
for (Annotation metaMetaAnnotation : metaAnnotation.annotationType().getAnnotations()) {
|
||||||
|
metaAnnotationTypeNames.add(metaMetaAnnotation.annotationType().getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.metaAnnotationMap.put(this.annotationType, metaAnnotationTypeNames);
|
this.metaAnnotationMap.put(this.annotationType, metaAnnotationTypeNames);
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +152,6 @@ final class AnnotationAttributesReadingVisitor implements AnnotationVisitor {
|
||||||
catch (ClassNotFoundException ex) {
|
catch (ClassNotFoundException ex) {
|
||||||
// Class not found - can't determine meta-annotations.
|
// Class not found - can't determine meta-annotations.
|
||||||
}
|
}
|
||||||
this.annotationMap.put(this.annotationType, this.attributes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,12 @@ import org.springframework.core.type.MethodMetadata;
|
||||||
*/
|
*/
|
||||||
final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
|
final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
|
||||||
|
|
||||||
private final Map<String, Map<String, Object>> annotationMap = new LinkedHashMap<String, Map<String, Object>>();
|
private final Set<String> annotationSet = new LinkedHashSet<String>();
|
||||||
|
|
||||||
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
private final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>();
|
||||||
|
|
||||||
|
private final Map<String, Map<String, Object>> attributeMap = new LinkedHashMap<String, Map<String, Object>>();
|
||||||
|
|
||||||
private final Set<MethodMetadata> methodMetadataSet = new LinkedHashSet<MethodMetadata>();
|
private final Set<MethodMetadata> methodMetadataSet = new LinkedHashSet<MethodMetadata>();
|
||||||
|
|
||||||
private final ClassLoader classLoader;
|
private final ClassLoader classLoader;
|
||||||
|
|
@ -63,26 +65,23 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
|
||||||
String className = Type.getType(desc).getClassName();
|
String className = Type.getType(desc).getClassName();
|
||||||
return new AnnotationAttributesReadingVisitor(className, this.annotationMap, this.metaAnnotationMap, this.classLoader);
|
this.annotationSet.add(className);
|
||||||
|
return new AnnotationAttributesReadingVisitor(className, this.attributeMap, this.metaAnnotationMap, this.classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Set<String> getAnnotationTypes() {
|
public Set<String> getAnnotationTypes() {
|
||||||
return this.annotationMap.keySet();
|
return this.annotationSet;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasAnnotation(String annotationType) {
|
|
||||||
return this.annotationMap.containsKey(annotationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
|
||||||
return this.annotationMap.get(annotationType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
public Set<String> getMetaAnnotationTypes(String annotationType) {
|
||||||
return this.metaAnnotationMap.get(annotationType);
|
return this.metaAnnotationMap.get(annotationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasAnnotation(String annotationType) {
|
||||||
|
return this.annotationSet.contains(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
public boolean hasMetaAnnotation(String metaAnnotationType) {
|
||||||
Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
|
Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
|
||||||
for (Set<String> metaTypes : allMetaTypes) {
|
for (Set<String> metaTypes : allMetaTypes) {
|
||||||
|
|
@ -93,6 +92,10 @@ final class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getAnnotationAttributes(String annotationType) {
|
||||||
|
return this.attributeMap.get(annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
public Set<MethodMetadata> getAnnotatedMethods() {
|
public Set<MethodMetadata> getAnnotatedMethods() {
|
||||||
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
|
||||||
for (MethodMetadata method : this.methodMetadataSet) {
|
for (MethodMetadata method : this.methodMetadataSet) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2007 the original author or authors.
|
* Copyright 2002-2009 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.
|
||||||
|
|
@ -77,8 +77,8 @@ public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter
|
||||||
}
|
}
|
||||||
else if (superClassName.startsWith("java.")) {
|
else if (superClassName.startsWith("java.")) {
|
||||||
try {
|
try {
|
||||||
Class clazz = getClass().getClassLoader().loadClass(superClassName);
|
Class<?> clazz = getClass().getClassLoader().loadClass(superClassName);
|
||||||
return Boolean.valueOf(clazz.getAnnotation(this.annotationType) != null);
|
return (clazz.getAnnotation(this.annotationType) != null);
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException ex) {
|
catch (ClassNotFoundException ex) {
|
||||||
// Class not found - can't determine a match that way.
|
// Class not found - can't determine a match that way.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2008 the original author or authors.
|
* Copyright 2002-2009 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.
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.transaction.annotation;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.AnnotatedElement;
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
|
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
|
||||||
|
|
@ -35,6 +36,14 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
|
||||||
|
|
||||||
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
|
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
|
||||||
Transactional ann = ae.getAnnotation(Transactional.class);
|
Transactional ann = ae.getAnnotation(Transactional.class);
|
||||||
|
if (ann == null) {
|
||||||
|
for (Annotation metaAnn : ae.getAnnotations()) {
|
||||||
|
ann = metaAnn.annotationType().getAnnotation(Transactional.class);
|
||||||
|
if (ann != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ann != null) {
|
if (ann != null) {
|
||||||
return parseTransactionAnnotation(ann);
|
return parseTransactionAnnotation(ann);
|
||||||
}
|
}
|
||||||
|
|
@ -51,23 +60,23 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
|
||||||
rbta.setReadOnly(ann.readOnly());
|
rbta.setReadOnly(ann.readOnly());
|
||||||
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
|
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
|
||||||
Class[] rbf = ann.rollbackFor();
|
Class[] rbf = ann.rollbackFor();
|
||||||
for (int i = 0; i < rbf.length; ++i) {
|
for (Class rbRule : rbf) {
|
||||||
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbf[i]);
|
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
|
||||||
rollBackRules.add(rule);
|
rollBackRules.add(rule);
|
||||||
}
|
}
|
||||||
String[] rbfc = ann.rollbackForClassName();
|
String[] rbfc = ann.rollbackForClassName();
|
||||||
for (int i = 0; i < rbfc.length; ++i) {
|
for (String rbRule : rbfc) {
|
||||||
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbfc[i]);
|
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
|
||||||
rollBackRules.add(rule);
|
rollBackRules.add(rule);
|
||||||
}
|
}
|
||||||
Class[] nrbf = ann.noRollbackFor();
|
Class[] nrbf = ann.noRollbackFor();
|
||||||
for (int i = 0; i < nrbf.length; ++i) {
|
for (Class rbRule : nrbf) {
|
||||||
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(nrbf[i]);
|
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
|
||||||
rollBackRules.add(rule);
|
rollBackRules.add(rule);
|
||||||
}
|
}
|
||||||
String[] nrbfc = ann.noRollbackForClassName();
|
String[] nrbfc = ann.noRollbackForClassName();
|
||||||
for (int i = 0; i < nrbfc.length; ++i) {
|
for (String rbRule : nrbfc) {
|
||||||
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(nrbfc[i]);
|
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
|
||||||
rollBackRules.add(rule);
|
rollBackRules.add(rule);
|
||||||
}
|
}
|
||||||
rbta.getRollbackRules().addAll(rollBackRules);
|
rbta.getRollbackRules().addAll(rollBackRules);
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,15 @@ package org.springframework.transaction.annotation;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import javax.ejb.TransactionAttributeType;
|
import javax.ejb.TransactionAttributeType;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.aop.framework.Advised;
|
import org.springframework.aop.framework.Advised;
|
||||||
import org.springframework.aop.framework.ProxyFactory;
|
import org.springframework.aop.framework.ProxyFactory;
|
||||||
|
|
@ -38,8 +42,9 @@ import org.springframework.util.SerializationTestUtils;
|
||||||
* @author Colin Sampaleanu
|
* @author Colin Sampaleanu
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
*/
|
*/
|
||||||
public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
public class AnnotationTransactionAttributeSourceTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSerializable() throws Exception {
|
public void testSerializable() throws Exception {
|
||||||
TestBean1 tb = new TestBean1();
|
TestBean1 tb = new TestBean1();
|
||||||
CallCountingTransactionManager ptm = new CallCountingTransactionManager();
|
CallCountingTransactionManager ptm = new CallCountingTransactionManager();
|
||||||
|
|
@ -63,6 +68,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
assertEquals(2, serializedPtm.commits);
|
assertEquals(2, serializedPtm.commits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testNullOrEmpty() throws Exception {
|
public void testNullOrEmpty() throws Exception {
|
||||||
Method method = Empty.class.getMethod("getAge", (Class[]) null);
|
Method method = Empty.class.getMethod("getAge", (Class[]) null);
|
||||||
|
|
||||||
|
|
@ -77,6 +83,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
* Test the important case where the invocation is on a proxied interface method
|
* Test the important case where the invocation is on a proxied interface method
|
||||||
* but the attribute is defined on the target class.
|
* but the attribute is defined on the target class.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testTransactionAttributeDeclaredOnClassMethod() throws Exception {
|
public void testTransactionAttributeDeclaredOnClassMethod() throws Exception {
|
||||||
Method classMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
|
Method classMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
|
||||||
|
|
||||||
|
|
@ -91,6 +98,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Test case where attribute is on the interface method.
|
* Test case where attribute is on the interface method.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testTransactionAttributeDeclaredOnInterfaceMethodOnly() throws Exception {
|
public void testTransactionAttributeDeclaredOnInterfaceMethodOnly() throws Exception {
|
||||||
Method interfaceMethod = ITestBean2.class.getMethod("getAge", (Class[]) null);
|
Method interfaceMethod = ITestBean2.class.getMethod("getAge", (Class[]) null);
|
||||||
|
|
||||||
|
|
@ -104,6 +112,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Test that when an attribute exists on both class and interface, class takes precedence.
|
* Test that when an attribute exists on both class and interface, class takes precedence.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testTransactionAttributeOnTargetClassMethodOverridesAttributeOnInterfaceMethod() throws Exception {
|
public void testTransactionAttributeOnTargetClassMethodOverridesAttributeOnInterfaceMethod() throws Exception {
|
||||||
Method interfaceMethod = ITestBean3.class.getMethod("getAge", (Class[]) null);
|
Method interfaceMethod = ITestBean3.class.getMethod("getAge", (Class[]) null);
|
||||||
Method interfaceMethod2 = ITestBean3.class.getMethod("getName", (Class[]) null);
|
Method interfaceMethod2 = ITestBean3.class.getMethod("getName", (Class[]) null);
|
||||||
|
|
@ -124,6 +133,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, actual2.getPropagationBehavior());
|
assertEquals(TransactionAttribute.PROPAGATION_REQUIRED, actual2.getPropagationBehavior());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRollbackRulesAreApplied() throws Exception {
|
public void testRollbackRulesAreApplied() throws Exception {
|
||||||
Method method = TestBean3.class.getMethod("getAge", (Class[]) null);
|
Method method = TestBean3.class.getMethod("getAge", (Class[]) null);
|
||||||
|
|
||||||
|
|
@ -153,6 +163,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
* Test that transaction attribute is inherited from class
|
* Test that transaction attribute is inherited from class
|
||||||
* if not specified on method.
|
* if not specified on method.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testDefaultsToClassTransactionAttribute() throws Exception {
|
public void testDefaultsToClassTransactionAttribute() throws Exception {
|
||||||
Method method = TestBean4.class.getMethod("getAge", (Class[]) null);
|
Method method = TestBean4.class.getMethod("getAge", (Class[]) null);
|
||||||
|
|
||||||
|
|
@ -165,6 +176,33 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules());
|
assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomClassAttributeDetected() throws Exception {
|
||||||
|
Method method = TestBean5.class.getMethod("getAge", (Class[]) null);
|
||||||
|
|
||||||
|
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
|
||||||
|
TransactionAttribute actual = atas.getTransactionAttribute(method, TestBean5.class);
|
||||||
|
|
||||||
|
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
|
||||||
|
rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class));
|
||||||
|
rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class));
|
||||||
|
assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomMethodAttributeDetected() throws Exception {
|
||||||
|
Method method = TestBean6.class.getMethod("getAge", (Class[]) null);
|
||||||
|
|
||||||
|
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
|
||||||
|
TransactionAttribute actual = atas.getTransactionAttribute(method, TestBean5.class);
|
||||||
|
|
||||||
|
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
|
||||||
|
rbta.getRollbackRules().add(new RollbackRuleAttribute(Exception.class));
|
||||||
|
rbta.getRollbackRules().add(new NoRollbackRuleAttribute(IOException.class));
|
||||||
|
assertEquals(rbta.getRollbackRules(), ((RuleBasedTransactionAttribute) actual).getRollbackRules());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testTransactionAttributeDeclaredOnClassMethodWithEjb3() throws Exception {
|
public void testTransactionAttributeDeclaredOnClassMethodWithEjb3() throws Exception {
|
||||||
Method getAgeMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
|
Method getAgeMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
|
||||||
Method getNameMethod = ITestBean.class.getMethod("getName", (Class[]) null);
|
Method getNameMethod = ITestBean.class.getMethod("getName", (Class[]) null);
|
||||||
|
|
@ -176,6 +214,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior());
|
assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testTransactionAttributeDeclaredOnClassWithEjb3() throws Exception {
|
public void testTransactionAttributeDeclaredOnClassWithEjb3() throws Exception {
|
||||||
Method getAgeMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
|
Method getAgeMethod = ITestBean.class.getMethod("getAge", (Class[]) null);
|
||||||
Method getNameMethod = ITestBean.class.getMethod("getName", (Class[]) null);
|
Method getNameMethod = ITestBean.class.getMethod("getName", (Class[]) null);
|
||||||
|
|
@ -187,6 +226,7 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior());
|
assertEquals(TransactionAttribute.PROPAGATION_SUPPORTS, getNameAttr.getPropagationBehavior());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testTransactionAttributeDeclaredOnInterfaceWithEjb3() throws Exception {
|
public void testTransactionAttributeDeclaredOnInterfaceWithEjb3() throws Exception {
|
||||||
Method getAgeMethod = ITestEjb.class.getMethod("getAge", (Class[]) null);
|
Method getAgeMethod = ITestEjb.class.getMethod("getAge", (Class[]) null);
|
||||||
Method getNameMethod = ITestEjb.class.getMethod("getName", (Class[]) null);
|
Method getNameMethod = ITestEjb.class.getMethod("getName", (Class[]) null);
|
||||||
|
|
@ -401,6 +441,31 @@ public class AnnotationTransactionAttributeSourceTests extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Transactional(rollbackFor=Exception.class, noRollbackFor={IOException.class})
|
||||||
|
public @interface Tx {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Tx
|
||||||
|
public static class TestBean5 {
|
||||||
|
|
||||||
|
public int getAge() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class TestBean6 {
|
||||||
|
|
||||||
|
@Tx
|
||||||
|
public int getAge() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static interface Foo<T> {
|
public static interface Foo<T> {
|
||||||
|
|
||||||
void doSomething(T theArgument);
|
void doSomething(T theArgument);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue