001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.annotation; 018 019import java.lang.annotation.Annotation; 020import java.util.Set; 021import java.util.regex.Pattern; 022 023import org.w3c.dom.Element; 024import org.w3c.dom.Node; 025import org.w3c.dom.NodeList; 026 027import org.springframework.beans.BeanUtils; 028import org.springframework.beans.factory.config.BeanDefinition; 029import org.springframework.beans.factory.config.BeanDefinitionHolder; 030import org.springframework.beans.factory.parsing.BeanComponentDefinition; 031import org.springframework.beans.factory.parsing.CompositeComponentDefinition; 032import org.springframework.beans.factory.support.BeanNameGenerator; 033import org.springframework.beans.factory.xml.BeanDefinitionParser; 034import org.springframework.beans.factory.xml.ParserContext; 035import org.springframework.beans.factory.xml.XmlReaderContext; 036import org.springframework.context.ConfigurableApplicationContext; 037import org.springframework.core.type.filter.AnnotationTypeFilter; 038import org.springframework.core.type.filter.AspectJTypeFilter; 039import org.springframework.core.type.filter.AssignableTypeFilter; 040import org.springframework.core.type.filter.RegexPatternTypeFilter; 041import org.springframework.core.type.filter.TypeFilter; 042import org.springframework.lang.Nullable; 043import org.springframework.util.ClassUtils; 044import org.springframework.util.ReflectionUtils; 045import org.springframework.util.StringUtils; 046 047/** 048 * Parser for the {@code <context:component-scan/>} element. 049 * 050 * @author Mark Fisher 051 * @author Ramnivas Laddad 052 * @author Juergen Hoeller 053 * @since 2.5 054 */ 055public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser { 056 057 private static final String BASE_PACKAGE_ATTRIBUTE = "base-package"; 058 059 private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern"; 060 061 private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters"; 062 063 private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config"; 064 065 private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator"; 066 067 private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver"; 068 069 private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy"; 070 071 private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter"; 072 073 private static final String INCLUDE_FILTER_ELEMENT = "include-filter"; 074 075 private static final String FILTER_TYPE_ATTRIBUTE = "type"; 076 077 private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression"; 078 079 080 @Override 081 @Nullable 082 public BeanDefinition parse(Element element, ParserContext parserContext) { 083 String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); 084 basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); 085 String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, 086 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); 087 088 // Actually scan for bean definitions and register them. 089 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); 090 Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); 091 registerComponents(parserContext.getReaderContext(), beanDefinitions, element); 092 093 return null; 094 } 095 096 protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { 097 boolean useDefaultFilters = true; 098 if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) { 099 useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)); 100 } 101 102 // Delegate bean definition registration to scanner class. 103 ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters); 104 scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults()); 105 scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns()); 106 107 if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) { 108 scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE)); 109 } 110 111 try { 112 parseBeanNameGenerator(element, scanner); 113 } 114 catch (Exception ex) { 115 parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause()); 116 } 117 118 try { 119 parseScope(element, scanner); 120 } 121 catch (Exception ex) { 122 parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause()); 123 } 124 125 parseTypeFilters(element, scanner, parserContext); 126 127 return scanner; 128 } 129 130 protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) { 131 return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters, 132 readerContext.getEnvironment(), readerContext.getResourceLoader()); 133 } 134 135 protected void registerComponents( 136 XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) { 137 138 Object source = readerContext.extractSource(element); 139 CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source); 140 141 for (BeanDefinitionHolder beanDefHolder : beanDefinitions) { 142 compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder)); 143 } 144 145 // Register annotation config processors, if necessary. 146 boolean annotationConfig = true; 147 if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) { 148 annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE)); 149 } 150 if (annotationConfig) { 151 Set<BeanDefinitionHolder> processorDefinitions = 152 AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source); 153 for (BeanDefinitionHolder processorDefinition : processorDefinitions) { 154 compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition)); 155 } 156 } 157 158 readerContext.fireComponentRegistered(compositeDef); 159 } 160 161 protected void parseBeanNameGenerator(Element element, ClassPathBeanDefinitionScanner scanner) { 162 if (element.hasAttribute(NAME_GENERATOR_ATTRIBUTE)) { 163 BeanNameGenerator beanNameGenerator = (BeanNameGenerator) instantiateUserDefinedStrategy( 164 element.getAttribute(NAME_GENERATOR_ATTRIBUTE), BeanNameGenerator.class, 165 scanner.getResourceLoader().getClassLoader()); 166 scanner.setBeanNameGenerator(beanNameGenerator); 167 } 168 } 169 170 protected void parseScope(Element element, ClassPathBeanDefinitionScanner scanner) { 171 // Register ScopeMetadataResolver if class name provided. 172 if (element.hasAttribute(SCOPE_RESOLVER_ATTRIBUTE)) { 173 if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) { 174 throw new IllegalArgumentException( 175 "Cannot define both 'scope-resolver' and 'scoped-proxy' on <component-scan> tag"); 176 } 177 ScopeMetadataResolver scopeMetadataResolver = (ScopeMetadataResolver) instantiateUserDefinedStrategy( 178 element.getAttribute(SCOPE_RESOLVER_ATTRIBUTE), ScopeMetadataResolver.class, 179 scanner.getResourceLoader().getClassLoader()); 180 scanner.setScopeMetadataResolver(scopeMetadataResolver); 181 } 182 183 if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) { 184 String mode = element.getAttribute(SCOPED_PROXY_ATTRIBUTE); 185 if ("targetClass".equals(mode)) { 186 scanner.setScopedProxyMode(ScopedProxyMode.TARGET_CLASS); 187 } 188 else if ("interfaces".equals(mode)) { 189 scanner.setScopedProxyMode(ScopedProxyMode.INTERFACES); 190 } 191 else if ("no".equals(mode)) { 192 scanner.setScopedProxyMode(ScopedProxyMode.NO); 193 } 194 else { 195 throw new IllegalArgumentException("scoped-proxy only supports 'no', 'interfaces' and 'targetClass'"); 196 } 197 } 198 } 199 200 protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) { 201 // Parse exclude and include filter elements. 202 ClassLoader classLoader = scanner.getResourceLoader().getClassLoader(); 203 NodeList nodeList = element.getChildNodes(); 204 for (int i = 0; i < nodeList.getLength(); i++) { 205 Node node = nodeList.item(i); 206 if (node.getNodeType() == Node.ELEMENT_NODE) { 207 String localName = parserContext.getDelegate().getLocalName(node); 208 try { 209 if (INCLUDE_FILTER_ELEMENT.equals(localName)) { 210 TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext); 211 scanner.addIncludeFilter(typeFilter); 212 } 213 else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) { 214 TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext); 215 scanner.addExcludeFilter(typeFilter); 216 } 217 } 218 catch (ClassNotFoundException ex) { 219 parserContext.getReaderContext().warning( 220 "Ignoring non-present type filter class: " + ex, parserContext.extractSource(element)); 221 } 222 catch (Exception ex) { 223 parserContext.getReaderContext().error( 224 ex.getMessage(), parserContext.extractSource(element), ex.getCause()); 225 } 226 } 227 } 228 } 229 230 @SuppressWarnings("unchecked") 231 protected TypeFilter createTypeFilter(Element element, @Nullable ClassLoader classLoader, 232 ParserContext parserContext) throws ClassNotFoundException { 233 234 String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE); 235 String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE); 236 expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression); 237 if ("annotation".equals(filterType)) { 238 return new AnnotationTypeFilter((Class<Annotation>) ClassUtils.forName(expression, classLoader)); 239 } 240 else if ("assignable".equals(filterType)) { 241 return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader)); 242 } 243 else if ("aspectj".equals(filterType)) { 244 return new AspectJTypeFilter(expression, classLoader); 245 } 246 else if ("regex".equals(filterType)) { 247 return new RegexPatternTypeFilter(Pattern.compile(expression)); 248 } 249 else if ("custom".equals(filterType)) { 250 Class<?> filterClass = ClassUtils.forName(expression, classLoader); 251 if (!TypeFilter.class.isAssignableFrom(filterClass)) { 252 throw new IllegalArgumentException( 253 "Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression); 254 } 255 return (TypeFilter) BeanUtils.instantiateClass(filterClass); 256 } 257 else { 258 throw new IllegalArgumentException("Unsupported filter type: " + filterType); 259 } 260 } 261 262 @SuppressWarnings("unchecked") 263 private Object instantiateUserDefinedStrategy( 264 String className, Class<?> strategyType, @Nullable ClassLoader classLoader) { 265 266 Object result; 267 try { 268 result = ReflectionUtils.accessibleConstructor(ClassUtils.forName(className, classLoader)).newInstance(); 269 } 270 catch (ClassNotFoundException ex) { 271 throw new IllegalArgumentException("Class [" + className + "] for strategy [" + 272 strategyType.getName() + "] not found", ex); 273 } 274 catch (Throwable ex) { 275 throw new IllegalArgumentException("Unable to instantiate class [" + className + "] for strategy [" + 276 strategyType.getName() + "]: a zero-argument constructor is required", ex); 277 } 278 279 if (!strategyType.isAssignableFrom(result.getClass())) { 280 throw new IllegalArgumentException("Provided class name must be an implementation of " + strategyType); 281 } 282 return result; 283 } 284 285}