Spring(一)源碼之@Configuration、@ComponentScan、@Bean的使用

最近在整理一些spring相關的知識,就順帶在這記錄一下總結一下,給自己看也希望能幫助到一些正在學習以及一直在學習的夥伴。

本文內容記錄一下@Configuration註解的使用以及它與xml文件是如何對應的。

自從spring3.0開始,@Configuration可以用於定義配置了,也就是替代了過去xml配置,被註解的類內部含有一個或多個Bean,這些方法會被AnnotationConfigApplicationContext或者AnnotationConfigWebApplicationContext加載掃描,並構建Bean從初始化spring容器,類似於加載xml文件ClassPathXmlApplicationContext類加載xml文件。

一、@Bean的使用:使用此註解,表示在容器中註冊一個Bean,類型爲方法返回值的類型,id默認爲是方法名,也可以自行指定id,在xml中類似於一組<bean></bean>標籤,舉個簡單例子:

 <bean id="person" class="bean.Person">
        <property name="name" value="張三"></property>
        <property name="age" value="18"></property>
 </bean>

xml的使用

首先創建一個類並在xml中註冊一個Bean,id爲person,class寫出全路徑,並定義兩個屬性name和age,

在過去我們通過ClassPathXmlApplicationContext來加載xml文件,根據指定bean的id通過getBean(id)來獲取到Bean,例如:

public class MainTes {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Person bean = (Person) applicationContext.getBean("person");
        System.out.println("person:"+bean);
        //打印信息:person:Person{name='張三', age='18'}
    }
}

@Bean註解的使用:

我們需要自定義一個類@Configuration來指定爲配置類等同於xml文件中的<beans></beans>,我們在類中註冊Bean

//配置類==配置文件
@Configuration //告訴spring這是一個配置類
public class MainConfig {
    @Bean(name = "person")  //在容器中註冊一個Bean;類型爲返回值的類型,id默認是用方法名作爲id,也可以自己指定name屬性爲id類型
    public Person person(){
        return new Person("李四","16");
    }
}

通過AnnotationConfigApplicationContext類掃描加載

public class MainTes {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        //根據返回值類型來得到Bean
        Person bean = applicationContext.getBean(Person.class);
        //根據id來得到Bean
        Person bean1 = (Person) applicationContext.getBean("person");
        //獲取bean的id,也就是@Bean(name="person")中name的值,沒有的話就默認爲方法名作爲id。
        String[] str = applicationContext.getBeanNamesForType(Person.class);
        for (String st : str) {
            System.out.println("str:" + st);
        }
        System.out.println("bean:" + bean);
        System.out.println("bean1:" + bean1);
//        打印信息:
//        str:person
//        bean:Person{name='李四', age='16'}
//        bean1:Person{name='李四', age='16'}
    }
}

我們都能加載並得到容器中的Bean

也就是說

@Configuration等同於xml中的<beans></beans>

@Bean等同於xml中的<bean></bean>

二、包掃描組件:只要標註了@Controller、@Service、@Respository、@Component 都會被掃描到

在xml的配置中是這樣<context:component-scan bse-package=""></context:component-scan>,一般我們在指定base-package的時候都是寫包的根目錄,因爲它會自動掃描根目錄 下所有的包,只要標註了以上四個註解的都會被掃描到。

 <context:component-scan base-package="bean"></context:component-scan>
    <bean id="person" class="bean.Person">
        <property name="name" value="張三"></property>
        <property name="age" value="18"></property>
 </bean>

在這我主要記錄一下@ComponentScan的使用:

@ComponentScan等同於上面xml配置文件的掃描組件:會自動掃描包路徑下面的所有@Controller、@Service、@Repository、@Component 的類

@ComponentScan裏的屬性:value指定掃描的包,includeFilters包含那些過濾,excludeFilters不包含那些過濾,useDefaultFilters默認的過濾規則是開啓的,如果我們要自定義的話是要關閉的。其中@Filters是一個過濾器的接口。

value屬性:點開spring定義的註解類ComponentScan我們可以發現value屬性默認爲一個String[]類型

value的值可以一般是掃描包名:

首先我分別創建一個dao,service,controller分別在三個不同包中,注意看下包名,我這爲了方便觀看,將三個類整合到了一起。

package dao;
import org.springframework.stereotype.Repository;
@Repository
public class TestDao {}

/*----------------------------------------------*/

package Controller;
import org.springframework.stereotype.Controller;
@Controller
public class TestController {}


/*----------------------------------------------*/

package service;
import org.springframework.stereotype.Service;
@Service
public class TestService {}

我這掃描Controller、dao,service註解

//配置類==配置文件
@Configuration //告訴spring這是一個配置類
//掃描策略
@ComponentScan(value = {"Controller","dao","service"}) //也可以根目錄掃描的方式,會自動掃描目錄下帶有4個註解的包
public class MainConfig {}
public class AnnoTest {
    @SuppressWarnings("resource")
    @Test
    public void test01(){
        ApplicationContext applicationContext =  new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definition = applicationContext.getBeanDefinitionNames();
        for(String name : definition){
            System.out.println("name:"+name);
            //在打印的信息中我們可以看到上方標註了@Controller @Service @Repository的類名
        }
    }
}

excludeFilters:過濾器,定義了過濾規則,相當於<context:component-scan bse-package=""></context:component-scan>內的子標籤<context:exclude-filter type="" expression=""/>主要的過濾規則爲:

FilterType.ANNOTATION:按照註解

FilterType.ASSIGNABLE_TYPE:按照給定的類型

FilterType.ASPECTJ:使用ASPECTJ表達式

FilterType.REGEX:使用正則

FilterType.CUSTOM:使用自定義規則

例如:type爲按照註解類型,value中指定了不被掃描的註解名稱,我們這樣可以在掃描的時候跳過帶有這兩個註解的類。

//配置類==配置文件
@Configuration //告訴spring這是一個配置類
//掃描策略
@ComponentScan(value = {"Controller","dao","service"},excludeFilters ={
        @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class,Service.class}), //按照註解來過濾
}) //也可以根目錄掃描的方式,會自動掃描目錄下帶有4個註解的包
public class MainConfig {

}

我們查看spring定義註解ComponentScan我們可以找到Filter,我們可以發現spring中默認是註解方式的過濾規則,點開FilterType我們能發現spring定義幾種過濾規則。

includeFilters:與excludeFilters作用相反,掃描時只掃描的包,但是需要注意的是,使用這個的時候需要指定userDefaultFilters = false把默認掃描規則爲false,因爲默認的時true不會生效。

@ComponentScan(value = {"Controller","dao","service"},includeFilters ={
        @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class,Service.class}), //按照註解來過濾
},useDefaultFilters = false) //也可以根目錄掃描的方式,會自動掃描目錄下帶有4個註解的包
public class MainConfig {

}

FilterType.CUSTOM:自定義掃描規則,這個稍微重要點。

我們需要自定義一個Filter實現TypeFilter並重寫match方法,並根據方法返回的布爾類型爲true或false來判斷是否過濾。

例如:指定Filter類型爲自定義類型,值爲自己定義的一個類

//配置類==配置文件
@Configuration //告訴spring這是一個配置類
//掃描策略
@ComponentScan(value = {"Controller","dao","service"},excludeFilters ={
        @ComponentScan.Filter(type = FilterType.CUSTOM,value = {MyFilter.class}) //由自定義的Filter返回的布爾值來過濾
})
public class MainConfig {

}
public class MyFilter implements TypeFilter {
    /**
     * @param metadataReader 讀取到的當前正在掃描的類的信息
     * @param metadataReaderFactory  獲取其他任何類的信息
     * @return
     * @throws IOException
     */
    @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();
        System.out.println("---->"+className);
        if(className.contains("er")){
            return true;
        }
        return false;
    }
}
public class AnnoTest {
    @SuppressWarnings("resource")
    @Test
    public void test01(){
        ApplicationContext applicationContext =  new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definition = applicationContext.getBeanDefinitionNames();
        for(String name : definition){
            System.out.println("name:"+name);
        }
    }
}

打印信息:

---->Controller.TestController
---->dao.d.TesDao1
---->dao.d1.TesDao2
---->dao.TestDao
---->service.TestService

name:tesDao1
name:tesDao2
name:testDao

我們可以看到掃描的類有5個,但是隻要標註了@Repository的沒有被過濾,@Controller和@Service都被過濾掉了,因爲他們包了"er"。

記錄學習筆記算是強制讓自己學習和鞭策自己,同時也希望能夠幫助有需要的夥伴,對於筆記有什麼錯誤的或者不足的希望大家能幫忙糾正一下。萬分感謝!

 

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