@ComponentScan这个组件还是比较重要的,上篇文章https://blog.csdn.net/weixin_37749640/article/details/83904144Spring注解系列——@Configuration&@Bean给容器中注册组件中列出了spring 2.5以下的版本中的配置文件是如何配置的。在配置文件中有以下几行代码
<context:component-scan base-package="cn.cisol.mvcdemo">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
相信大家都不陌生,是声明容器要扫描的包。那现在不用这个配置文件了,启用注解方式,用的就是这个 @ComponentScan标签。下面来介绍下是怎么用的
1.在配置类上添加@ComponentScan
package com.sht.configuration;
import com.sht.entity.Student;
import com.sht.repository.MyFilterType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
/**
* 配置类,替代spring的配置文件applicationContext.xml
* 在类上添加@Configuration @ComponentScan
*/
@Configuration
@ComponentScan
public class MainConfiguration1 {
/**
* 替代配置文件中声明的组件bean
* <bean id="student" class="com.sht.entity.Student">……</bean>
* 默认方法名为id
* @return
*/
@Bean("stu")
public Student student(){
return new Student();
}
}
如果不添加@ComponentScan标签或者@ComponentScan没有标注要扫描的包,那就只是扫描当前类所在的包。让我们看下@ComponentScan都有哪些属性
2.@ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
/**
* Alias for {@link #basePackages}.
* <p>Allows for more concise annotation declarations if no other attributes
* are needed — for example, {@code @ComponentScan("org.my.pkg")}
* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
*/
@AliasFor("basePackages")
String[] value() default {};
/**
* Base packages to scan for annotated components.
* <p>{@link #value} is an alias for (and mutually exclusive with) this
* attribute.
* <p>Use {@link #basePackageClasses} for a type-safe alternative to
* String-based package names.
*/
@AliasFor("value")
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages} for specifying the packages
* to scan for annotated components. The package of each class specified will be scanned.
* <p>Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};
/**
* The {@link BeanNameGenerator} class to be used for naming detected components
* within the Spring container.
* <p>The default value of the {@link BeanNameGenerator} interface itself indicates
* that the scanner used to process this {@code @ComponentScan} annotation should
* use its inherited bean name generator, e.g. the default
* {@link AnnotationBeanNameGenerator} or any custom instance supplied to the
* application context at bootstrap time.
* @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
*/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/**
* The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components.
*/
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
/**
* Indicates whether proxies should be generated for detected components, which may be
* necessary when using scopes in a proxy-style fashion.
* <p>The default is defer to the default behavior of the component scanner used to
* execute the actual scan.
* <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.
* @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
*/
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
/**
* Controls the class files eligible for component detection.
* <p>Consider use of {@link #includeFilters} and {@link #excludeFilters}
* for a more flexible approach.
*/
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
/**
* Indicates whether automatic detection of classes annotated with {@code @Component}
* {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled.
*/
boolean useDefaultFilters() default true;
/**
* Specifies which types are eligible for component scanning.
* <p>Further narrows the set of candidate components from everything in {@link #basePackages}
* to everything in the base packages that matches the given filter or filters.
* <p>Note that these filters will be applied in addition to the default filters, if specified.
* Any type under the specified base packages which matches a given filter will be included,
* even if it does not match the default filters (i.e. is not annotated with {@code @Component}).
* @see #resourcePattern()
* @see #useDefaultFilters()
*/
Filter[] includeFilters() default {};
/**
* Specifies which types are not eligible for component scanning.
* @see #resourcePattern
*/
Filter[] excludeFilters() default {};
/**
* Specify whether scanned beans should be registered for lazy initialization.
* <p>Default is {@code false}; switch this to {@code true} when desired.
* @since 4.1
*/
boolean lazyInit() default false;
/**
* Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters
* include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
/**
* The type of filter to use.
* <p>Default is {@link FilterType#ANNOTATION}.
* @see #classes
* @see #pattern
*/
FilterType type() default FilterType.ANNOTATION;
/**
* Alias for {@link #classes}.
* @see #classes
*/
@AliasFor("classes")
Class<?>[] value() default {};
/**
* The class or classes to use as the filter.
* <p>The following table explains how the classes will be interpreted
* based on the configured value of the {@link #type} attribute.
* <table border="1">
* <tr><th>{@code FilterType}</th><th>Class Interpreted As</th></tr>
* <tr><td>{@link FilterType#ANNOTATION ANNOTATION}</td>
* <td>the annotation itself</td></tr>
* <tr><td>{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE}</td>
* <td>the type that detected components should be assignable to</td></tr>
* <tr><td>{@link FilterType#CUSTOM CUSTOM}</td>
* <td>an implementation of {@link TypeFilter}</td></tr>
* </table>
* <p>When multiple classes are specified, <em>OR</em> logic is applied
* — for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
* <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the
* following {@link org.springframework.beans.factory.Aware Aware} interfaces, and
* their respective methods will be called prior to {@link TypeFilter#match match}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
* </ul>
* <p>Specifying zero classes is permitted but will have no effect on component
* scanning.
* @since 4.2
* @see #value
* @see #type
*/
@AliasFor("value")
Class<?>[] classes() default {};
/**
* 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 {};
}
}
3.常用属性分析
我们看到这个注解是要用来类上的,且可以重复应用的。
(1)value | basePackages
正如以前一样,如果我们要扫描包,是要指定要扫描包的路径的。
@ComponentScan(value = "com.sht")
public class MainConfiguration1 {
这样就会扫描com.sht包及其子包了。也可以按照以下的写法
@ComponentScan(basePackages = "com.sht")
public class MainConfiguration1 {
(2)excludeFilters
Filter[] excludeFilters() default {};
是Filter的一个数组,让我们来看看Filter
/**
* Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters
* include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
/**
* The type of filter to use.
* <p>Default is {@link FilterType#ANNOTATION}.
* @see #classes
* @see #pattern
*/
FilterType type() default FilterType.ANNOTATION;
它也是一个注解,常用默认属性为type和class,我们可以看到type的默认值为FilterType.ANNOTATION,也就是说默认是注解类型。再让我们看看FIlterType里面都包含什么
public enum FilterType {
/**
* Filter candidates marked with a given annotation.
* @see org.springframework.core.type.filter.AnnotationTypeFilter
*/
ANNOTATION,
/**
* Filter candidates assignable to a given type.
* @see org.springframework.core.type.filter.AssignableTypeFilter
*/
ASSIGNABLE_TYPE,
/**
* Filter candidates matching a given AspectJ type pattern expression.
* @see org.springframework.core.type.filter.AspectJTypeFilter
*/
ASPECTJ,
/**
* Filter candidates matching a given regex pattern.
* @see org.springframework.core.type.filter.RegexPatternTypeFilter
*/
REGEX,
/** Filter candidates using a given custom
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM
是一个枚举类,常用的为ANNOTATION、REGEX、CUSTOM。让我们来举几个例子:
/*
* 过滤掉含有@Service注解的组件,即我们容器的组件中不会加载标注@Service的组件
* spring默认的组件为@Component/@Service/@Repository/@Controller
*/
@Configuration
@ComponentScan(value = "com.sht",excludeFilters={
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})
})
public class MainConfiguration1 {
如果我们想自定义来过滤掉指定的组件呢,看下面的配置
package com.sht.repository;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class MyFilterType implements TypeFilter {
/**
*
* @param metadataReader 能读取到所有的类信息
* @param metadataReaderFactory
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
//不加载类名含有Include的类组件spring的组件
if (className.contains("Include")){
return true;
}
// System.out.println(annotationMetadata);
return false;
}
}
需要我们自定义的类实现TypeFilter接口,并实现match方法,当match方法返回为true时不加载组件,为false时加载组件,同时在配置类中的配置如下
@Configuration
@ComponentScan(value = "com.sht",excludeFilters={
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyFilterType.class})
})
public class MainConfiguration1 {
看一下spring中的组件
十一月 11, 2018 12:45:32 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@67f89fa3: startup date [Sun Nov 11 12:45:32 CST 2018]; root of context hierarchy
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfiguration1
excludeClass
stu
是不是类名含有“Include”的组件都已经被排除在外了呢。
(4)IncludeFilters
原理同excludeFilters,只不过要启用另一个属性
@ComponentScan(value = "com.sht",includeFilters={
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Repository.class})
},useDefaultFilters = false)
就是useDefaultFilters = false,不启用spring默认的过滤器。
其实,这些都不难,难就难在没有切入点,不知道该从何处入手。如果要真正把知识学会,还是需要自己多动手,多思考。今天就要这了。