Spring Boot自动装配过程解析及简单Demo演示


Spring Boot 是由 Pivotal 团队提供的全新框架,其采用 约定大于配置的方式来大量的减少了普通Spring MVC应用的初始搭建、配置过程。

1、约定大于配置

约定大于配置是一种软件设计范式,简单来说就是简化配置,即用户只需要在Maven-pom文件中添加相关依赖包,在application.yml中配置必要部分(如数据库链接、redis链接、rabbitmq链接等)就行了,可以直接启动项目而不用写一行xml配置。
通过Maven快速创建Spring Boot项目的过程简介如下:https://blog.csdn.net/weixin_44360895/article/details/112060112

2、自动装配原理

Spring Boot通过main方法启动SpringApplication类的静态方法run()来启动项目。

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

2.1、@SpringBootApplication

@SpringBootApplication注解标注是一个Spring Boot应用,它是一个复合注解:
在这里插入图片描述
@SpringBootConfiguration注解其实就携带了一个@Configuration注解,可以认为@SpringBootConfiguration = @Configuration;

2.2、@EnableAutoConfiguration

在这里插入图片描述
@AutoConfigurationPackage是自动配置包注解,作用和@ComponentScan一样,也是将主配置类所在的包及其子包里面的组件扫描到IOC容器中,但是两个注解扫描的对象是不同的。@AutoConfigurationPackage扫描的是@Enitity、@MapperScan等第三方依赖注解标注的类,@ComponentScan只扫描@Controller、@Service、@Component、@Repository等注解标注的类。

2.3、@Import

@Import是自动装配的核心注解,导入AutoConfigurationImportSelector类。
关于@Import注解的作用详见:https://blog.csdn.net/weixin_44360895/article/details/112758122

2.4、AutoConfigurationImportSelector.class

AutoConfigurationImportSelector这个类需要关注的核心就是它其实是实现了ImportSelector接口:
在这里插入图片描述在这里插入图片描述
这个过程是运用了@Import注解的第二种用法,重写了selectImports()方法,返回自动配置类的全类名的string[ ]数组后,这些自动配置类就会被叫给Spring容器管理。

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

2.5、selectImports()方法

selectImports()方法是如何返回自动配置类的全类名的string[ ]数组的呢?
我们先进入其中的getAutoConfigurationEntry()方法,再进入其中的getCandidateConfigurations()方法,可以看到它是通过SpringFactoriesLoader.loadFactoryNames()方法进行加载的。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

我们进入到SpringFactoriesLoader类的loadFactoryNames()方法,发现它是去加载META-INF/spring.factories文件。

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryName = var9[var11];
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

另外注意到SpringFactoriesLoader.loadFactoryNames()方法的第一个参数,传的是EnableAutoConfiguration.class

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

EnableAutoConfiguration类的全类名刚好是META-INF/spring.factories中的key值,
SpringFactoriesLoader会根据这个键去所有spring.factories中找所对应的values,并返回。如mybatis的spring.factories:
在这里插入图片描述
至此Spring Boot自动装配过程结束。

3、简单Demo

下面我们写一个简单的组件,并让Spring Boot自动装配它。
要先明确自动装配的对象:
假如你要自动装配一个女朋友来代替你的右手^_^……
首先可以用创建Spring Boot项目的方式创建一个简单的Maven项目,然后删掉App启动类、application.yml文件、test单元测试包、修改pom.xml文件,整体代码结构如下:
在这里插入图片描述
pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xmotor</groupId>
    <artifactId>girl-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>girl-spring-boot-starter</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--这是Spring Boot的核心启动器,包含了自动配置、日志和YAML。-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!--此注解的作用是给自定义的配置类生成元数据信息,让我们在application.yml中写配置的时候有提示……-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!--打包时忽略启动类-->
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

其中关于spring-boot-configuration-processor依赖的作用具体见:https://blog.csdn.net/weixin_43328357/article/details/106993172

3.1、装配目标

定义一个自动装配的目标类,即你想得到的对象,比如一个女朋友……

//自动配置的目的就是创建这个类
public class Girl {
    private String name;//姓名
    private Integer age;//年龄
    private String face;//颜值

    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;
    }

    public String getFace() {
        return face;
    }

    public void setFace(String face) {
        this.face = face;
    }

    public String doSomething(){
        return "我的女朋友是-- 姓名:"+this.name+",年龄:"+this.age+",颜值:"+this.face;
    }
}

3.2、配置属性类

这个类的作用是获取application.yml中配置的属性值

@ConfigurationProperties(prefix = "com.xmotor.girl")
public class GirlProperties {
    private String name;//姓名
    private Integer age;//年龄
    private String face;//颜值
    private Boolean isCreateGirl;//是否创建女朋友

    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;
    }

    public String getFace() {
        return face;
    }

    public void setFace(String face) {
        this.face = face;
    }

    public Boolean getCreateGirl() {
        return isCreateGirl;
    }

    public void setCreateGirl(Boolean createGirl) {
        isCreateGirl = createGirl;
    }
}

3.3、自动装配类

这个类是给@Import注解去加载的,在此类中创建目标类。

@Configuration
@ConditionalOnClass(Girl.class)//当引入Girl类时创建
@EnableConfigurationProperties(GirlProperties.class)//让GirlProperties属性类生效
public class GirlAutoConfigure {

    @Autowired
    private GirlProperties girlProperties;

    @Bean
    @ConditionalOnMissingBean//当没有此实例时才创建
    @ConditionalOnProperty(prefix = "com.xmotor.girl",value = "isCreateGirl",havingValue = "true")//有这个值时才创建
    public Girl getGirl(){
        Girl girl = new Girl();
        girl.setName(girlProperties.getName());
        girl.setAge(girlProperties.getAge());
        girl.setFace(girlProperties.getFace());
        return girl;
    }
}

3.4、spring.factories文件

在resources资源文件夹下创建META-INF文件夹及spring.factories文件,内容如下:

#-------starter自动装配---------
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xmotor.girlspringbootstarter.GirlAutoConfigure

自此女朋友组件创建完成,通过maven-install安装到本地或maven-deploy发布到依赖仓即可使用。

3.5、使用组件

在pom.xml中引入依赖:

<dependency>
	  <groupId>com.xmotor</groupId>
	  <artifactId>girl-spring-boot-starter</artifactId>
	  <version>0.0.1-SNAPSHOT</version>
</dependency>

在application.yml中配置你的女朋友的属性:

com:
  xmotor:
    girl:
      name: 林志玲
      age: 18
      face: 漂亮
      isCreateGirl: true

随便写个方法看看你创建的女朋友还满意不:

@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    private Girl girl;

    @RequestMapping("/test1")
    public Object test1(){
        String aaa = girl.doSomething();
        System.out.println(aaa);
        return aaa;
    }
}
print: 
我的女朋友是-- 姓名:林志玲,年龄:18,颜值:漂亮

以上就是Spring Boot的自动装配过程解析及简单Demo演示,不足之处欢迎斧正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alex·Guangzhou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值