SpringBoot自动装配分析总结

@SpringBootApplication三个最重要的注解

组合注解@SpringBootApplication注解中三个最重要的注解:
@SpringBootConfiguration(底层实际是@Configuration)
@ComponentScan
@EnableAutoConfiguration 【最为重要,其用于自动装配】

@SpringBootConfiguration或@Configuration

@SpringBootConfiguration用于指定Springboot的启动类也是一个配置类,该配置类中的@Bean注解标注的方法也会被加载进Spring容器。
因为@SpringBootConfiguration底层也是@Configuration
@Configuration注解可以达到在Spring中使用xml配置文件的beans标签的作用。
@Bean就等同于xml配置文件中的bean标签
@SpringBootConfiguration和@Configuration的唯一区别:@SpringBootConfiguration在自动配置中会被SpringBoot识别

@ComponentScan

@ComponentScan作用是自动扫描并加载符合条件的组件(比如@Service、@Component和@Repository等),最终将它们的bean定义加载到IoC容器中

@EnableAutoConfiguration

SpringBoot中有许多@Enablexxxx开头的注解,@EnableAutoConfiguration注解与这些注解的实现方式是相同的,都是借助@Import注解,将所有符合自动配置条件的bean定义加载到IoC容器

  • @EnableScheduling是通过@Import将Spring调度框架相关的bean定* 义都加载到IoC容器。
  • @EnableZuulProxy是通过@Import将zuul相关的bean定义加载到IoC容器。

@Import

@Import注解的作用:导入指定配置对象到IOC容器
@Import导入配置有三种方式:
(1)导入普通类

@Import(value = {
    
    UserBean.class})
public class UserConfig {
    
    
}

public class Demo {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserConfig.class);
        UserBean user =(UserBean) applicationContext.getBean(UserBean.class.getName());
        System.out.println(UserBean.class.getName());
    }
}
public class UserBean {
    
    
    public UserBean() {
    
    
        System.out.println("========UserBean无参构造器==========");
    }
    @Bean
    public User user(){
    
    
        return new User();
    }
}

(2)导入ImportBeanDefinitionRegistrar接口的实现类,该接口实现类中覆写的方法创建了普通类的BeanDefinition对象

@Import(value = {
    
    CustomImportBeanDefinitionRegistrar.class})
public class UserConfig {
    
    
}

public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
    
    
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(UserBean.class);
        beanDefinitionRegistry.registerBeanDefinition(UserBean.class.getName(), rootBeanDefinition);
    }
}
public class Demo {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserConfig.class);
        UserBean user =(UserBean) applicationContext.getBean(UserBean.class.getName());
        System.out.println(UserBean.class.getName());
    }
}
public class UserBean {
    
    
    public UserBean() {
    
    
        System.out.println("========UserBean无参构造器==========");
    }
    @Bean
    public User user(){
    
    
        return new User();
    }
}

(3)导入ImportSelector接口的实现类型,该接口实现类中覆写的方法中指定了该普通类的完全限定名【这种方法可以导入多个配置类】

@Import(value = {
    
    UserImportSelector.class})
public class UserConfig {
    
    
}
public class UserImportSelector implements ImportSelector {
    
    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    
    
    // 指定配置类的完全限定名,可以有多个,这里存在硬编码
        return new String[]{
    
    "top.onething.demo.config.UserBean"};
    }
}
public class Demo {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserConfig.class);
        UserBean user =(UserBean) applicationContext.getBean(UserBean.class.getName());
        System.out.println(UserBean.class.getName());
    }
}
public class UserBean {
    
    
    public UserBean() {
    
    
        System.out.println("========UserBean无参构造器==========");
    }
    @Bean
    public User user(){
    
    
        return new User();
    }
}

导入ImportSelector接口的实现类这种方式,在上述代码中存在硬编码指定实现类路径的问题。为解决这个问题,SpringBoot默认把需要自动装配的配置类放到了classpath路径下META-INF目录的spring.factories文件中

SpringFactoryiesLoader

  • SpringFactoriesLoader工厂加载机制是Spring内部提供的一个约定俗成的加载方式,只需要在模块的META-INF/spring.factories文件,这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader来实现相应的实现类注入Spirng容器中。
  • SpringFactoryiesLoader会读取META-INF/spring.factories下的EnableAutoConfiguration的配置,紧接着在进行排除【去重,不同key下面可能有相同的类】与过滤【spring.factories中有redis、rabbitmq、kafka等需要的配置类,SpringFactoryiesLoader会全部加装,加装后根据pom文件的依赖过滤掉不需要的配置类】,进而得到需要装配的类

BeanDefinition

BeanDefinition:Bean的描述对象,包括Bean对象的字节码文件、是否懒加载、作用域等 BeanDefinition是接口,AbstractBeanDefinition其抽象实现类
在这里插入图片描述
BeanDefinition描述对象的三个重要属性
(1)class:
(2)autowireMode

#spring的自动装配的方式
//不自动注入(默认是这种模型)
AbstractBeanDefinition.AUTOWIRE_NO
//根据类型自动装配(但是这个类型是根据set***方法进行装配的)
AbstractBeanDefinition.AUTOWIRE_BY_TYPE
//根据名称自动装配
AbstractBeanDefinition.AUTOWIRE_BY_NAME
//根据构造函数自动装配
AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR

(3)constructorArumentValues
通过设置构造器参数来设置spring需要调用哪一个构造器,它会根据参数进行推断

DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) defaultListableBeanFactory.getBeanDefinition("name");
//修改构造参数
ConstructorArgumentValues constructorArgumentValues1 = new ConstructorArgumentValues();
constructorArgumentValues1.addIndexedArgumentValue(0, "第1个参数");
constructorArgumentValues1.addIndexedArgumentValue(1, "第2个参数");
beanDefinition.setConstructorArgumentValues(constructorArgumentValues1);
//修改注入模型
beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_NAME);

获取Bean的描述对象----》BeanFactoryPostProcessor接口【Bean工厂的后置处理器,这就是修改Bean的描述对象】-----》getBean 根据Bean的描述对象创建Bean对象----->BeanPostProcessor接口【Bean对象后置处理器:这是修改的Bean实列对象】----->Bean实例化对象

  • 应用启动后加载字节码文件,通过Spring的包扫描@Controller、@Service、@Dao、@Component、@Configuration、@Bean等注解标注的类或方法,生成他们各自的Bean描述对象,并这些Bean的描述对象存储到BeanDefinitionMap容器中,其中key为Bean的名称、value为Bean的描述对象

  • BeanFactory的实现类具有属性BeanDefinitionMap
    在这里插入图片描述从Spring容器中获取Bean对象

public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
    
    
   BeanDefinition bd = (BeanDefinition)this.beanDefinitionMap.get(beanName);
   if (bd == null) {
    
    
       if (this.logger.isTraceEnabled()) {
    
    
           this.logger.trace("No bean named '" + beanName + "' found in " + this);
       }

       throw new NoSuchBeanDefinitionException(beanName);
   } else {
    
    
       return bd;
   }
}

SpringIO容器是一个单例缓存池,是Bean描述对象的集合

//根据配置类创建容器,容器中的元素即配置类生成的Bea对象
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(@Configuration注解标注的配置类.class);
//根据配置类所在包名创建容器,容器中的元素即配置类生成的Bea对象
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("@Configuration注解标注的配置类所在包名");
Object obj = applicationContext.getBean("bean名称");

总结

  • SpringBoot是怎么自动装配的
    自动装配是利用了SpringFactoriesLoader来加载META-INF/spring.factoires文件里所有配置的EnableAutoConfgruation,它会经过exclude和filter等操作,最终确定要装配的类
  • 自动装配的什么对象
    配置类的Bean定义对象,加载后放入IOC容器

猜你喜欢

转载自blog.csdn.net/user2025/article/details/108304958