2021-10-04

Spring 注解之 @ComponentScan

@ComponentScan 的作用
据定义的扫描路径,把符合扫描规则的类装配到spring容器中

@Controller、@Service、@Repository,@Component标注的类。
@Filter: 是@ComponentScan注解类中的子注解(内部注解),可以指定一些过滤规则

           FilterType.ANNOTATION:按照注解注入
           FilterType.ASSIGNABLE_TYPE:按照给定的类型注入
           FilterType.ASPECTJ:使用ASPECTJ表达式
           FilterType.REGEX:使用正则指定
           FilterType.CUSTOM:使用自定义规

在org.springframework.context.annotation包下,查看ComponentScan的注解定义信息,这个注解,标注了@Repeatable(ComponentScans.class),在jdk1.8以上,可以重复标注,都会生效,也可以使用ComponentScans.class,使用@ComponentScan注解的数组,标注多个扫描

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    //字符串形式指定扫描的包路径
    @AliasFor("basePackages")
    String[] value() default {};
//字符串形式指定扫描的包路径
    @AliasFor("value")
    String[] basePackages() default {};
//这个英文翻译,大概就是指定类或者接口的类型,会按照指定的类所在的包,扫描包下的类
    Class<?>[] basePackageClasses() default {};//为bean生成名称的规则的类,需要实现BeanNameGenerator接口,后面指定了默认的生成规则
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;//用于解析bean定义范围的策略接口。(单例,多例等
    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    //各种作用域代理选项 
    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
    //看解释,以及默认值(static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";),是扫描的文件类型
    String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;//是否启动自动扫描所有标注了 @Controller、@Service、@Repository,@Component的类
    boolean useDefaultFilters() default true;
    //扫描只包含Filter规则的类
    Filter[] includeFilters() default {};
 //排除规则Filter的类
    Filter[] excludeFilters() default {};
//懒加载
    boolean lazyInit() default false;@Retention(RetentionPolicy.RUNTIME)
    @Target({})
    @interface Filter {
        //Filter的定义策略
        FilterType type() default FilterType.ANNOTATION;
        //跟type()配合使用
        @AliasFor("classes")
        Class<?>[] value() default {};
       //跟type()配合使用
        @AliasFor("value")
        Class<?>[] classes() default {};
        /**  
            跟type()配合使用,大概就是,如果指定的类型是ASPECTJ,那么就是ASPECTJ的表达式,是REGEX,就是REGEX的表达式,不常用
         * The pattern (or patterns) to use for the filter, as an alternative
         * to specifying a Class {@link #value}.
         * <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ},
         * this is an AspectJ type pattern expression. If {@link #type} is
         * set to {@link FilterType#REGEX REGEX}, this is a regex pattern
         * for the fully-qualified class names to match.
         * @see #type
         * @see #classes
         */
        String[] pattern() default {};
    }
}

下面直接上代码,看使用的方式

//配置类==配置文件
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.bader", includeFilters = {
        //只扫描有@Controller注解的类
        @Filter(type = FilterType.ANNOTATION, classes = { Controller.class }),
        //只扫描类型是BookService的类
        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { BookService.class }),
       //自定义过滤规则
        @Filter(type = FilterType.CUSTOM, classes = { MyTypeFilter.class }) }, 
        useDefaultFilters = false)
// @ComponentScan value:指定要扫描的包
// excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
// includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
// FilterType.ANNOTATION:按照注解
// FilterType.ASSIGNABLE_TYPE:按照给定的类型;
// FilterType.ASPECTJ:使用ASPECTJ表达式
// FilterType.REGEX:使用正则指定
// FilterType.CUSTOM:使用自定义规则
public class MyConfig {}

自定义的Filter,最后的CUSTOM,也说了,需要实现org.springframework.core.type.filter.TypeFilter接口

public class MyCustomTypeFilter implements TypeFilter {
 
    /**
     * metadataReader:读取到的当前正在扫描的类的信息
     * metadataReaderFactory:可以获取到其他任何类信息的
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        // 获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取当前类资源(类的路径)
        Resource resource = metadataReader.getResource();
 
        String className = classMetadata.getClassName();
        System.out.println("--->" + className);
        //根据扫描的className,包含service的,扫描成功
        if (className.contains("service")) {
            return true;
        }
        return false;
    }
}

@ComponentScans:是@ComponentScan注解的集合,里面可以指定多个@ComponentScan注解,扫描多个包路径,
spring 的xml配置如下

<context:component-scan base-package="com.chongzi"></context:component-scan>

源码参考类:
ComponentScanAnnotationParser

/
*
 * Copyright 2002-2018 the original author or authors.
 *
 * 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
 *
 *      https://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 org.springframework.context.annotation;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
import org.springframework.core.type.filter.AnnotationTypeFilter;
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.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * Parser for the @{@link ComponentScan} annotation.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 3.1
 * @see ClassPathBeanDefinitionScanner#scan(String...)
 * @see ComponentScanBeanDefinitionParser
 */
class ComponentScanAnnotationParser {

	private final Environment environment;

	private final ResourceLoader resourceLoader;

	private final BeanNameGenerator beanNameGenerator;

	private final BeanDefinitionRegistry registry;


	public ComponentScanAnnotationParser(Environment environment, ResourceLoader resourceLoader,
			BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) {

		this.environment = environment;
		this.resourceLoader = resourceLoader;
		this.beanNameGenerator = beanNameGenerator;
		this.registry = registry;
	}


	public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

		Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
		boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
		scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
				BeanUtils.instantiateClass(generatorClass));

		ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
		if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
			scanner.setScopedProxyMode(scopedProxyMode);
		}
		else {
			Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
			scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
		}

		scanner.setResourcePattern(componentScan.getString("resourcePattern"));

		for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
				scanner.addIncludeFilter(typeFilter);
			}
		}
		for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
				scanner.addExcludeFilter(typeFilter);
			}
		}

		boolean lazyInit = componentScan.getBoolean("lazyInit");
		if (lazyInit) {
			scanner.getBeanDefinitionDefaults().setLazyInit(true);
		}

		Set<String> basePackages = new LinkedHashSet<>();
		String[] basePackagesArray = componentScan.getStringArray("basePackages");
		for (String pkg : basePackagesArray) {
			String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			Collections.addAll(basePackages, tokenized);
		}
		for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
			basePackages.add(ClassUtils.getPackageName(clazz));
		}
		// 如果basePackages 是空的话,会将所在类的包地址默认未 包扫描路径
		if (basePackages.isEmpty()) {
			basePackages.add(ClassUtils.getPackageName(declaringClass));
		}

		scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
			@Override
			protected boolean matchClassName(String className) {
				return declaringClass.equals(className);
			}
		});
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}

	private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
		List<TypeFilter> typeFilters = new ArrayList<>();
		FilterType filterType = filterAttributes.getEnum("type");

		for (Class<?> filterClass : filterAttributes.getClassArray("classes")) {
			switch (filterType) {
				case ANNOTATION:
					Assert.isAssignable(Annotation.class, filterClass,
							"@ComponentScan ANNOTATION type filter requires an annotation type");
					@SuppressWarnings("unchecked")
					Class<Annotation> annotationType = (Class<Annotation>) filterClass;
					typeFilters.add(new AnnotationTypeFilter(annotationType));
					break;
				case ASSIGNABLE_TYPE:
					typeFilters.add(new AssignableTypeFilter(filterClass));
					break;
				case CUSTOM:
					Assert.isAssignable(TypeFilter.class, filterClass,
							"@ComponentScan CUSTOM type filter requires a TypeFilter implementation");

					TypeFilter filter = ParserStrategyUtils.instantiateClass(filterClass, TypeFilter.class,
							this.environment, this.resourceLoader, this.registry);
					typeFilters.add(filter);
					break;
				default:
					throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
			}
		}

		for (String expression : filterAttributes.getStringArray("pattern")) {
			switch (filterType) {
				case ASPECTJ:
					typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
					break;
				case REGEX:
					typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
					break;
				default:
					throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
			}
		}

		return typeFilters;
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值