Spring Boot註解學習之@SpringBootApplication(二)
@ComponentScan屬性basePackages 與 valuebasePackageClassesincludeFiltersexcludeFilters新增自定義過濾規則@Component@ComponentScanscontext:component-scanSpringBootApplication 註解中4個方法小結參考文獻
@ComponentScan
在講述 @Configuration
啟動容器+@Component
註冊 Bean 小節中簡單介紹了@ComponentScan
註解的使用。
@ComponentScan
的功能其實就是自動掃描並載入符合條件的元件或 bean 定義,最終將這些 bean 定義載入到容器中。我們可以通過 backPackages 等屬性指定@ComponentScan 自動掃描的範圍,如果不指定,則預設 Spring 框架實現從宣告@ComponentScan 所在類的 package 進行掃描,預設情況下是不指定的,所以 SpringBoot 的啟動類最好在 root package 下。
首先看下@ComponentScan
的原始碼:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
"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 {};
}
}
複製程式碼
重點有以下幾個屬性:
- basePackages 與 value:用於指定包的路徑進行掃描;
- basePackageClasses:用於指定某個類的包的路徑進行掃描;
- nameGenerator:bean 的名稱的生成器;
- useDefaultFilters:是否開啟對@Component,@Repository,@Service,@Controller 的類進行檢測,預設為 true;
- includeFilters:包含的過濾條件 FilterType.ANNOTATION:按照註解過濾; FilterType.ASSIGNABLE_TYPE:按照給定的型別;FilterType.ASPECTJ:使用 ASPECTJ 表示式;FilterType.REGEX:正則;FilterType.CUSTOM:自定義規則 。
- excludeFilters:排除的過濾條件,用法和 includeFilters 一樣。
屬性
basePackages 與 value
@ComponentScan(basePackages = “”) //單個
@ComponentScan(basePackages = {“com.example.dao”,“aaa”,“…”}) //多個
//value 同理
複製程式碼
注意:可以省略 “ basePackages = ”。
@Configuration
@ComponentScan("com.example.dao")
public class MyConfig {}
"com.example.dao","com.example.service")
"com.example.*") //萬用字元匹配所有的包
MyConfig {}
複製程式碼
basePackageClasses
@ComponentScan(basePackageClasses = “”) @ComponentScan(basePackageClasses = {“HelloController.class”,“bbb”,“…”}) //多個
複製程式碼
注意:不可以省略“basePackageClasses =”
@ComponentScan(basePackageClasses = HelloController.class)
MyConfig {
}
複製程式碼
includeFilters
接下來我們用以下程式碼進行測試。
Repository 的註解類:
package com.example.dao;
@Repository
BusinessDAO {
public void update(){
System.out.println("呼叫了 dao 層的 update 方法....");
}
}
複製程式碼
Service 的註解類:
package com.example.service;
@Service
BusinessService {
@Autowired
private BusinessDAO businessDAO;
service(){
System.out.println("呼叫了 service 層的 service() 方法 .....");
businessDAO.update();
}
}
複製程式碼
Controller 的註解類:
package com.example.controller;
@Controller
BusinessController {
private BusinessService service;
request() {
System.out.println(" 呼叫了 Controller 的 request() 方法...");
service.service();
}
}
複製程式碼
Configuration 配置類:
package com.example.configuration;
//@ComponentScan(basePackages = {"com.example.dao","com.example.service","com.example.controller"})
@ComponentScan(value = {"com.example.service",159); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-string">"com.example.controller"},
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Repository.class)},
useDefaultFilters = false)
ScanConfig {
}
複製程式碼
測試類程式碼:
ScanConfigTest {
static main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanConfig.class);
String[] definitionNames = context.getBeanDefinitionNames();
for(String name:definitionNames){
System.out.println(name);
}
}
}
複製程式碼
執行結果為:
19:22:48.157 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig'
19:22:48.162 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO'
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
scanConfig
businessDAO
複製程式碼
根據結果分析: 除了 spring 本身註冊的一些 bean 之外,可以看到最後一行,已經將 ScanConfig 這個類和 BusinessDAO 註冊進容器中了。
includeFilters 的引數是一個 Filter[] 陣列,然後指定 FilterType 的型別為 ANNOTATION,也就是通過註解來過濾,最後的 value 則是 Repository 註解類。配置之後,在 spring 掃描的時候,就會篩選 com.example 下的三個包中,所有被 @Repository 註解標註的類。
注意:需要設定 useDefaultFilters 為 false,否則@ComponentScan 註解會將 被 @Component、@Repository、@Service 和 @Controller 標註的類都註冊進容器中。同時因為我們在 Controller 和 Service 註解的類中關聯了其他的類(@Autowired),所以最好在 includeFilters 的 Filter 屬性中不要設定為 Controller.class 或 Service.class。
excludeFilters
修改配置類:
ScanConfig {
}
複製程式碼
同 excludeFilters 屬性類似, 配置之後,在 spring 掃描的時候,就會跳過 com.example 下的三個包中,所有被 @Controller 註解標註的類。
執行結果為:
19:44:39.191 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig'
19:44:39.195 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO'
19:44:39.195 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessService'
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
scanConfig
businessDAO
businessService
複製程式碼
新增自定義過濾規則
在前面使用過 @Filter 註解,裡面的 type 屬性是一個 FilterType 的列舉型別:
enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM
}
複製程式碼
使用 CUSTOM 型別,就可以實現自定義過濾規則。
package com.example.filter;
CustomTypeFilter implements TypeFilter {
@Override
boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 獲取當前掃描到的類的註解元資料
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 獲取當前掃描到的類的元資料
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 獲取當前掃描到的類的資源資訊
Resource resource = metadataReader.getResource();
if (classMetadata.getClassName().contains("Business")){
return true;
}
false;
}
}
複製程式碼
增加 Service 註解修飾的類:
UserService {
}
複製程式碼
修改配置類:
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = CustomTypeFilter.class)})
ScanConfig {
}
複製程式碼
執行測試類程式碼,結果為:
19:56:25.419 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig'
19:56:25.424 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
scanConfig
userService
複製程式碼
這裡簡單對掃描到的類名進行判斷,如果類名包含”Business“的就符合條件,就會被剔除,不會注入到容器中。
@Component
1、不指定 bean 的名稱,預設為類名首字母小寫 university
@Component
BeanWithComponent {
sayHello(){
System.out.println("BeanWithComponent sayHello...");
}
start(){
System.out.println("BeanWithComponent 初始化。。。");
}
cleanUp(){
System.out.println("BeanWithComponent 銷燬。。。");
}
}
複製程式碼
獲取 bean 方式:
@Autowired
BeanWithComponent beanWithComponent;
複製程式碼
或
ApplicationContext context = new AnnotationConfigApplicationContext(ComfigureWithScan.class);
BeanWithComponent bean = (BeanWithComponent) context.getBean("beanWithComponent");
複製程式碼
2、指定 bean 的名稱
@Component("bean2")
BeanWithComponent {
}
複製程式碼
獲取 bean 方式:
@Autowired
BeanWithComponent bean2;
複製程式碼
或
"bean2");
複製程式碼
總結
@ComponentScan
註解有以下特性:
自定義掃描路徑下邊帶有
@Controller
,@Service
,@Repository
,@Component
註解加入 spring 容器通過 includeFilters 加入掃描路徑下沒有以上註解的類加入 spring 容器
通過 excludeFilters 過濾出不用加入 spring 容器的類
自定義增加了@Component 註解的註解方式
接下來講述一下關於@ComponentScan 註解的兩個擴充套件。
@ComponentScans
原始碼如下:
@Documented
@interface ComponentScans {
ComponentScan[] value();
}
複製程式碼
配置類:
@ComponentScans(value = "com.example.dao"))
ScanConfig {
}
複製程式碼
執行結果為:
20:07:45.908 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig'
20:07:45.914 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO'
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
scanConfig
businessDAO
複製程式碼
context:component-scan
上述程式碼我們是通過@Configuration
+ @ComponentScan
註解來實現 spring 載入,當然也可以在配置檔案中加上掃描的配置。
spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--<context:component-scan base-package="com.example.dao" annotation-config="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>-->
<context:component-scan base-package="com.example.dao" />
<context:component-scan base-package="com.example.controller" />
<context:component-scan base-package="com.example.service" />
</beans>
複製程式碼
修改測試類程式碼:
main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
String[] definitionNames = context.getBeanDefinitionNames();
for(String name:definitionNames){
System.out.println(name);
}
}
}
複製程式碼
執行得到以下結果:
14:59:12.773 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO'
14:59:12.778 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessController'
14:59:12.789 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessService'
14:59:12.789 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
businessDAO
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
businessController
businessService
userService
複製程式碼
關於 的使用,關於屬性的詳細講解可以參考:詳解
注意: 中有一個 annotation-config 屬性,該屬性主要是隱式地向 Spring 容器註冊 internalConfigurationAnnotationProcessor
、internalAutowiredAnnotationProcessor
、internalCommonAnnotationProcessor
、internalEventListenerProcessor
以及 internalEventListenerFactory
這五個 bean 類。
當 annotation-config 屬性值設為 false 時,即不會註冊這五個類。但是@ComponentScan
註解沒有該屬性。 這是因為在幾乎所有情況下,使用@ComponentScan
時,都假定使用預設的註釋配置處理(例如,處理@Autowired 和 friends )。 此外,在使用AnnotationConfigApplicationContext
時,註釋配置處理器始終會被註冊,這意味著在@ComponentScan
級別禁用它們的任何嘗試都將被忽略。
SpringBootApplication 註解中4個方法
@SpringBootApplication不僅包括上面的三個重要註解,還包含有4個方法:
-
Class[] exclude() default {};
根據 Class 來排除特定的類加入 Spring 容器,傳入引數是 class 型別; -
String[] excludeName() default {};
根據 Class Name 排除特定的類加入 Spring 容器,傳入引數是 class 的全類名字字串陣列; -
String[] scanBasePackages() default {};
指定掃描包,引數是包名的字串陣列; -
Class[] scanBasePackageClasses() default {};
指定掃描包,引數是 Class 型別陣列。
小結
這裡總結下@SpringBootApplication
中的三個重要註解的特徵:
@Configuration
定義 Spring Ioc 容器的配置類。
@EnableAutoConfiguration
從 classpath 中搜尋所有 META/spring.factories 配置檔案,並將其中org.springframework.boot.autoconfigure.EnableAutoConfiguration
對應的配置項,也就是一個自動配置類列表載入到 Ioc 容器中。 簡單說,就是@EnawebleAutoConfiguration
讓 Spring Boot 根據類路徑下的 jar 包依賴為當前專案進行自動配置,例如,添加了 spring-boot-starter-web 依賴,會自動新增 Tomcat 和 Spring MVC 的依賴。而對於所有標註@Configuration
的配置類,統一使用ConfigurationClassParser
解析的。
@ComponentScan
自動掃描並載入符合條件的元件或者 bean 定義。