View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.context.annotation;
18  
19  import java.io.IOException;
20  import java.lang.annotation.ElementType;
21  import java.lang.annotation.Retention;
22  import java.lang.annotation.RetentionPolicy;
23  import java.lang.annotation.Target;
24  import java.util.HashSet;
25  
26  import example.scannable.CustomComponent;
27  import example.scannable.CustomStereotype;
28  import example.scannable.DefaultNamedComponent;
29  import example.scannable.FooService;
30  import example.scannable.MessageBean;
31  import example.scannable.ScopedProxyTestBean;
32  import example.scannable_implicitbasepackage.ComponentScanAnnotatedConfigWithImplicitBasePackage;
33  import example.scannable_scoped.CustomScopeAnnotationBean;
34  import example.scannable_scoped.MyScope;
35  import org.junit.Test;
36  
37  import org.springframework.aop.support.AopUtils;
38  import org.springframework.beans.factory.annotation.CustomAutowireConfigurer;
39  import org.springframework.beans.factory.config.BeanDefinition;
40  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
41  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
42  import org.springframework.context.annotation.ComponentScan.Filter;
43  import org.springframework.context.annotation.ComponentScanParserTests.KustomAnnotationAutowiredBean;
44  import org.springframework.context.annotation.componentscan.simple.ClassWithNestedComponents;
45  import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
46  import org.springframework.context.support.GenericApplicationContext;
47  import org.springframework.tests.context.SimpleMapScope;
48  import org.springframework.util.SerializationTestUtils;
49  
50  import static org.hamcrest.CoreMatchers.*;
51  import static org.junit.Assert.*;
52  import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
53  
54  /**
55   * Integration tests for processing ComponentScan-annotated Configuration classes.
56   *
57   * @author Chris Beams
58   * @author Juergen Hoeller
59   * @author Sam Brannen
60   * @since 3.1
61   */
62  @SuppressWarnings("resource")
63  public class ComponentScanAnnotationIntegrationTests {
64  
65  	@Test
66  	public void controlScan() {
67  		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
68  		ctx.scan(example.scannable._package.class.getPackage().getName());
69  		ctx.refresh();
70  		assertThat("control scan for example.scannable package failed to register FooServiceImpl bean",
71  				ctx.containsBean("fooServiceImpl"), is(true));
72  	}
73  
74  	@Test
75  	public void viaContextRegistration() {
76  		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
77  		ctx.register(ComponentScanAnnotatedConfig.class);
78  		ctx.refresh();
79  		ctx.getBean(ComponentScanAnnotatedConfig.class);
80  		ctx.getBean(TestBean.class);
81  		assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig"), is(true));
82  		assertThat("@ComponentScan annotated @Configuration class registered directly against " +
83  				"AnnotationConfigApplicationContext did not trigger component scanning as expected",
84  				ctx.containsBean("fooServiceImpl"), is(true));
85  	}
86  
87  	@Test
88  	public void viaContextRegistration_WithValueAttribute() {
89  		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
90  		ctx.register(ComponentScanAnnotatedConfig_WithValueAttribute.class);
91  		ctx.refresh();
92  		ctx.getBean(ComponentScanAnnotatedConfig_WithValueAttribute.class);
93  		ctx.getBean(TestBean.class);
94  		assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig_WithValueAttribute"), is(true));
95  		assertThat("@ComponentScan annotated @Configuration class registered directly against " +
96  				"AnnotationConfigApplicationContext did not trigger component scanning as expected",
97  				ctx.containsBean("fooServiceImpl"), is(true));
98  	}
99  
100 	@Test
101 	public void viaContextRegistration_FromPackageOfConfigClass() {
102 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
103 		ctx.register(ComponentScanAnnotatedConfigWithImplicitBasePackage.class);
104 		ctx.refresh();
105 		ctx.getBean(ComponentScanAnnotatedConfigWithImplicitBasePackage.class);
106 		assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfigWithImplicitBasePackage"), is(true));
107 		assertThat("@ComponentScan annotated @Configuration class registered directly against " +
108 				"AnnotationConfigApplicationContext did not trigger component scanning as expected",
109 				ctx.containsBean("scannedComponent"), is(true));
110 	}
111 
112 	@Test
113 	public void viaContextRegistration_WithComposedAnnotation() {
114 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
115 		ctx.register(ComposedAnnotationConfig.class);
116 		ctx.refresh();
117 		ctx.getBean(ComposedAnnotationConfig.class);
118 		ctx.getBean(SimpleComponent.class);
119 		ctx.getBean(ClassWithNestedComponents.NestedComponent.class);
120 		ctx.getBean(ClassWithNestedComponents.OtherNestedComponent.class);
121 		assertThat("config class bean not found",
122 				ctx.containsBeanDefinition("componentScanAnnotationIntegrationTests.ComposedAnnotationConfig"), is(true));
123 		assertThat("@ComponentScan annotated @Configuration class registered directly against " +
124 						"AnnotationConfigApplicationContext did not trigger component scanning as expected",
125 				ctx.containsBean("simpleComponent"), is(true));
126 	}
127 
128 	@Test
129 	public void viaBeanRegistration() {
130 		DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
131 		bf.registerBeanDefinition("componentScanAnnotatedConfig",
132 				genericBeanDefinition(ComponentScanAnnotatedConfig.class).getBeanDefinition());
133 		bf.registerBeanDefinition("configurationClassPostProcessor",
134 				genericBeanDefinition(ConfigurationClassPostProcessor.class).getBeanDefinition());
135 		GenericApplicationContext ctx = new GenericApplicationContext(bf);
136 		ctx.refresh();
137 		ctx.getBean(ComponentScanAnnotatedConfig.class);
138 		ctx.getBean(TestBean.class);
139 		assertThat("config class bean not found", ctx.containsBeanDefinition("componentScanAnnotatedConfig"), is(true));
140 		assertThat("@ComponentScan annotated @Configuration class registered " +
141 				"as bean definition did not trigger component scanning as expected",
142 				ctx.containsBean("fooServiceImpl"), is(true));
143 	}
144 
145 	@Test
146 	public void withCustomBeanNameGenerator() {
147 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
148 		ctx.register(ComponentScanWithBeanNameGenenerator.class);
149 		ctx.refresh();
150 		assertThat(ctx.containsBean("custom_fooServiceImpl"), is(true));
151 		assertThat(ctx.containsBean("fooServiceImpl"), is(false));
152 	}
153 
154 	@Test
155 	public void withScopeResolver() {
156 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithScopeResolver.class);
157 		// custom scope annotation makes the bean prototype scoped. subsequent calls
158 		// to getBean should return distinct instances.
159 		assertThat(ctx.getBean(CustomScopeAnnotationBean.class), not(sameInstance(ctx.getBean(CustomScopeAnnotationBean.class))));
160 	}
161 
162 	@Test
163 	public void withCustomTypeFilter() {
164 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanWithCustomTypeFilter.class);
165 		assertFalse(ctx.getDefaultListableBeanFactory().containsSingleton("componentScanParserTests.KustomAnnotationAutowiredBean"));
166 		KustomAnnotationAutowiredBean testBean = ctx.getBean("componentScanParserTests.KustomAnnotationAutowiredBean", KustomAnnotationAutowiredBean.class);
167 		assertThat(testBean.getDependency(), notNullValue());
168 	}
169 
170 	@Test
171 	public void withScopedProxy() throws IOException, ClassNotFoundException {
172 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
173 		ctx.register(ComponentScanWithScopedProxy.class);
174 		ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope());
175 		ctx.refresh();
176 		// should cast to the interface
177 		FooService bean = (FooService) ctx.getBean("scopedProxyTestBean");
178 		// should be dynamic proxy
179 		assertThat(AopUtils.isJdkDynamicProxy(bean), is(true));
180 		// test serializability
181 		assertThat(bean.foo(1), equalTo("bar"));
182 		FooService deserialized = (FooService) SerializationTestUtils.serializeAndDeserialize(bean);
183 		assertThat(deserialized, notNullValue());
184 		assertThat(deserialized.foo(1), equalTo("bar"));
185 	}
186 
187 	@Test
188 	public void withScopedProxyThroughRegex() throws IOException, ClassNotFoundException {
189 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
190 		ctx.register(ComponentScanWithScopedProxyThroughRegex.class);
191 		ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope());
192 		ctx.refresh();
193 		// should cast to the interface
194 		FooService bean = (FooService) ctx.getBean("scopedProxyTestBean");
195 		// should be dynamic proxy
196 		assertThat(AopUtils.isJdkDynamicProxy(bean), is(true));
197 	}
198 
199 	@Test
200 	public void withScopedProxyThroughAspectJPattern() throws IOException, ClassNotFoundException {
201 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
202 		ctx.register(ComponentScanWithScopedProxyThroughAspectJPattern.class);
203 		ctx.getBeanFactory().registerScope("myScope", new SimpleMapScope());
204 		ctx.refresh();
205 		// should cast to the interface
206 		FooService bean = (FooService) ctx.getBean("scopedProxyTestBean");
207 		// should be dynamic proxy
208 		assertThat(AopUtils.isJdkDynamicProxy(bean), is(true));
209 	}
210 
211 	@Test
212 	public void withMultipleAnnotationIncludeFilters1() throws IOException, ClassNotFoundException {
213 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
214 		ctx.register(ComponentScanWithMultipleAnnotationIncludeFilters1.class);
215 		ctx.refresh();
216 		ctx.getBean(DefaultNamedComponent.class); // @CustomStereotype-annotated
217 		ctx.getBean(MessageBean.class);           // @CustomComponent-annotated
218 	}
219 
220 	@Test
221 	public void withMultipleAnnotationIncludeFilters2() throws IOException, ClassNotFoundException {
222 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
223 		ctx.register(ComponentScanWithMultipleAnnotationIncludeFilters2.class);
224 		ctx.refresh();
225 		ctx.getBean(DefaultNamedComponent.class); // @CustomStereotype-annotated
226 		ctx.getBean(MessageBean.class);           // @CustomComponent-annotated
227 	}
228 
229 	@Test
230 	public void withBasePackagesAndValueAlias() {
231 		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
232 		ctx.register(ComponentScanWithBasePackagesAndValueAlias.class);
233 		ctx.refresh();
234 		assertThat(ctx.containsBean("fooServiceImpl"), is(true));
235 	}
236 
237 
238 	@Configuration
239 	@ComponentScan
240 	@Retention(RetentionPolicy.RUNTIME)
241 	@Target(ElementType.TYPE)
242 	public static @interface ComposedConfiguration {
243 		String[] basePackages() default {};
244 	}
245 
246 	@ComposedConfiguration(basePackages = "org.springframework.context.annotation.componentscan.simple")
247 	public static class ComposedAnnotationConfig {
248 	}
249 
250 }
251 
252 
253 @Configuration
254 @ComponentScan(basePackageClasses=example.scannable._package.class)
255 class ComponentScanAnnotatedConfig {
256 	@Bean
257 	public TestBean testBean() {
258 		return new TestBean();
259 	}
260 }
261 
262 @Configuration
263 @ComponentScan("example.scannable")
264 class ComponentScanAnnotatedConfig_WithValueAttribute {
265 	@Bean
266 	public TestBean testBean() {
267 		return new TestBean();
268 	}
269 }
270 
271 @Configuration
272 @ComponentScan
273 class ComponentScanWithNoPackagesConfig {}
274 
275 @Configuration
276 @ComponentScan(basePackages="example.scannable", nameGenerator=MyBeanNameGenerator.class)
277 class ComponentScanWithBeanNameGenenerator {}
278 
279 class MyBeanNameGenerator extends AnnotationBeanNameGenerator {
280 	@Override
281 	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
282 		return "custom_" + super.generateBeanName(definition, registry);
283 	}
284 }
285 
286 @Configuration
287 @ComponentScan(basePackages="example.scannable_scoped", scopeResolver=MyScopeMetadataResolver.class)
288 class ComponentScanWithScopeResolver {}
289 
290 class MyScopeMetadataResolver extends AnnotationScopeMetadataResolver {
291 	MyScopeMetadataResolver() {
292 		this.scopeAnnotationType = MyScope.class;
293 	}
294 }
295 
296 @Configuration
297 @ComponentScan(basePackages="org.springframework.context.annotation",
298 		useDefaultFilters=false,
299 		includeFilters=@Filter(type=FilterType.CUSTOM, value=ComponentScanParserTests.CustomTypeFilter.class),
300 		// exclude this class from scanning since it's in the scanned package
301 		excludeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=ComponentScanWithCustomTypeFilter.class),
302 		lazyInit = true)
303 class ComponentScanWithCustomTypeFilter {
304 	@Bean
305 	@SuppressWarnings({ "rawtypes", "serial", "unchecked" })
306 	public static CustomAutowireConfigurer customAutowireConfigurer() {
307 		CustomAutowireConfigurer cac = new CustomAutowireConfigurer();
308 		cac.setCustomQualifierTypes(new HashSet() {{ add(ComponentScanParserTests.CustomAnnotation.class); }});
309 		return cac;
310 	}
311 
312 	public ComponentScanParserTests.KustomAnnotationAutowiredBean testBean() {
313 		return new ComponentScanParserTests.KustomAnnotationAutowiredBean();
314 	}
315 }
316 
317 @Configuration
318 @ComponentScan(basePackages="example.scannable",
319 		scopedProxy=ScopedProxyMode.INTERFACES,
320 		useDefaultFilters=false,
321 		includeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE, value=ScopedProxyTestBean.class))
322 class ComponentScanWithScopedProxy {}
323 
324 @Configuration
325 @ComponentScan(basePackages="example.scannable",
326 		scopedProxy=ScopedProxyMode.INTERFACES,
327 		useDefaultFilters=false,
328 		includeFilters=@Filter(type=FilterType.REGEX, pattern ="((?:[a-z.]+))ScopedProxyTestBean"))
329 class ComponentScanWithScopedProxyThroughRegex {}
330 
331 @Configuration
332 @ComponentScan(basePackages="example.scannable",
333 		scopedProxy=ScopedProxyMode.INTERFACES,
334 		useDefaultFilters=false,
335 		includeFilters=@Filter(type=FilterType.ASPECTJ, pattern ="*..ScopedProxyTestBean"))
336 class ComponentScanWithScopedProxyThroughAspectJPattern {}
337 
338 @Configuration
339 @ComponentScan(basePackages="example.scannable",
340 		useDefaultFilters=false,
341 		includeFilters={
342 			@Filter(CustomStereotype.class),
343 			@Filter(CustomComponent.class)
344 		}
345 	)
346 class ComponentScanWithMultipleAnnotationIncludeFilters1 {}
347 
348 @Configuration
349 @ComponentScan(basePackages="example.scannable",
350 		useDefaultFilters=false,
351 		includeFilters=@Filter({CustomStereotype.class, CustomComponent.class})
352 	)
353 class ComponentScanWithMultipleAnnotationIncludeFilters2 {}
354 
355 @Configuration
356 @ComponentScan(
357 		value="example.scannable",
358 		basePackages="example.scannable",
359 		basePackageClasses=example.scannable._package.class)
360 class ComponentScanWithBasePackagesAndValueAlias {}