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 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传送门

你可能感兴趣的:(Spring,Boot)