SpringBoot自動裝配原理分析之上篇

1、@EnableXXX註解驅動原理

從Spring 3.x開始中有許多@EnableXXX的註解,例如@EnableWebMvc,@EnableAsync,@EnableCaching等待註解,這些註解的意義在於根據需要完成自動裝配所需的bean。自動裝配好比汽車的自動擋一樣,它的實現大致分爲兩種方式,一種是通過自定義註解,另一種是實現相應的接口。

1.1 基於接口實現

一種是通過實現ImportSelector接口,另一種是實現ImportBeanDefinitionRegistrar接口。下面我們先看第一種實現方式。

package com.william.demo3;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/13 14:24
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MilkImportSelector.class)
public @interface EnableMilk {

    Milk.Type type();

    String name();
    
}

接口以及實現類

package com.william.demo3;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/15 16:26
 */


public interface Milk {

    void processMilk();

    void getMilk();


    enum Type{
        MENGNIU,
        YILI
    }

}

package com.william.demo3;

import org.springframework.stereotype.Component;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/15 21:45
 */

@Component
public class MengniuMilk implements Milk {

    @Override
    public void processMilk() {
        System.out.println("加工蒙牛牛奶");
    }

    @Override
    public void getMilk() {
        System.out.println("獲取蒙牛牛奶");
    }
}


package com.william.demo3;

import org.springframework.stereotype.Component;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/15 21:44
 */

@Component
public class YiliMilk implements Milk {

    @Override
    public void processMilk() {
        System.out.println("加工伊利牛奶");
    }

    @Override
    public void getMilk() {
        System.out.println("獲取伊利牛奶");
    }
}

 實現ImportSelector接口

package com.william.demo3;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.Map;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/15 21:49
 */

public class MilkImportSelector implements ImportSelector{

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //獲取EnableServer中所有的屬性方法
        Map<String,Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableMilk.class.getName());
        Milk.Type type = (Milk.Type) annotationAttributes.get("type");
        String [] importClassNames = new String[0];
        switch (type){
            case MENGNIU:
                importClassNames = new String[]{MengniuMilk.class.getName()};
                break;
            case YILI:
                importClassNames = new String[]{YiliMilk.class.getName()};
                break;
        }

        return importClassNames;
    }
}

測試類

package com.william.demo3;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/15 22:11
 */

@Configuration
@EnableMilk(type = Milk.Type.MENGNIU,name = "蒙牛")
public class EnableDemoMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(EnableDemoMain.class);
        context.refresh();
        Milk milk = context.getBean(Milk.class);
        milk.processMilk();
        milk.getMilk();
        
    }
}

代碼示例:github傳送門

第二種實現ImportBeanDefinitionRegistrar接口,方式差不多:

package com.william.demo4;

import com.william.demo3.MilkImportSelector;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/15 25:19
 */
public class MilkImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        ImportSelector importSelector = new MilkImportSelector();
//        String[] slecatedClassNames =  importSelector.selectImports(annotationMetadata);
        RootBeanDefinition beanDefinition=new RootBeanDefinition(importSelector.getClass());
        String beanName= StringUtils.uncapitalize(importSelector.getClass().getSimpleName());
        beanDefinitionRegistry.registerBeanDefinition(beanName,beanDefinition);

    }
}

註解名改爲@EnableNewMilk

package com.william.demo4;

import com.william.demo3.Milk;
import com.william.demo3.MilkImportSelector;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/13 14:24
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MilkImportBeanDefinitionRegistrar.class)
public @interface EnableNewMilk {

    Milk.Type type();

    String name();


}

測試類

package com.william.demo4;

import com.william.demo3.Milk;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: WilliamDream
 * @Description:
 * @Date: 2019/9/15 22:25
 */

@Configuration
@EnableNewMilk(type = Milk.Type.YILI,name = "伊利")
public class EnableRegisterDemoMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(EnableRegisterDemoMain.class);
        context.refresh();
        Milk milk = context.getBean(Milk.class);
        milk.processMilk();
        milk.getMilk();


    }

}

代碼示例:github傳送門

1.2 基於註解方式實現

@Configuration
public class DataSourceConfiguration {

    @Bean
    public DataSource dataSource(){
        return new DataSource();
    }

}

public class DataSource {

    private String driverClassName;
    private String url;
    private String username;
    private String password;
    //geter&seter略

}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DataSourceConfiguration.class)
public @interface EnableDataSource {



}

//測試類
@EnableDataSource
public class EnableDatasourceMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //註冊當前引導類(被@Configuration註解標註)到Spring上下文中
        context.register(EnableDatasourceMain.class);
        context.refresh();
        //獲取名爲dataSource的bean對象
        Object object = context.getBean("dataSource");
        System.out.println(object);
        context.close();
    }
}

 

測試結果爲:可以在控制檯打印出DataSource對象實例。

相關代碼示例:github傳送門

1.3  @Enable模塊驅動原理

1.3.1 裝在@Configuration Class

Spring3.0開始引入了@Configuration註解,此時還未引入@ComponentScan註解。@Configuration配合@Import註解使用,但還不能替換xml中<context:component-scan />自動掃描配置。

CongfigurationClassPostProcessor無論是在XML配置驅動還是在註解驅動的使用場景下,均通過AnnotationConfigUtils#registerAnnotationConfigProcessors方法的執行得到裝載。

 

 

 

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