深入理解spring註解之@ComponentScan註解

今天主要從以下幾個方面來介紹一下@ComponentScan註解:

  • @ComponentScan註解是什麼

  • @ComponentScan註解的詳細使用


1,@ComponentScan註解是什麼


其實很簡單,@ComponentScan主要就是定義掃描的路徑從中找出標識了需要裝配的類自動裝配到spring的bean容器中



2,@ComponentScan註解的詳細使用


做過web開發的同學一定都有用過@Controller,@Service,@Repository註解,查看其源碼你會發現,他們中有一個共同的註解@Component,沒錯@ComponentScan註解默認就會裝配標識了@Controller,@Service,@Repository,@Component註解的類到spring容器中,好下面咱們就先來簡單演示一下這個例子


在包com.zhang.controller下新建一個UserController帶@Controller註解如下:


package com.zhang.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}


在包com.zhang.service下新建一個UserService帶@Service註解如下:


package com.zhang.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
}


在包com.zhang.dao下新建一個UserDao帶@Repository註解如下:


package com.zhang.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}


新建一個配置類如下:


/**
* 主配置類  包掃描com.zhang
*
* @author zhangqh
* @date 2018年5月12日
*/

@ComponentScan(value="com.zhang")
@Configuration
public class MainScanConfig {
}


新建測試方法如下:


AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainScanConfig.class);
       String[] definitionNames = applicationContext2.getBeanDefinitionNames();
       for (String name : definitionNames) {
           System.out.println(name);
}


運行結果如下:


mainScanConfig
userController
userDao
userService


怎麼樣,包掃描的方式比以前介紹的通過@Bean註解的方式是不是方便很多,這也就是爲什麼web開發的同學經常使用此方式的原因了


上面只是簡單的介紹了@ComponentScan註解檢測包含指定註解的自動裝配,接下來讓我們來看看@ComponentScan註解的更加詳細的配置,在演示詳細的配置之前,讓我們先看看@ComponentScan的源代碼如下:


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
   /**
    * 對應的包掃描路徑 可以是單個路徑,也可以是掃描的路徑數組
    * @return
    */

   @AliasFor("basePackages")
   String[] value() default {};
   /**
    * 和value一樣是對應的包掃描路徑 可以是單個路徑,也可以是掃描的路徑數組
    * @return
    */

   @AliasFor("value")
   String[] basePackages() default {};
   /**
    * 指定具體的掃描的類
    * @return
    */

   Class<?>[] basePackageClasses() default {};
   /**
    * 對應的bean名稱的生成器 默認的是BeanNameGenerator
    * @return
    */

   Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
   /**
    * 處理檢測到的bean的scope範圍
    */

   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;
   /**
    * 控制符合組件檢測條件的類文件   默認是包掃描下的  **/
*.class
    * @return
    */
   String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
   /**
    * 是否對帶有@Component @Repository @Service @Controller註解的類開啓檢測,默認是開啓的
    * @return
    */

   boolean useDefaultFilters() default true;
   /**
    * 指定某些定義Filter滿足條件的組件 FilterType有5種類型如:
    *                                  ANNOTATION, 註解類型 默認
                                       ASSIGNABLE_TYPE,指定固定類
                                       ASPECTJ, ASPECTJ類型
                                       REGEX,正則表達式
                                       CUSTOM,自定義類型
    * @return
    */

   Filter[] includeFilters() default {};
   /**
    * 排除某些過來器掃描到的類
    * @return
    */

   Filter[] excludeFilters() default {};
   /**
    * 掃描到的類是都開啓懶加載 ,默認是不開啓的
    * @return
    */

   boolean lazyInit() default false;
}


a,演示basePackageClasses參數,如我們把配置文件改成如下:


@ComponentScan(value="com.zhang.dao",useDefaultFilters=true,basePackageClasses=UserService.class)
@Configuration
public class MainScanConfig {
}


測試結果如下:


mainScanConfig
userDao
userService


只有userDao外加basePackageClasses指定的userService加入到了spring容器中


b,演示includeFilters參數的使用如下:


在com.zhang.service包下新建一個UserService2類如下:注意沒有帶@Service註解


package com.zhang.service;
public class UserService2 {
}


配置類改成:


@ComponentScan(value="com.zhang",useDefaultFilters=true,
   includeFilters={
       @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
       @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={UserService2.class})
   })
@Configuration
public class MainScanConfig {
}


運行結果如下:


mainScanConfig
userController
userDao
userService
userService2


userService2同樣被加入到了spring容器

新增一個自定義的實現了TypeFilter的MyTypeFilter類如下:


/**
* 自定義過濾
*
* @author zhangqh
* @date 2018年5月12日
*/

public class MyTypeFilter implements TypeFilter {
   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);
       // 檢測名字包含Service的bean
       if(className.contains("Service")){
           return true;
       }
       return false;
   }
}


修改主配置如下:


@ComponentScan(value="com.zhang",useDefaultFilters=true,
   includeFilters={
       @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
       @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
   })
@Configuration
public class MainScanConfig {
}


運行結果如下:


mainScanConfig
userController
userDao
userService
userService2


可以發現同樣userService2被加入到了spring容器中 


好了includeFilters參數就演示到這,另外一個參數excludeFilters和includeFilters用戶一摸一樣,只是他是過濾出不加入spring容器中,感興趣的同學可以自己試試,我這邊就不演示了


總結一下@ComponentScan的常用方式如下


  • 自定掃描路徑下邊帶有@Controller,@Service,@Repository,@Component註解加入spring容器

  • 通過includeFilters加入掃描路徑下沒有以上註解的類加入spring容器

  • 通過excludeFilters過濾出不用加入spring容器的類

  • 自定義增加了@Component註解的註解方式



最後一種方式這邊沒有演示,算留給大家的一個小問題吧,感興趣的同學自己實現下,有疑問也歡迎留言


以上是今天文章的所有內容,歡迎大家吐槽


推薦閱讀


深入理解spring生命週期與BeanPostProcessor的實現原理

250G偷懶必看資料全集


更多優質文章請關注以下公衆號查閱:




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章