案例已上傳GitHub,歡迎star:github.com/oneStarLR/s…java
什麼是組件?git
組件也是抽象的概念,能夠理解爲一些符合某種規範的類組合在一塊兒就構成了組件,他能夠提供某些特定的功能,但實際他們都是類,只不過有他們特殊的規定。組件和類的關係:符合某種規範的類的組合構成組件。github
加了包掃描@ComponentScan註解後,只要標註了@Controller、@Service、@Repository、@Component註解中的任何一個,其組件都會被自動掃描,加入到容器中。正則表達式
【1】value
:指定要掃描的包spring
【2】excludeFilters=Filter[ ]
:設置排除的過濾條件,指定掃描的時候按照什麼規則排除哪些組件,不掃描哪些包數組
【3】includeFilters=filter[ ]
:設置掃描過濾條件,指定掃描的時候按照什麼規則包含哪些組件,知足該條件才進行掃描markdown
【4】自定義過濾規則
:經過實現TypeFilter接口,自定義過濾規則app
下面我們經過實例來分析一下@ComponentScan註解的做用dom
以maven項目爲例,經過@ComponentScan、@Controller、@Service、@Repository註解獲取容器中的對象:maven
// 啓動類
@Test
public void TestMain(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
String[] beanNames = applicationContext.getBeanDefinitionNames();
for(String beanName : beanNames){
System.out.println("容器中對象名稱:"+beanName);
}
}
// 配置類
@Configuration
@ComponentScan(value = "com.onestar")
public class AppConfig {
}
// 控制層
@Controller
public class UserController {
}
// 業務層
@Service
public class UserService {
}
// 持久層
@Repository
public class UserDao {
}
複製代碼
運行測試類,能夠看到以下打印信息,除了spring啓動時注入到容器的對象外,還有咱們本身加了註解,被spring掃描,加入到容器中
@ComponentScan(value = "com.onestar")
:value表示指定要掃描的包點進@ComponentScan註解源碼,咱們能夠看到,使用value能夠指定要掃描的包,咱們還能夠排除要掃描的包,包含要掃描的包,甚至還能夠自定義過濾規則
excludeFilters=Filter[ ]
:指定掃描的時候按照什麼規則排除哪些包includeFilters=filter[ ]
:指定掃描的時候是須要包含哪些包【@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,能夠自定義實現命名Bean, * 一、@ComponentScan(value = "spring.annotation.componentscan",nameGenerator = MyBeanNameGenerator.class) * MyBeanNameGenerator.class 須要實現 BeanNameGenerator 接口,全部實現BeanNameGenerator 接口的實現類都會被調用 * 二、使用 AnnotationConfigApplicationContext 的 setBeanNameGenerator方法注入一個BeanNameGenerator * BeanNameGenerator beanNameGenerator = (definition,registry)-> String.valueOf(new Random().nextInt(1000)); * AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); * annotationConfigApplicationContext.setBeanNameGenerator(beanNameGenerator); * annotationConfigApplicationContext.register(MainConfig2.class); * annotationConfigApplicationContext.refresh(); * 第一種方式只會重命名@ComponentScan掃描到的註解類 * 第二種只有是初始化的註解類就會被重命名 * 列如第一種方式不會重命名 @Configuration 註解的bean名稱,而第二種就會重命名 @Configuration 註解的Bean名稱 */
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/** * 用於解析@Scope註解,可經過 AnnotationConfigApplicationContext 的 setScopeMetadataResolver 方法從新設定處理類 * ScopeMetadataResolver scopeMetadataResolver = definition -> new ScopeMetadata(); 這裏只是new了一個對象做爲演示,沒有作實際的邏輯操做 * AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); * annotationConfigApplicationContext.setScopeMetadataResolver(scopeMetadataResolver); * annotationConfigApplicationContext.register(MainConfig2.class); * annotationConfigApplicationContext.refresh(); * 也能夠經過@ComponentScan 的 scopeResolver 屬性設置 *@ComponentScan(value = "spring.annotation.componentscan",scopeResolver = MyAnnotationScopeMetadataResolver.class) */
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
// 設置類的代理模式
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
/** * 掃描路徑 如 resourcePattern = "**/*.class" * 使用 includeFilters 和 excludeFilters 會更靈活 */ String resourcePattern() default "**/*.class"; /** * 指示是否應啓用對帶有{@code @Component},{@ code @Repository}, * {@ code @Service}或{@code @Controller}註釋的類的自動檢測。 */
boolean useDefaultFilters() default true;
/** * 對被掃描的包或類進行過濾,若符合條件,不論組件上是否有註解,Bean對象都將被建立 * @ComponentScan(value = "com.onestar",includeFilters = { * @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}), * @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {UserDao.class}), * @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}), * @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "spring.annotation..*"), * @ComponentScan.Filter(type = FilterType.REGEX, pattern = "^[A-Za-z.]+Dao$") * },useDefaultFilters = false) * useDefaultFilters 必須設爲 false */
ComponentScan.Filter[] includeFilters() default {};
// 設置排除的過濾條件,指定掃描的時候按照什麼規則排除哪些組件,排除要掃描的包,用法參考includeFilters
ComponentScan.Filter[] excludeFilters() default {};
/** * 指定是否應註冊掃描的Bean以進行延遲初始化。 * @ComponentScan(value = "com.onestar",lazyInit = true) */
boolean lazyInit() default false;
// @Filter註解,用於 includeFilters 或 excludeFilters 的類型篩選器
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
/** * 要使用的過濾器類型,默認爲 ANNOTATION 註解類型 * @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}) */
FilterType type() default FilterType.ANNOTATION;
/** * 過濾器的參數,參數必須爲class數組,單個參數能夠不加大括號 * 只能用於 ANNOTATION 、ASSIGNABLE_TYPE 、CUSTOM 這三個類型 * @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class, Service.class}) * @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {UserDao.class}) * @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}) */
@AliasFor("classes")
Class<?>[] value() default {};
/** * 做用同上面的 value 相同 * ANNOTATION 參數爲註解類,如 Controller.class, Service.class, Repository.class * ASSIGNABLE_TYPE 參數爲類,如 UserDao.class * CUSTOM 參數爲實現 TypeFilter 接口的類 ,如 MyTypeFilter.class * MyTypeFilter 同時還能實現 EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware,ResourceLoaderAware * 這四個接口 * EnvironmentAware * 此方法用來接收 Environment 數據 ,主要爲程序的運行環境,Environment 接口繼承自 PropertyResolver 接口, * 詳細內容在下方 * @Override * public void setEnvironment(Environment environment) { * String property = environment.getProperty("os.name"); * } * * BeanFactoryAware * BeanFactory Bean容器的根接口,用於操做容器,如獲取bean的別名、類型、實例、是否單例的數據 * @Override * public void setBeanFactory(BeanFactory beanFactory) throws BeansException { * Object bean = beanFactory.getBean("BeanName") * } * * BeanClassLoaderAware * ClassLoader 是類加載器,在此方法裏只能獲取資源和設置加載器狀態 * @Override * public void setBeanClassLoader(ClassLoader classLoader) { * ClassLoader parent = classLoader.getParent(); * } * * ResourceLoaderAware * ResourceLoader 用於獲取類加載器和根據路徑獲取資源 * public void setResourceLoader(ResourceLoader resourceLoader) { * ClassLoader classLoader = resourceLoader.getClassLoader(); * } */
@AliasFor("value")
Class<?>[] classes() default {};
/** * 這個參數是 classes 或 value 的替代參數,主要用於 ASPECTJ 類型和 REGEX 類型 * ASPECTJ 爲 ASPECTJ 表達式 * @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "com.onestar..*") * REGEX 參數爲 正則表達式 * @ComponentScan.Filter(type = FilterType.REGEX, pattern = "^[A-Za-z.]+Dao$") */
String[] pattern() default {};
}
}
複製代碼
經過excludeFilters=Filter[ ]
來排除要掃描的包,在配置類註解中修改:
@Configuration
@ComponentScan(value = "com.onestar",excludeFilters = { @Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})})
public class AppConfig {
}
複製代碼
查看源碼咱們能夠得知,經過實現Filter接口的type屬性用於設置過濾類型,默認值爲
FilterType.ANNOTATION
,提供了這幾個過濾類型:
FilterType.ANNOTATION
:按照註解過濾FilterType.ASSIGNABLE_TYPE
:按照給定的類型過濾FilterType.ASPECTJ
:按照ASPECTJ表達式過濾FilterType.REGEX
:按照正則表達式過濾FilterType.CUSTOM
:按照自定義規則過濾
classes和value屬性爲過濾器的參數,必須爲class數組,類只能爲如下三種類型:
- ANNOTATION 參數爲註解類,如
Controller.class, Service.class, Repository.class
- ASSIGNABLE_TYPE 參數爲類,如 UserDao.class
- CUSTOM 參數爲實現 TypeFilter 接口的類 ,如 MyTypeFilter.class
經過上面的excludeFilters
註解配置,我們再來運行一下測試類,打印信息以下,能夠看到,標有@Controller和@Service註解的組件不被掃描:
經過includeFilters=Filter[ ]
只須要包含要掃描的包,在配置類註解中修改,默認的規則是掃描value下全部的包,因此咱們要禁用這個規則:
@Configuration
@ComponentScan(value = "com.onestar",includeFilters = { @Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})},useDefaultFilters = false)
public class AppConfig {
}
複製代碼
useDefaultFilters = false
:禁用默認掃描規則經過上面的includeFilters
註解配置,我們再來運行一下測試類,打印信息以下,能夠看到,標有@Controller和@Service註解的組件才被掃描:
FilterType.CUSTOM
上面提到,能夠使用FilterType.CUSTOM
按照自定義規則進行過濾,經過源碼,咱們知道,能夠實現TypeFilter的實現類來進行自定義過濾
public class MyTypeFilter implements TypeFilter {
/** * @description TODO * @author ONESTAR * @date 2021/1/20 14:29 * @param metadataReader:讀取到當前正在掃描的類的信息 * @param metadataReaderFactory:能夠獲取到其餘任何類的信息 * @throws * @return boolean */
@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();
// 指定包含「serv」的組件進行掃描
if(className.contains("serv")){
return true;
}
return false;
}
}
複製代碼
該實現類經過獲取當前全部資源組件,並進行指定掃描,這裏包含「serv」的組件進行掃描,而後修改配置類指定自定義掃描規則:
@Configuration
@ComponentScan(value = "com.onestar",includeFilters = { @Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})},useDefaultFilters = false)
public class AppConfig {
}
複製代碼
MyTypeFilter.class
:指定自定義過濾參數運行測試類,能夠看到打印信息,包含「serv」的組件進行掃描:
一樣,我們以AnnotationConfigApplicationContext
類爲入口,點進去跟蹤源碼:
經過refresh()跟蹤Bean 工廠的後置處理器調用:
繼續跟蹤,查看源碼,這裏實例化並調用全部已註冊的
BeanFactoryPostProcessor
:
繼續跟蹤,主方法中有一些加載順序之類的,找到invokeBeanDefinitionRegistryPostProcessors,查看調用Bean的註冊定義的後置處理器的方法,進入源碼查看:
繼續跟蹤,這裏有個循環,會把全部的處理器拿出來進行處理:
繼續跟蹤,能夠看到是一個接口,進入查看實現類,咱們能夠看到一個配置Bean的定義,就是指咱們Configuration配置的裏面的一些Bean的定義:
繼續跟蹤,
processConfigBeanDefinitions
類中能夠看到Configuration
類的解析器,經過parser.parse(candidates);
把要掃描的東西進行解析
繼續跟蹤,判斷咱們聲明的是不是一個註解Bean,是的話,再進行解析
繼續跟蹤:
繼續跟蹤,在
processConfigurationClass
方法中找到和配置相關的方法:
繼續跟蹤,能夠看到和@ComponentScan註解註解相關的東西了,咱們進入
componentScanParser.parse
看其具體的解析方法:
繼續跟蹤,查看構造方法:
繼續跟蹤,前面咱們講到
useDefaultFilters
表示默認掃描規則,默認是true:
跟蹤到這就能夠了,就是在這裏將全部標有@Component註解的組件都添加到咱們的includeFiltes裏面去,而
@Controller、@Service、@Repository
註解都標有@Component
註解,因此只要標註了@Controller、@Service、@Repository、@Component註解中的任何一個,其組件都會被自動掃描,加入到容器中