基于@ComponentScan注解及其XML配置方式
@ComponentScan注解及其XML配置
開發(fā)中會經(jīng)常使用包掃描,只要標(biāo)注了@Controller、@Service、@Repository,@Component 注解的類會自動(dòng)加入到容器中,ComponentScan有注解和xml配置兩種方式。
注解
@ComponentScan 包含過濾和排除過濾
- ComponentScan.Filter[] includeFilters() default {}; 按照某些規(guī)則排除組件
- ComponentScan.Filter[] excludeFilters() default {}; 指定掃描的時(shí)候只需要包含哪些組件
@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 {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; String resourcePattern() default "**/*.class"; boolean useDefaultFilters() default true; ComponentScan.Filter[] includeFilters() default {}; ComponentScan.Filter[] excludeFilters() default {}; boolean lazyInit() default false; @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Filter { FilterType type() default FilterType.ANNOTATION; @AliasFor("classes") Class<?>[] value() default {}; @AliasFor("value") Class<?>[] classes() default {}; String[] pattern() default {}; } }
FilterType 指定不同的包含/排除規(guī)則:
package org.springframework.context.annotation; public enum FilterType { ANNOTATION, ASSIGNABLE_TYPE, ASPECTJ, REGEX, CUSTOM; private FilterType() { } }
1、Spring使用注解 包掃描 @ComponentScan
@ComponentScan("com.spring.annotation") @Configuration public class MainConfig { }
注意:mainConfig 配置類也是一個(gè)組件 因?yàn)锧Configuration 注解中標(biāo)有@Component。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { String value() default ""; }
2、按照注解類型排除
例如排除以下類型:Controller.class, Service.class, Repository.class:
@ComponentScan(value = "com.spring.annotation", excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class}) }) @Configuration public class MainConfig { }
3、包含過濾includeFilters
如果想要只包含 Controller 注解的bean,如下配置:
@ComponentScan(value = "com.spring.annotation", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},useDefaultFilters = false) @Configuration public class MainConfig { }
注意:需要添加 useDefaultFilters = false。
4、使用@ComponentScans 來指定掃描策略
ComponentScans 注解結(jié)構(gòu)如下:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented public @interface ComponentScans { ComponentScan[] value(); }
可以看到其內(nèi)部是一個(gè)ComponentScan[] 數(shù)組,所以我們可以在其中指定多個(gè)ComponentScan。
例如:
指定不同的類型,包含Controller 注解的bean 和 BookService類型的bean:
@ComponentScans(value = { @ComponentScan(value = "com.spring.annotation", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}) },useDefaultFilters = false) }) @Configuration public class MainConfig { }
5、FilterType.CUSTOM:使用自定義規(guī)則
1>. 編寫MyTypeFilter 并實(shí)現(xiàn) TypeFilter 接口;
2>. match方法中 實(shí)現(xiàn)自定義規(guī)則。
/** * 自定義過濾規(guī)則 */ public class MyTypeFilter implements TypeFilter { /** * * @param metadataReader * @param metadataReaderFactory * @return * @throws IOException * metadataReader:讀取到的當(dāng)前正在掃描的類的信息 * metadataReaderFactory:可以獲取到其他任何類信息的 * * */ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // TODO Auto-generated method stub //獲取當(dāng)前類注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //獲取當(dāng)前正在掃描的類的類信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //獲取當(dāng)前類資源(類的路徑) Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->"+className); if(className.contains("er")){ return true; } return false; } }
3>. 使用實(shí)例(當(dāng)前掃描到的類,類名中包含er,就會注入到容器中):
@ComponentScans(value = { @ComponentScan(value = "com.spring.annotation", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}), @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}) },useDefaultFilters = false) }) @Configuration public class MainConfig { }
注解小結(jié):
@ComponentScan value
:指定要掃描的包excludeFilters = Filter[]
:指定掃描的時(shí)候按照什么規(guī)則排除那些組件includeFilters = Filter[]
:指定掃描的時(shí)候只需要包含哪些組件FilterType.ANNOTATION
:按照注解FilterType.ASSIGNABLE_TYPE
:按照給定的類型;FilterType.ASPECTJ
:使用ASPECTJ表達(dá)式FilterType.REGEX
:使用正則指定FilterType.CUSTOM
:使用自定義規(guī)則
XML配置
我們使用component-scan來進(jìn)行bean的加載,例如,我們通常會使用如下的配置:
application.xml:
<context:component-scan base-package="com.cn.kvn.service,com.cn.kvn.config,com.baidu"> <context:include-filter type="annotation" expression="com.alibaba.dubbo.config.annotation.Service" /> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
spring-servlet.xml:
<context:component-scan base-package="com.cn.kvn.controller" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
原理: componentScan解析bean的入口為:ComponentScanBeanDefinitionParser#parse(Element element, ParserContext parserContext)
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // Actually scan for bean definitions and register them. ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
base-package屬性告訴spring要掃描的包,use-default-filters="false"表示不要使用默認(rèn)的過濾器。
configureScanner會去配置scan時(shí)的使用的filter,其中use-default-filters屬性是來控制是否要使用默認(rèn)的過濾器的(默認(rèn)的過濾器會去解析base-package下的含有@Component注解的類作為bean,其中@Repository, @Service, and @Controller都是@Component的子注解,故,默認(rèn)的過濾器會將它們?nèi)拷馕龀蒪ean)。
原碼中的英文注釋:
ClassPathScanningCandidateComponentProvider#ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment)
useDefaultFilters whether to register the default filters for the @Component, @Repository, @Service, and @Controller stereotype annotations
用戶自定義的include-filter和exclude-filter會在以下方法中被解析加載。
ComponentScanBeanDefinitionParser#parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext)
在執(zhí)行Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);的時(shí)候,使用過濾器的順序是,exclude-filter優(yōu)于include-filter。
也就是說,如果同時(shí)定義了exclude-filter排除了某類(某個(gè))bean,但是include-filter又將其包含了,則該bean不會被加載到spring容器。
ClassPathScanningCandidateComponentProvider.java /** * Determine whether the given class does not match any exclude filter * and does match at least one include filter. * @param metadataReader the ASM ClassReader for the class * @return whether the class qualifies as a candidate component */ protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return isConditionMatch(metadataReader); } } return false; }
附:過濾規(guī)則設(shè)置
filter標(biāo)簽的type和表達(dá)式說明如下:
Filter Type | Examples Expression | Description | include-filter為例 |
annotation | org.example.SomeAnnotation | 符合SomeAnnoation的target class | <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/> 表示掃描base-package下的類上加了Aspect注解的類,并注冊到spring的bean容器 |
assignable | org.example.SomeClass | 指定class或interface的全名 | <context:include-filter type="assignable" expression="com.test.scan.StuService"/> 指定掃描StuService類作為bean |
aspectj | org.example..*Service+ | AspectJ語法 | |
regex | org\.example\.Default.* | Regelar Expression | |
custom | org.example.MyTypeFilter | Spring3新增自訂Type,實(shí)作org.springframework.core.type.TypeFilter |
注意:如果通過regex將filter的type設(shè)置成了正則表達(dá)式,注意在正則里面.表示所有字符,而\.才表示真正的.字符。例如我們的正則表示以Dao或者Service結(jié)束的類。
我們也可以使用annotaion來限定,如下:
<context:component-scan base-package="cn.outofmemory.spring" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 關(guān)于@ComponentScan注解的用法及作用說明
- SpringBoot中@ComponentScan注解過濾排除不加載某個(gè)類的3種方法
- @AutoConfigurationPackage與@ComponentScan注解區(qū)別
- spring?boot自動(dòng)裝配之@ComponentScan注解用法詳解
- Spring @ComponentScan注解使用案例詳細(xì)講解
- Spring @ComponentScan注解掃描組件原理
- Spring?component-scan?XML配置與@ComponentScan注解配置
- 基于ComponentScan注解的掃描范圍及源碼解析
相關(guān)文章
關(guān)于Java鎖性能提高(鎖升級)機(jī)制的總結(jié)
這篇文章主要介紹了關(guān)于Java鎖性能提高(鎖升級)機(jī)制的總結(jié),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05解決java 查看JDK中底層源碼的實(shí)現(xiàn)方法
本篇文章是對在java中查看JDK中底層源碼的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Java 實(shí)戰(zhàn)項(xiàng)目錘煉之在線購書商城系統(tǒng)的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+jsp+mysql+servlet+ajax實(shí)現(xiàn)一個(gè)在線購書商城系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11Java中l(wèi)ist.foreach()和list.stream().foreach()用法詳解
在Java中List是一種常用的集合類,用于存儲一組元素,List提供了多種遍歷元素的方式,包括使用forEach()方法和使用Stream流的forEach()方法,這篇文章主要給大家介紹了關(guān)于Java中l(wèi)ist.foreach()和list.stream().foreach()用法的相關(guān)資料,需要的朋友可以參考下2024-07-07java開發(fā)之spring webflow實(shí)現(xiàn)上傳單個(gè)文件及多個(gè)文件功能實(shí)例
這篇文章主要介紹了java開發(fā)之spring webflow實(shí)現(xiàn)上傳單個(gè)文件及多個(gè)文件功能,結(jié)合具體實(shí)例形式分析了spring webflow文件上傳具體操作技巧,需要的朋友可以參考下2017-11-11Springboot+ElementUi實(shí)現(xiàn)評論、回復(fù)、點(diǎn)贊功能
這篇文章主要介紹了通過Springboot ElementUi實(shí)現(xiàn)評論、回復(fù)、點(diǎn)贊功能。如果是自己評論的還可以刪除,刪除的規(guī)則是如果該評論下還有回復(fù),也一并刪除。需要的可以參考一下2022-01-01SpringBoot啟動(dòng)異常Exception in thread “main“ 
本文主要介紹了SpringBoot啟動(dòng)異常Exception in thread “main“ java.lang.UnsupportedClassVersionError,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07