SpringBoot自动装配核心原理(下)

 

在上个章节中,我们提到了@SpringBootApplication核心原理中十分重要的注解@ConditionalOnClass,解释了SpringBoot实现自动装配的关键步骤,其次对核心源码也做了简要介绍,本章将对SpringBoot外部化配置做详细说明。

首先回到上个章节,笔者提出的问题,很多时候我们引入的组件都是需要我们用户自定义的,比如数据库连接、ip、端口,这些是没有办法确定的,但是这些类却需要自动配置到SpringBoot工厂里面去,因此SpringBoot针对这类东西提出了一个解决方案,叫@EnableConfigurationProperties,从名字就可以看出这又是一种自动配置注解,可能大家都不太熟悉这个注解,我们下面还是用案例的方式说明。

首先新建一个属性类,这个类是用来存放配置信息的

package com.example.boot02.autoconfiguration;


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

import java.util.Map;

@ConfigurationProperties(prefix= Properties.HELLO_FORMAT_PREFIX)
public class Properties {

    public static final String HELLO_FORMAT_PREFIX="com.format";
    private Map<String,Object> info;

    public Map<String, Object> getInfo() {
        return info;
    }

    public void setInfo(Map<String, Object> info) {
        this.info = info;
    }
}

在新建一个类,这个类是上个章节就创建好的

package com.example.boot02.autoconfiguration;


import com.example.boot02.format.FormatProcessor;
import com.example.boot02.format.JsonFormatProcessor;
import com.example.boot02.format.StringFormatProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
public class FormatAutoConfiguration {

    @ConditionalOnMissingClass("com.alibaba.fastjson.JSON")
    @Bean
    public FormatProcessor stringFormat(){
        System.out.println("stringFormat");
        return new StringFormatProcessor();
    }

    @ConditionalOnClass(name = "com.alibaba.fastjson.JSON")
    @Bean
    public FormatProcessor jsonFormat(){
        System.out.println("jsonFormat");
        return new JsonFormatProcessor();
    }

}

最后在创建我们的自动配置类

package com.example.boot02.autoconfiguration;



import com.example.boot02.FormatTemplate;
import com.example.boot02.format.FormatProcessor;
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(FormatAutoConfiguration.class)
@EnableConfigurationProperties({Properties.class})
@Configuration
public class AutoConfiguration {

    @Bean
    public FormatTemplate FormatTemplate(Properties properties, FormatProcessor formatProcessor){
        return new FormatTemplate(properties,formatProcessor);
    }
}

同样的我们的format接口和实现类

package com.example.boot02.format;

public interface FormatProcessor {

    <T> String format();
}
package com.example.boot02.format;



public class JsonFormatProcessor implements FormatProcessor{
    @Override
    public <T> String format() {
        return "JsonFormatProcessor";
    }
}

 

package com.example.boot02.format;

import java.util.Objects;

public class StringFormatProcessor implements FormatProcessor{


    @Override
    public <T> String format() {
        return "StringFormatProcessor";
    }
}

这里在新建一个获取配置信息的类

package com.example.boot02;


import com.example.boot02.autoconfiguration.Properties;
import com.example.boot02.format.FormatProcessor;

public class FormatTemplate {

    private FormatProcessor formatProcessor;

    private Properties properties;

    public FormatTemplate(Properties properties, FormatProcessor formatProcessor) {
        this.properties=properties;
        this.formatProcessor = formatProcessor;
    }

    public <T> String doFormat(){

        StringBuilder stringBuilder=new StringBuilder();
        stringBuilder.append("begin:Execute format").append("<br/>");
        System.out.println(properties.getInfo().toString());

        return stringBuilder.toString();

    }
}

这个类是用来打印我们配置信息的,最后我们还是老规矩,在resouces下面的META-INF下面的spring.factories文件里添加下面的内容

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.example.boot02.autoconfiguration.AutoConfiguration

然后打包,放在我们的Boot1项目下

springboot如何自动下载maven依赖_spring boot

然后我们修改下Boot1的这个Boot01Application类

package com.example.boot01;


import com.example.boot02.FormatTemplate;
import com.example.boot02.autoconfiguration.FormatAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication()
public class Boot01Application {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(Boot01Application.class, args);
        applicationContext.getBean(FormatTemplate.class).doFormat();
    }

}

然后再配置下我们的属性,在application.properties添加

com.format.info.name=Li
com.format.info.age=18

然后我们再测试一下

"C:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.3.5\lib\idea_rt.jar=49255:C:\Program Files\JetBrains\IntelliJ IDEA 2018.3.5\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;C:\Users\HP\IdeaProjects\Spring\boot01\target\classes;C:\Users\HP\.m2\repository\org\springframework\boot\spring-boot-starter\2.4.1\spring-boot-starter-2.4.1.jar;C:\Users\HP\.m2\repository\org\springframework\boot\spring-boot\2.4.1\spring-boot-2.4.1.jar;C:\Users\HP\.m2\repository\org\springframework\spring-context\5.3.2\spring-context-5.3.2.jar;C:\Users\HP\.m2\repository\org\springframework\spring-aop\5.3.2\spring-aop-5.3.2.jar;C:\Users\HP\.m2\repository\org\springframework\spring-beans\5.3.2\spring-beans-5.3.2.jar;C:\Users\HP\.m2\repository\org\springframework\spring-expression\5.3.2\spring-expression-5.3.2.jar;C:\Users\HP\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.4.1\spring-boot-autoconfigure-2.4.1.jar;C:\Users\HP\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.4.1\spring-boot-starter-logging-2.4.1.jar;C:\Users\HP\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\HP\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\HP\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;C:\Users\HP\.m2\repository\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;C:\Users\HP\.m2\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;C:\Users\HP\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;C:\Users\HP\.m2\repository\org\springframework\spring-core\5.3.2\spring-core-5.3.2.jar;C:\Users\HP\.m2\repository\org\springframework\spring-jcl\5.3.2\spring-jcl-5.3.2.jar;C:\Users\HP\.m2\repository\org\yaml\snakeyaml\1.27\snakeyaml-1.27.jar;C:\Users\HP\IdeaProjects\Spring\boot02\target\classes;C:\Users\HP\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar" com.example.boot01.Boot01Application

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.1)

2020-12-24 10:46:40.943  INFO 4616 --- [           main] com.example.boot01.Boot01Application     : Starting Boot01Application using Java 1.8.0_201 on HP-PC with PID 4616 (C:\Users\HP\IdeaProjects\Spring\boot01\target\classes started by HP in C:\Users\HP\IdeaProjects\Spring)
2020-12-24 10:46:40.946  INFO 4616 --- [           main] com.example.boot01.Boot01Application     : No active profile set, falling back to default profiles: default
stringFormat
2020-12-24 10:46:41.274  INFO 4616 --- [           main] com.example.boot01.Boot01Application     : Started Boot01Application in 0.575 seconds (JVM running for 0.983)
{name=Li, age=18}

Process finished with exit code 0

可以看到,我们已经获取到了我们的配置信息,这种操作就好比将数据库连接需要用户配置,然后我们再获取用户的配置信息,已达到实现自定义的功能。然后至于底层逻辑,其实和我们的自动化配置是大差不差的,有兴趣的读者可以自行阅读,这里我就不再深入探究了。小结一下,这三个小的章节对SpringBoot自动配置和外部化配置的核心原理做了详细的介绍,并其核心源码做了简要分析。