Spring註解驅動(一)IOC容器註冊方式

一、配置類

我們想將一個類註冊到IOC容器中首先要有個配置,一般有基於xml配置文件的方式和註解的方式。

已知Person類

package com.tan.bean;

public class Person {

    private String name;

    private Integer age;
}

1、xml配置的方式(配置文件爲:bean.xml)

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

2、基於註解的方式的配置(等於xml的配置文件)

  •  @Configuration:告訴spring這是一個配置類。

二、註冊方式(給容器中註冊組件的方式)

  1. 包掃描 + 組件標註註解(@Controller,@Service, @Repository,@Component)
  2. @Bean[導入的第三方包裏面的組件]
  3. @Import[快速給容器中導入一個組件]

        1)@Import(要導入到容器中的組件);容器中就會自動註冊這個組件,id默認是全類名

         2)@ImportSelector 返回需要導入的組件的全類名數組

         3)@ImportBeanDefinitionRegistrar 手動將bean註冊到容器中

  1. 使用spring提供的FactoryBean(工廠bean)

1、包掃描 + 組件標註註解(@Controller,@Service, @Repository,@Component)

@ComponentScan:包掃描

@ComponentScan(value = "com.tan", excludeFilters = {
        //@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
        //type:按什麼類型排除,以上是按註解去排除
        //classes:指定掃描的時候按照什麼規則排除哪些組建
        // includeFilters + useDefaultFilters=false 只掃描classes中的組建
        //@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
})

參數解析

value:指定要掃描的包

包過濾掃描:

excludeFilters = Filter[]:指定掃描的時候按照什麼規則排除哪些組件

includeFilters = Filter[]:指定掃描的時候只需要包含哪些組件,(需要關閉默認全部掃描的設置,useDefaultFilters=false)

@Filter中參數解析:

type:按什麼類型排除

 FilterType.ANNOTATION       按照註解進行掃描
 FilterType.ASSIGNABLE_TYPE  按照給定的類型
 FilterType.ASPECTJ          使用ASPECTJ表達式方式
 FilterType.REGEX            使用正則表達式方式
 FilterType.CUSTOM           使用自定義規則

classes:指定掃描的時候按照什麼規則排除哪些組件

自定義過濾掃描包

@ComponentScan(value = "com.tan", excludeFilters = {

        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})

})

自定義掃描規則類實例

package com.tan.config;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;

/**
 * 自定義包掃描過濾規則
 */
public class MyTypeFilter implements TypeFilter{

    /**
     * @param metadataReader 讀取到的當前正在掃描的類的信息
     * @param metadataReaderFactory  可以獲取到其他任何類信息
     * @return
     * @throws IOException
     */
    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")) { //bean名字中包含er字符的返回true
            return true;
        }
        return false;
    }
}

2、@Bean[導入的第三方包裏面的組件]

   @Conditional({MacConditional.class})
   @Scope("prototype")
   @Lazy  //懶加載模式,容器在啓動的時候不會創建,當獲取一次的時候會加載,第二次再進來就不會在new 
            了
    @Bean(value = "person02")
    public Person person() {
        return new Person("張三", 10);
    }
@Bean("person"):給容器註冊一個bean (類型爲返回值的類型,id默認用方法名作爲id)。
@Lazy:懶加載模式,容器在啓動的時候不會創建,當獲取一次的時候會加載,第二次再進來就不會在new了。

Scope 註解調整作用域:

  • @Scope("singleton"):單實例的(默認是單實例的)   ioc容器啓動會調用方法創建對象放到ioc容器中,以後每次獲取就是                                          直接從容器(map.get())中拿。
  • @Scope("prototype"):多實例的ioc啓動並不會去調用方法創建對象放在容器中,而是在每次獲取bean的時候,纔會                                          調用方法創建對象,每次獲取的時候都會調一遍。
  • @Scope("request"):同一次請求創建一個實例(基於web的)。
  • @Scope("session"):同一個session創建一個實例(基於web的)。
@Conditional({Conditional}) :按照一定的條件進行判斷,滿足條件給容器中註冊bean。

   類中組件統一設置,滿足當前條件,這個類中配置的所有bean註冊才能生效,可以放在類上也可以放在方法上。

Conditional實例:

    /**
     * @Conditional({Conditional}) 按照一定的條件進行判斷,滿足條件給容器中註冊bean
     * 如果系統是mac,放這個bean
     * 如果是linux系統就放linus這個bean
     */
    @Conditional({MacConditional.class})
    @Bean(value = "mac")
    public Person person02() {
        return new Person("mac", 40);
    }

    @Conditional({LinuxConditional.class})
    @Bean(value = "linus")
    public Person person03() {
        return new Person("linus", 50);
    }

條件過濾類:

/**
 * 判斷是否是mac系統
 */
public class MacConditional implements Condition{

    /**
     *
     * @param context  判斷條件,能使用的上下文(環境)
     * @param metadata 註釋信息
     * @return
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        //是否mac系統
        //1、能獲取到ioc使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

        //2、獲取類加載器
        ClassLoader classLoader = context.getClassLoader();

        //3、獲取當前環境信息
        Environment environment = context.getEnvironment();

        //4、獲取bean定義的註冊類
        BeanDefinitionRegistry registry = context.getRegistry();

        String property = environment.getProperty("os.name");
        if (property.contains("Mac OS X")) {
            return true;
        }

        return false;
    }
}
MacConditional實現了Condition接口,實現了matches方法,方法中的參數類(ConditionContext)可以獲取到ioc容器的beanfactory,可以獲取類加載器,可以獲取當前環境,可以獲取bean定義的註冊類。

LinuxConditional類同理(略)........

3、@Import[快速給容器中導入一個組件]

導入組件,id默認是組件的全類名

@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})

 1)@Import(要導入到容器中的組件);容器中就會自動註冊這個組件,id默認是全類名。

 2)、@ImportSelector 返回需要導入的組件的全類名數組。

3)@ImportBeanDefinitionRegistrar 手動將bean註冊到容器中。

@ImportSelector註冊方式:

/**
 * 自定義邏輯返回需要導入的組件
 */
public class MyImportSelector implements ImportSelector{


    /**
     * 返回值,就是導入到容器中的組件全類名
     * @param importingClassMetadata  當前標註@Import註解的類的所有註解信息
     * @return
     */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return new String[]{"com.tan.bean.Blue", "com.tan.bean.Yellow"};
    }
}

@ImportBeanDefinitionRegistrar手動註冊方式

/**
 * 手動註冊
 */
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{

    /**
     * @param importingClassMetadata  當前類的註解信息
     * @param registry                BeanDefinition註冊類
     *                                把所有需要添加到容器中的bean調用
     *                                BeanDefinitionRegistry.registerBeanDefinition()手工註冊進來
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        boolean definition1 = registry.containsBeanDefinition("com.tan.bean.Red");
        boolean definition2 = registry.containsBeanDefinition("com.tan.bean.Blue");
        if (definition1 && definition2) {
            //指定bean的定義信息(bean的類型,作用域啥的都可以在這指定)
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Black.class);
            //註冊一個bean,指定bean名
            registry.registerBeanDefinition("black", rootBeanDefinition);
        }
    }
}

4、使用spring提供的FactoryBean(工廠bean)

@Bean public ColorFactoryBean colorFactoryBean() {

return new ColorFactoryBean();

}

實現類:

/**
 * 創建一個spring定義的FactoryBean
 */
public class ColorFactoryBean implements FactoryBean<Color>{

    /**
     * 返回color對象,這個對象會添加到容器中
     * @return
     * @throws Exception
     */
    public Color getObject() throws Exception {

        System.out.println("ColorFactoryBean...getObject...");
        return new Color();
    }

    public Class<?> getObjectType() {

        return Color.class;
    }

    /**
     * 是單實例?
     * true:  這個bean是單實例,在容器中保存一份
     * false: 多實例,每次獲取都會創建一個新的bean
     * @return
     */
    public boolean isSingleton() {
        return false;
    }
}

分析:

使用spring提供的FactoryBean(工廠bean), 默認獲取到的是工廠bean調用getObject創建的對象,以上獲取就是Color的bean

要獲取bean本身,我們需要給id前面加一個& (&colorFactoryBean)才能回去。

springboot中大量使用了這種方式。

三、bean的獲取方式

1、基於xml的方式的獲取

 /*********基於spring xml配置文件式開發**********/
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);

2、基於註解配置文件的方式獲取

/*********基於spring 註解式開發**********/
        ApplicationContext applicationContext= new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = applicationContext.getBean(Person.class);
        System.out.println(person);

        //通過類名稱去找bean的名稱
        String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);

        for (String name : beanNamesForType) {
            System.out.println(name);
        }

 

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