13
13
import org .springframework .context .ConfigurableApplicationContext ;
14
14
import org .springframework .context .support .ClassPathXmlApplicationContext ;
15
15
import org .springframework .context .support .GenericApplicationContext ;
16
+ import org .springframework .stereotype .Component ;
16
17
import org .springframework .test .context .ContextConfiguration ;
17
18
import org .springframework .test .context .ContextHierarchy ;
18
19
import org .springframework .test .context .TestContextManager ;
19
20
20
21
import java .lang .annotation .Annotation ;
21
22
import java .util .Collection ;
22
23
import java .util .HashSet ;
24
+ import java .util .Set ;
25
+ import java .util .Stack ;
23
26
24
27
/**
25
28
* Spring based implementation of ObjectFactory.
26
29
* <p/>
27
30
* <p>
28
31
* <ul>
29
32
* <li>It uses TestContextManager to manage the spring context.
30
- * Configuration via: @ContextConfiguration or @ContextHierarcy
31
- * At least on step definition class needs to have a @ContextConfiguration or
32
- * @ContextHierarchy annotation. If more that one step definition class has such
33
+ * Configuration via: @{@link ContextConfiguration} or @{@link ContextHierarchy}
34
+ * At least one step definition class needs to have a @ContextConfiguration
35
+ * or @ContextHierarchy annotation. If more that one step definition class has such
33
36
* an annotation, the annotations must be equal on the different step definition
34
- * classes. If no step definition class with @ContextConfiguration or
35
- * @ContextHierarcy is found, it will try to load cucumber.xml from the classpath.
37
+ * classes. If no step definition class with @ContextConfiguration or @ContextHierarchy
38
+ * is found, it will try to load cucumber.xml from the classpath.
36
39
* </li>
37
40
* <li>The step definitions class with @ContextConfiguration or @ContextHierarchy
38
- * annotation, may also have a @WebAppConfiguration or @DirtiesContext annotation.
41
+ * annotation, may also have a @{@link org.springframework.test.context.web.WebAppConfiguration}
42
+ * or @{@link org.springframework.test.annotation.DirtiesContext} annotation.
39
43
* </li>
40
- * <li>The step definitions added to the TestContextManagers context and
44
+ * <li>The step definitions are added to the TestContextManagers context and
41
45
* is reloaded for each scenario.</li>
46
+ * <li>Step definitions should not be annotated with @{@link Component} or
47
+ * other annotations that mark it as eligible for detection by classpath scanning.</li>
48
+ * <li>When a step definition class is annotated by @Component or an annotation that has the @Component stereotype an
49
+ * exception will be thrown</li>
50
+ * </li>
42
51
* </ul>
43
- * </p>
44
52
* <p/>
45
53
* <p>
46
54
* Application beans are accessible from the step definitions using autowiring
@@ -61,6 +69,7 @@ public SpringFactory() {
61
69
@ Override
62
70
public boolean addClass (final Class <?> stepClass ) {
63
71
if (!stepClasses .contains (stepClass )) {
72
+ checkNoComponentAnnotations (stepClass );
64
73
if (dependsOnSpringContext (stepClass )) {
65
74
if (stepClassWithSpringContext == null ) {
66
75
stepClassWithSpringContext = stepClass ;
@@ -73,6 +82,42 @@ public boolean addClass(final Class<?> stepClass) {
73
82
return true ;
74
83
}
75
84
85
+ private static void checkNoComponentAnnotations (Class <?> type ) {
86
+ for (Annotation annotation : type .getAnnotations ()) {
87
+ if (hasComponentStereoType (annotation )) {
88
+ throw new CucumberException (String .format ("" +
89
+ "Glue class %1$s was annotated with @%2$s; marking it as a candidate for auto-detection by " +
90
+ "Spring. Glue classes are detected and registered by Cucumber. Auto-detection of glue classes by " +
91
+ "spring may lead to duplicate bean definitions. Please remove the @%2$s annotation" ,
92
+ type .getName (),
93
+ annotation .annotationType ().getSimpleName ()));
94
+ }
95
+ }
96
+ }
97
+
98
+ private static boolean hasComponentStereoType (Annotation annotation ) {
99
+ Set <Class <? extends Annotation >> seen = new HashSet <Class <? extends Annotation >>();
100
+ Stack <Class <? extends Annotation >> toCheck = new Stack <Class <? extends Annotation >>();
101
+ toCheck .add (annotation .annotationType ());
102
+
103
+ while (!toCheck .isEmpty ()) {
104
+ Class <? extends Annotation > annotationType = toCheck .pop ();
105
+ if (Component .class .equals (annotationType )) {
106
+ return true ;
107
+ }
108
+
109
+ seen .add (annotationType );
110
+ for (Annotation annotationTypesAnnotations : annotationType .getAnnotations ()) {
111
+ if (!seen .contains (annotationTypesAnnotations .annotationType ())) {
112
+ toCheck .add (annotationTypesAnnotations .annotationType ());
113
+ }
114
+ }
115
+
116
+ }
117
+ return false ;
118
+ }
119
+
120
+
76
121
private void checkAnnotationsEqual (Class <?> stepClassWithSpringContext , Class <?> stepClass ) {
77
122
Annotation [] annotations1 = stepClassWithSpringContext .getAnnotations ();
78
123
Annotation [] annotations2 = stepClass .getAnnotations ();
0 commit comments