/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.citrus.service.moduleloader.impl.factory; import static com.alibaba.citrus.springext.util.DomUtil.*; import static com.alibaba.citrus.util.Assert.*; import static com.alibaba.citrus.util.CollectionUtil.*; import static com.alibaba.citrus.util.ObjectUtil.*; import static com.alibaba.citrus.util.StringUtil.*; import static com.alibaba.citrus.util.regex.ClassNameWildcardCompiler.*; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import com.alibaba.citrus.util.regex.ClassNameWildcardCompiler; import org.springframework.beans.BeanUtils; import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.AspectJTypeFilter; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.core.type.filter.RegexPatternTypeFilter; import org.springframework.core.type.filter.TypeFilter; import org.w3c.dom.Element; /** * 解析class-modules。 * * @author Michael Zhou */ public class ClassModuleFactoryDefinitionParser extends AbstractModuleFactoryDefinitionParser<ClassModuleFactory> { @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { Map<String, ParsingModuleInfo> classes = parseSpecificBeans(element, parserContext, builder.getRawBeanDefinition(), and(beansNs(), name("bean"))); ElementSelector searchPackages = and(sameNs(element), name("search-packages")); ElementSelector searchClasses = and(sameNs(element), name("search-classes")); ModuleDefinitionScanner scanner = getScanner(parserContext); for (Element subElement : subElements(element)) { Pattern classNamePattern = null; String typeName = null; String moduleName = null; String classResourceName = null; if (searchPackages.accept(subElement)) { String packageName = assertNotNull(normalizeClassName(subElement.getAttribute("packages")), "no package name provided for search-packages"); classNamePattern = compileClassName(packageName, MATCH_PREFIX); typeName = assertNotNull(trimToNull(subElement.getAttribute("type")), "no type name provided"); classResourceName = classNameToPathName(packageName) + "/**/*.class"; log.trace("Searching in packages: {}, moduleType={}", packageName, typeName); } else if (searchClasses.accept(subElement)) { String className = assertNotNull(normalizeClassName(subElement.getAttribute("classes")), "no class name provided for search-classes"); classNamePattern = compileClassName(className, MATCH_PREFIX); typeName = assertNotNull(trimToNull(subElement.getAttribute("type")), "no type name provided"); moduleName = assertNotNull(trimToNull(subElement.getAttribute("name")), "no module name provided"); classResourceName = classNameToPathName(className); if (classResourceName.endsWith("**")) { classResourceName += "/*.class"; } else { classResourceName += ".class"; } log.trace("Searching for classes: {}, moduleType={}, moduleName={}", new Object[] { className, typeName, moduleName }); } boolean includeAbstractClasses = "true".equalsIgnoreCase(trimToNull(subElement .getAttribute("includeAbstractClasses"))); if (classResourceName != null) { // 处理所有的include/exclude filters ClassLoader classLoader = scanner.getResourceLoader().getClassLoader(); List<TypeFilter> includes = createLinkedList(); List<TypeFilter> excludes = createLinkedList(); parseTypeFilters(subElement, classLoader, includes, excludes); ModuleTypeFilter filter = new ModuleTypeFilter(classes, classNamePattern, typeName, moduleName, includes); // 事实上,只有一个顶级的include filter,其它的include filter被这一个moduleTypeFilter所调用。 scanner.addIncludeFilter(filter); // Exclude filter比较简单,直接加到scanner中即可。 for (TypeFilter exclude : excludes) { scanner.addExcludeFilter(exclude); } scanner.setBeanNameGenerator(filter); scanner.setResourcePattern(classResourceName.replace('?', '*')); scanner.setIncludeAbstractClasses(includeAbstractClasses); scanner.setBeanDefinitionDefaults(getBeanDefinitionDefaults(subElement, parserContext)); int found = scanner.scan(""); log.debug("Found {} module classes with pattern: {}", found, classResourceName); } } postProcessItems(element, parserContext, builder, classes, "search-packages or search-classes"); } private ModuleDefinitionScanner getScanner(ParserContext parserContext) { ModuleDefinitionScanner scanner = new ModuleDefinitionScanner(parserContext.getRegistry()); scanner.setResourceLoader(parserContext.getReaderContext().getResourceLoader()); return scanner; } @Override protected String parseItemName(ParserContext parserContext, Element element, BeanDefinition bd) { return assertNotNull(bd.getBeanClassName(), "no className provided for bean definition: %s", bd); } @Override protected String getDefaultName() { return "classModuleFactory"; } /** 解析<code>TypeFilter</code>s。 */ private void parseTypeFilters(Element element, ClassLoader classLoader, List<TypeFilter> includes, List<TypeFilter> excludes) { ElementSelector includeSelector = and(sameNs(element), name("include-filter")); ElementSelector excludeSelector = and(sameNs(element), name("exclude-filter")); for (Element subElement : subElements(element)) { if (includeSelector.accept(subElement)) { TypeFilter filter = createTypeFilter(subElement, classLoader); if (filter != null) { includes.add(filter); } } else if (excludeSelector.accept(subElement)) { TypeFilter filter = createTypeFilter(subElement, classLoader); if (filter != null) { excludes.add(filter); } } } } /** 创建指定的TypeFilter。 */ private TypeFilter createTypeFilter(Element element, ClassLoader classLoader) { String filterType = defaultIfNull(trimToNull(element.getAttribute("type")), "wildcard"); String expression = assertNotNull(trimToNull(element.getAttribute("expression")), "expression for %s" + filterType); try { if ("assignable".equals(filterType)) { return new AssignableTypeFilter(classLoader.loadClass(expression)); } else if ("aspectj".equals(filterType)) { return new AspectJTypeFilter(expression, classLoader); } else if ("wildcard".equals(filterType)) { return new RegexPatternTypeFilter(ClassNameWildcardCompiler.compileClassName(expression, ClassNameWildcardCompiler.MATCH_PREFIX)); } else if ("custom".equals(filterType)) { Class<?> filterClass = classLoader.loadClass(expression); assertTrue(TypeFilter.class.isAssignableFrom(filterClass), "Class is not assignable to TypeFilter: %s", expression); return (TypeFilter) BeanUtils.instantiateClass(filterClass); } else { unreachableCode("Unsupported filter type: %s", filterType); return null; } } catch (ClassNotFoundException e) { throw new FatalBeanException("Failed to create TypeFilter of type " + filterType + ": " + expression, e); } } private static class ModuleDefinitionScanner extends ClassPathBeanDefinitionScanner { private boolean includeAbstractClasses; public ModuleDefinitionScanner(BeanDefinitionRegistry registry) { super(registry, false); } public void setIncludeAbstractClasses(boolean includeAbstractClasses) { this.includeAbstractClasses = includeAbstractClasses; } @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { // 排除内联类 if (beanDefinition.getMetadata().getClassName().contains("$")) { return false; } // 对于concreteClass,直接返回true if (super.isCandidateComponent(beanDefinition)) { return true; } if (!includeAbstractClasses) { return false; } // 非concrete,但指定了includeAbstractClasses=true beanDefinition.setBeanClassName(NonInstantiatableClassFactoryBean.class.getName()); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, beanDefinition.getMetadata().getClassName(), Class.class.getName()); return true; } } private static class ModuleTypeFilter extends ParsingModuleMatcher implements TypeFilter, BeanNameGenerator { public ModuleTypeFilter(Map<String, ParsingModuleInfo> classes, Pattern classNamePattern, String typeName, String moduleName, List<TypeFilter> includeFilters) { super(classes, classNamePattern, typeName, moduleName, includeFilters); } public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return doMatch(metadataReader, metadataReaderFactory); } public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { String className; // 从metadata中取得classname,因为对于非concrete类,当includeAbstractClasses=true时, // definition.getBeanClassName() == NonInstantiatableObject,而不是最终的类。 if (definition instanceof AnnotatedBeanDefinition) { className = ((AnnotatedBeanDefinition) definition).getMetadata().getClassName(); } else { className = definition.getBeanClassName(); } return generateBeanName(className, registry); } } public static class NonInstantiatableClassFactoryBean implements FactoryBean { private final Class<?> clazz; public NonInstantiatableClassFactoryBean(Class<?> clazz) { this.clazz = assertNotNull(clazz, "class"); } public Object getObject() throws Exception { return clazz; } public Class<?> getObjectType() { return Class.class; } public boolean isSingleton() { return true; } } }