Spring Boot自动配置底层源码

SpringBoot自动配置主要是因为  @SpringBootApplication注解,@SpringBootApplication是一个复合注解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}

想明白Spring Boot自动配置需要先知道一些注解的作用,例如注解@Configuration、@Bean、@Component、@Import、@ComponentScan、@Conditional、@ImportResources、@ConfigurationProperties等  想看源码直接看第二部分哈,第一部分是一些注解的说明

第一部分:

@Configuration、@Bean

@Configuration告诉SpringBoot这是一个配置类

例如xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="mySchool" class="com.example.demo.pojo.School">
        <property name="sname" value="aynu"/>
    </bean>

    <bean id="myStudent" class="com.example.demo.pojo.Student">
        <property name="age" value="20"/>
        <property name="name" value="张三"/>
    </bean>
</beans>

Student.java

package com.example.demo.pojo;

/**
 * company: www.abc.com
 * Author: Administrator
 * Create Data: 2019/10/21 0021
 */
public class School {
    public School() {
    }

    public School(String sname) {
        this.sname = sname;
    }

    private String sname;

    public void setSname(String sname) {
        this.sname = sname;
    }

    @Override
    public String toString() {
        return "School{" +
                "sname='" + sname + '\'' +
                '}';
    }
}

School.java

package com.example.demo.pojo;

/**
 * company: www.abc.com
 * Author: Administrator
 * Create Data: 2019/10/21 0021
 */
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }


    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age ;

    }
}

而用@Configuration和@Bean

package com.example.demo.config;


import com.example.demo.pojo.School;
import com.example.demo.pojo.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration   //@Configuration(proxyBeanMethods = false)代理对象存在就新建默认为true  单例   false不会检测MyConfig.class下面的@Bean 所以没有组件依赖是false就会很快
public class MyConfig {


    //@Bean给容器内添加组件,以方法名作为组件的id 默认是单例的
    @Bean
    public School school(){
        return new School("beida");
    }

    @Bean
    public Student student(){
        return new Student("小明",18);
    }




}

测试:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        //返回我们IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);

        //查看容器里面的组件
        String[] beanDefinitionNames = run.getBeanDefinitionNames();

        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }

        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);

        Student student = bean.student();
        Student student2 = bean.student();
        System.out.println(student == student2);


    }

}

结果截图

说明school和student成功被注入到Spring容器中

注意默认是单例的,因为需要解决循环依赖问题,至今我见到都是单例,还没有见过用原型的

@Import

1.给容器自动创建出这两个类型的组件,默认组件的名字就是全类名

package com.example.demo.config;


import ch.qos.logback.core.db.dialect.MySQLDialect;
import com.example.demo.pojo.School;
import com.example.demo.pojo.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//@Import: 给容器自动创建出这两个类型的组件,默认组件的名字就是全类名
@Import({Student.class, MySQLDialect.class})
@Configuration
public class MyConfig {


    @Bean//给容器内添加组件,以方法名作为组件的id 默认是单例的
    public School school(){
        return new School("beida");
    }

    @Bean
    public Student student(){
        return new Student("小明",18);
    }




}

代码:


        //获得组件
        String[] beanNamesForType = run.getBeanNamesForType(Student.class);
        for (String s : beanNamesForType) {
            System.out.println(s);
        }

        MySQLDialect bean1 = run.getBean(MySQLDialect.class);
        System.out.println(bean1);

2.ImportSelector:返回需要导入的组件的全类名数组;把这些类导入到容器内

package com.example.demo.abc;

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

public class MyImportSelector implements ImportSelector {


    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {

        return new String[]{"com.example.demo.pojo.car","com.example.demo.pojo.cat"};
    }
}

AnnotationMetadata是能找到原注解所在位置的所有注解:

Debug如下:

 结果:

3.ImportBeanDefinitionRegistrar 

自定义bean注入容器中

package com.example.demo.abc;

import com.example.demo.pojo.fox;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;


public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

/**
 *     AnnotationMetadata:当前类的注解信息
 *     BeanDefinitionRegistry:BeanDefinition注解类
 *              把所有需要添加到容器中的bean;调用
 *              BeanDefinitionRegistry.registerBeanDefinition手工注册进来
 * */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){

        boolean cat = registry.containsBeanDefinition("com.example.demo.pojo.cat");
        boolean car = registry.containsBeanDefinition("com.example.demo.pojo.car");
        if (car && cat){
            //判断fox.class是否存在 已经指定Bean定义信息,Bean的类型等等
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(fox.class);
            //指定bean名 向容器内注入一个组件
            registry.registerBeanDefinition("fox",rootBeanDefinition);
        }
    }

}

fox类

package com.example.demo.pojo;

public class fox {
}

在MyConfig类中加入

@Import({Student.class, MySQLDialect.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})

@ComponentScan

//包扫描,只要标注了@Controller、@Service、@Repository、@Component、@Configuration、@Bean都会自动扫描加进容器中

例如:@ComponentScan(value = "com.example.demo")

package com.example.demo.config;


import ch.qos.logback.core.db.dialect.MySQLDialect;
import com.example.demo.pojo.People;
import com.example.demo.pojo.School;
import com.example.demo.pojo.Student;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.*;

//@Import: 给容器自动创建出这两个类型的组件,默认组件的名字就是全类名
@Import({Student.class, MySQLDialect.class})
@Configuration
@ConditionalOnMissingBean(name = "cat")  //当没有cat是才会运行下面的代码,就是一个条件判断
//@ConditionalOnBean(name = "cat");
@ImportResource("classpath:bean1.xml")   //导入bean1.xml
@EnableConfigurationProperties(People.class)
//1.开启People配置绑定功能
//2.把这个People这个组件自动注册到容器中  People上面不需要@Component




@ComponentScan(value = "com.example.demo")
//包扫描,只要标注了@Controller、@Service、@Repository、@Component、@Configuration、@Bean都会自动扫描加进容器中
public class MyConfig {


    @Bean//给容器内添加组件,以方法名作为组件的id 默认是单例的
    public School school(){
        return new School("beida");
    }

    @Bean
    public Student student(){
        return new Student("小明",18);
    }




}

我们创建一个类,加入容器中

测试:

package com.example.demo;

import com.example.demo.config.MyConfig;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

class ApplicationTests {

    @Test
    void contextLoads() {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }

    }

}

@ComponentScan(value = "com.example.demo") 

结果就没有 school2 组件

@Conditional     

条件装配:当满足特定的条件,则进行组件注入

alt+h可以看  继承树 如图:

例如:

package com.example.demo.config;


import ch.qos.logback.core.db.dialect.MySQLDialect;
import com.example.demo.pojo.School;
import com.example.demo.pojo.Student;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

//@Import: 给容器自动创建出这两个类型的组件,默认组件的名字就是全类名
@Import({Student.class, MySQLDialect.class})
@Configuration
@ConditionalOnMissingBean(name = "cat")  //当没有cat是才会运行下面的代码,就是一个条件判断
//@ConditionalOnBean(name = "cat");
public class MyConfig {


    @Bean//给容器内添加组件,以方法名作为组件的id 默认是单例的
    public School school(){
        return new School("beida");
    }

    @Bean
    public Student student(){
        return new Student("小明",18);
    }




}

@ImportResources

导入Spring配置文件

package com.example.demo.config;


import ch.qos.logback.core.db.dialect.MySQLDialect;
import com.example.demo.pojo.School;
import com.example.demo.pojo.Student;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

//@Import: 给容器自动创建出这两个类型的组件,默认组件的名字就是全类名
@Import({Student.class, MySQLDialect.class})
@Configuration
@ConditionalOnMissingBean(name = "cat")  //当没有cat是才会运行下面的代码,就是一个条件判断
//@ConditionalOnBean(name = "cat");
@ImportResource("classpath:bean1.xml")   //导入bean1.xml
public class MyConfig {


    @Bean//给容器内添加组件,以方法名作为组件的id 默认是单例的
    public School school(){
        return new School("beida");
    }

    @Bean
    public Student student(){
        return new Student("小明",18);
    }




}

@ConfigurationProperties

配置绑定

package com.example.demo.pojo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
*   只有在容器中的组件,才会拥有SpringBoot提供的强大功能
 */
@Component
@ConfigurationProperties(prefix = "people")
public class People {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

application.properties

#访问端口号
server.port=8080


people.name=xiaohong
people.age=19

测试:

package com.example.demo.web;

import com.example.demo.pojo.People;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebTest {

    @Autowired
    People people;

    @RequestMapping("/people")
    public People people(){
        return people;
    }
}

结果:

@EnableConfigurationProperties 

配置绑定和

package com.example.demo.config;


import ch.qos.logback.core.db.dialect.MySQLDialect;
import com.example.demo.pojo.People;
import com.example.demo.pojo.School;
import com.example.demo.pojo.Student;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

//@Import: 给容器自动创建出这两个类型的组件,默认组件的名字就是全类名
@Import({Student.class, MySQLDialect.class})
@Configuration
@ConditionalOnMissingBean(name = "cat")  //当没有cat是才会运行下面的代码,就是一个条件判断
//@ConditionalOnBean(name = "cat");
@ImportResource("classpath:bean1.xml")   //导入bean1.xml
@EnableConfigurationProperties(People.class)
//1.开启People配置绑定功能
//2.把这个People这个组件自动注册到容器中  People上面不需要@Component
public class MyConfig {


    @Bean//给容器内添加组件,以方法名作为组件的id 默认是单例的
    public School school(){
        return new School("beida");
    }

    @Bean
    public Student student(){
        return new Student("小明",18);
    }




}

第二部分

@SpringBootApplication是复合注解主要是以下三个注解组成@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

@SpringBootConfiguration

里面有一个@Configuration  说明@SpringBootApplication是一个配置类SpringBoot主启动类是一个启动类

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@ComponentScan

//包扫描,只要标注了@Controller、@Service、@Repository、@Component、@Configuration、@Bean都会自动扫描加进容器中

@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}

excludeFilters = {@Filter(  //排除过滤器
type = FilterType.CUSTOM,   //类型是FilterType.CUSTOM
classes = {TypeExcludeFilter.class} //具体是TypeExcludeFilter.class  这些可以不看,都是springboot默认规则
)

是指定扫描的目录:

例如:@SpringBootApplication 就可以写成如下:

@EnableAutoConfiguration

最主要的是@EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}

主要包含了@AutoConfigurationPackage、@Import

@AutoConfigurationPackage

自动配置包

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

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

@Import({Registrar.class})

Registrar.class:

这个是@Import注解的高级用法:registerBeanDefinitions,看上面注解解释就

就是自定义的组件注入到容器中,metadata是获得当前类的所有注解,前面注解也说了

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
        }
    }

这个@AutoConfigurationPackage就是将 当前包(其实就是Application.class)下的所有组件注入到容器中   

@Import({AutoConfigurationImportSelector.class})

这个是最重要的:

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值