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