From f2e4ad2364941bdae8fd58e79d44a0faa31ad631 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Aug 2014 15:39:06 +0200 Subject: [PATCH] ComponentScan annotation allows for registering beans with lazy initialization Issue: SPR-10459 --- .../ClassPathBeanDefinitionScanner.java | 10 +++++- .../context/annotation/ComponentScan.java | 9 ++++- .../ComponentScanAnnotationParser.java | 7 +++- ...mponentScanAnnotationIntegrationTests.java | 34 ++++++++++--------- .../ComponentScanAnnotationTests.java | 17 ++++++---- .../annotation/ComponentScanParserTests.java | 9 ++--- 6 files changed, 57 insertions(+), 29 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java index 0c53a93b9e..1fb174bd81 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -163,6 +163,14 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo (beanDefinitionDefaults != null ? beanDefinitionDefaults : new BeanDefinitionDefaults()); } + /** + * Return the defaults to use for detected beans (never {@code null}). + * @since 4.1 + */ + public BeanDefinitionDefaults getBeanDefinitionDefaults() { + return this.beanDefinitionDefaults; + } + /** * Set the name-matching patterns for determining autowire candidates. * @param autowireCandidatePatterns the patterns to match against diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java index 10529a50c7..d855d90d81 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -132,6 +132,13 @@ public @interface ComponentScan { */ Filter[] excludeFilters() default {}; + /** + * Specify whether scanned beans should be registered for lazy initialization. + *

Default is {@code false}; switch this to {@code true} when desired. + * @since 4.1 + */ + boolean lazyInit() default false; + /** * Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters() diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java index 559645800a..0f6f9d1a37 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -106,6 +106,11 @@ class ComponentScanAnnotationParser { } } + boolean lazyInit = componentScan.getBoolean("lazyInit"); + if (lazyInit) { + scanner.getBeanDefinitionDefaults().setLazyInit(true); + } + List basePackages = new ArrayList(); for (String pkg : componentScan.getStringArray("value")) { if (StringUtils.hasText(pkg)) { diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java index b913d281b3..e228b0d758 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java @@ -23,7 +23,17 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.HashSet; +import example.scannable.CustomComponent; +import example.scannable.CustomStereotype; +import example.scannable.DefaultNamedComponent; +import example.scannable.FooService; +import example.scannable.MessageBean; +import example.scannable.ScopedProxyTestBean; +import example.scannable_implicitbasepackage.ComponentScanAnnotatedConfigWithImplicitBasePackage; +import example.scannable_scoped.CustomScopeAnnotationBean; +import example.scannable_scoped.MyScope; import org.junit.Test; + import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.CustomAutowireConfigurer; import org.springframework.beans.factory.config.BeanDefinition; @@ -36,16 +46,6 @@ import org.springframework.context.support.GenericApplicationContext; import org.springframework.tests.context.SimpleMapScope; import org.springframework.util.SerializationTestUtils; -import example.scannable.CustomComponent; -import example.scannable.CustomStereotype; -import example.scannable.DefaultNamedComponent; -import example.scannable.FooService; -import example.scannable.MessageBean; -import example.scannable.ScopedProxyTestBean; -import example.scannable_implicitbasepackage.ComponentScanAnnotatedConfigWithImplicitBasePackage; -import example.scannable_scoped.CustomScopeAnnotationBean; -import example.scannable_scoped.MyScope; - import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*; @@ -116,10 +116,10 @@ public class ComponentScanAnnotationIntegrationTests { ctx.getBean(ComposedAnnotationConfig.class); ctx.getBean(SimpleComponent.class); assertThat("config class bean not found", - ctx.containsBeanDefinition("componentScanAnnotationIntegrationTests.ComposedAnnotationConfig"), is(true)); - assertThat("@ComponentScan annotated @Configuration class registered directly against " - + "AnnotationConfigApplicationContext did not trigger component scanning as expected", - ctx.containsBean("simpleComponent"), is(true)); + ctx.containsBeanDefinition("componentScanAnnotationIntegrationTests.ComposedAnnotationConfig"), is(true)); + assertThat("@ComponentScan annotated @Configuration class registered directly against " + + "AnnotationConfigApplicationContext did not trigger component scanning as expected", + ctx.containsBean("simpleComponent"), is(true)); } @Test @@ -159,7 +159,8 @@ public class ComponentScanAnnotationIntegrationTests { @Test public void withCustomTypeFilter() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithCustomTypeFilter.class); - KustomAnnotationAutowiredBean testBean = ctx.getBean(KustomAnnotationAutowiredBean.class); + assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("kustomBean")); + KustomAnnotationAutowiredBean testBean = ctx.getBean("kustomBean", KustomAnnotationAutowiredBean.class); assertThat(testBean.getDependency(), notNullValue()); } @@ -294,7 +295,8 @@ class MyScopeMetadataResolver extends AnnotationScopeMetadataResolver { useDefaultFilters=false, includeFilters=@Filter(type=FilterType.CUSTOM, value=ComponentScanParserTests.CustomTypeFilter.class), // exclude this class from scanning since it's in the scanned package - excludeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=ComponentScanWithCustomTypeFilter.class)) + excludeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=ComponentScanWithCustomTypeFilter.class), + lazyInit = true) class ComponentScanWithCustomTypeFilter { @Bean @SuppressWarnings({ "rawtypes", "serial", "unchecked" }) diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationTests.java index f686a69bdc..ec17007cdc 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.context.annotation; import org.junit.Test; + import org.springframework.beans.factory.support.DefaultBeanNameGenerator; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.core.type.filter.TypeFilter; @@ -29,6 +30,7 @@ import org.springframework.core.type.filter.TypeFilter; * @see ComponentScanAnnotationIntegrationTests */ public class ComponentScanAnnotationTests { + @Test public void noop() { // no-op; the @ComponentScan-annotated MyConfig class below simply excercises @@ -36,7 +38,9 @@ public class ComponentScanAnnotationTests { } } -@interface MyAnnotation { } + +@interface MyAnnotation { +} @Configuration @ComponentScan( @@ -44,18 +48,19 @@ public class ComponentScanAnnotationTests { nameGenerator = DefaultBeanNameGenerator.class, scopedProxy = ScopedProxyMode.NO, scopeResolver = AnnotationScopeMetadataResolver.class, - useDefaultFilters = false, resourcePattern = "**/*custom.class", + useDefaultFilters = false, includeFilters = { @Filter(type = FilterType.ANNOTATION, value = MyAnnotation.class) }, excludeFilters = { @Filter(type = FilterType.CUSTOM, value = TypeFilter.class) - } + }, + lazyInit = true ) class MyConfig { - } @ComponentScan(basePackageClasses=example.scannable.NamedComponent.class) -class SimpleConfig { } \ No newline at end of file +class SimpleConfig { +} diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserTests.java index 8b1068b65f..230cf600d3 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserTests.java @@ -21,7 +21,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import example.profilescan.ProfileAnnotatedComponent; +import example.scannable.AutowiredQualifierFooService; import org.junit.Test; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -29,9 +32,7 @@ import org.springframework.context.support.GenericXmlApplicationContext; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; - -import example.profilescan.ProfileAnnotatedComponent; -import example.scannable.AutowiredQualifierFooService; +import org.springframework.stereotype.Component; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -134,13 +135,13 @@ public class ComponentScanParserTests { * Intentionally spelling "custom" with a "k" since there are numerous * classes in this package named *Custom*. */ + @Component("kustomBean") public static class KustomAnnotationAutowiredBean { @Autowired @CustomAnnotation private KustomAnnotationDependencyBean dependency; - public KustomAnnotationDependencyBean getDependency() { return this.dependency; }