SpringBoot自动装配源码解析

SpringBoot是什么

SpringBoot是一个脚手架


Spring 纯注解方式去定义bean

package cn.edu.guet.config;

import org.springframework.context.annotation.*;

/**
 * @author pangjian
 * @ClassName config
 * @Description MyConfig就是一个配置类,你可以抽象理解它为xml定义bean方式里面的配置文件
 * @date 2021/8/30 8:50
 */
@Configuration
public class MyConfig {

	// 这个相当于xml定义bean方式里面的<bean id="" class=""/>
	// 最后被容器扫描并解析变成一个bean定义对象(BeanDefinition对象)
	// 如果是xml文件,最后也是读取<bean/> 标签去解析变成一个bean定义对象
    @Bean
    public MyBean myBean(){
        return new MyBean();
    }

}
  • 比对这启动IOC容器的方式,就能知道纯注解的方式去定义bean了
public class App {

    public static void main(String[] args) {
        // ApplicationContext context = new ClassPathXmlApplicationContext("配置文件.xml");
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        MyBean myBean = context.getBean("myBean", MyBean.class);
        System.out.println(myBean.toString());
    }
}

SpringBoot自动装配

  • 怎么叫自动

通过批量的机制去读取我们meta-inf/spring.factoryies下面的约定配置

  • 装配什么

装配bean定义(beanDefinition),对象装配到IOC容器,并且对象也要装配到对应的变量

// 没有去通过@Bean配置bean了,也没有加@Component,为什么还能加载出来
@Configuration
@Import({MyBeanPostProcess.class, Boy.class, MyBean.class})
public class MyConfig {
    
}
public class App {
	// 就是因为配置类用了@Import注解,把我们需要的组件进行了导入
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        MyBean myBean = context.getBean("myBean", MyBean.class);
        System.out.println(myBean.toString());
    }
}
  • @Import

@lmport也是为我们容器中导入bean定义,Springboot自动装配要理解该注解。

SpringBoot大概实现就是使用@Import注解导入一个选择器,去扫描meta-info/spring.factory中org.springframework.boot.autoconfigure.EnableAutoConfiguration键对应的值(各种框架的配置类全限定名,如redis等),然后通过该值创建配置类(特殊的bean,就是上面纯注解方法定义bean的配置类),然后第三方框架的bean就会交给SpringIOC容器管理。

源码解读

  • 从SpringBoot启动类去进行解读
// 我们先看看@SpringBootApplication这个复合注解
@SpringBootApplication
public class PethouseApplication {

    public static void main(String[] args) {
        SpringApplication.run(PethouseApplication.class, args);
    }

}
  • @SpringBootApplication
// 重点解读下面三个注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
	// 略
}

@SpringBootConfiguration

就是为了可以让我们在启动类里面纯注解配置Bean

@SpringBootApplication
public class PethouseApplication {

    public static void main(String[] args) {
        SpringApplication.run(PethouseApplication.class, args);
    }
    
    @Bean
    public Admin getAdmin() {
        return new Admin();
    }
    
}

@ComponentScan

就是为了扫描启动类所在的包和子包下面的注解类,就是我们写的@Controller、@Service、@Component等修饰的类,扫描然后将beanDefinition装配到Ioc容器中

@EnableAutoConfiguration

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

它在IOC容器导入了一个AutoConfigurationImportSelector.class的类的类定义,看看这个类有什么用,它是一个选择器,可以通过selectImports()批量导入全类名字符串放到一个数组里面。通过全类名就可以导入该组件。SpringBoot就是通过该方法,把spring.factory里面的109约定好的配置读取出来。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	// 
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
	        if (!this.isEnabled(annotationMetadata)) {
	            return NO_IMPORTS;
	        } else {
	        	// 获取配置的入口方法 getAutoConfigurationEntry()
	            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
	            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	        }
	    }
}
  • getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	// 获取后选的配置,就是可以要或不要
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = getConfigurationClassFilter().filter(configurations);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}
  • getCandidateConfigurations()

获取后选配置

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
			getBeanClassLoader());
	return configurations;
}
  • loadFactoryNames()
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
	// 获取类全限定名org.springframework.boot.autoconfigure.EnableAutoConfiguration
    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
  • loadSpringFactories()
// Map<类全限定名org.springframework.boot.autoconfigure.EnableAutoConfiguration,109个约定配置的全限定名数组>
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	// 从缓存中读取配置
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
    	// 略
	}
}

第一次打开spring.factories的时候(第一次就是容器初始化,监听器构建时候,是比bean装配要早)就会全部加载里面的全部配置(包括了Rabbitmq的等)到缓存中了,第二次我们直接拿就可以了,我们再回到获取后选配置方法。

在这里插入图片描述

在这里插入图片描述

  • getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	// 获取后选的配置,就是可以要或不要
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	// 进行去重,防止你自己写了一个同名的启动器
	configurations = removeDuplicates(configurations);
	// 从109个约定好的配置中排除你不需要的
	// 你可以在@SpringBootApplication用它的excludeName属性去排除你不需要的配置,比如redis等
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	// 根据你pom.xml文件的依赖进行过滤,比如你pom没有rabbitmq的依赖,它就会进行一个自动排除。
	configurations = getConfigurationClassFilter().filter(configurations);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

由于spring-data jpa和mybatis是竞争关系,所以约定配置文件中并没有mybatis的启动器,就需要我们自动导入,所有一般SpringBoot整合mybatis时候导入一个启动器就完成整合了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值