事業(yè)單位網(wǎng)站建設(shè)工作方案sem百度競(jìng)價(jià)推廣
文章目錄
- SpringBoot依賴(lài)管理特性
- 依賴(lài)管理
- 開(kāi)發(fā)導(dǎo)入starter場(chǎng)景啟動(dòng)器
- SpringBoot自動(dòng)配置特性
- 自動(dòng)配好Tomcat
- 自動(dòng)配好SpringMVC
- 默認(rèn)的包結(jié)構(gòu)
- 各種配置擁有默認(rèn)值
- 按需加載所有自動(dòng)配置項(xiàng)
- SpringBoot注解底層注解
- @Configuration
- @Import導(dǎo)入組件
- @Conditional條件裝配
- @ImportResource導(dǎo)入Spring配置文件
- @ConfigurationProperties配置綁定
- 自動(dòng)配置
- @SpringBootApplication的源碼
- @SpringBootConfiguration
- @ComponentScan
- @EnableAutoConfiguration
- 自動(dòng)包規(guī)則原理
- @AutoConfigurationPackage
- 初始加載自動(dòng)配置類(lèi)
- @Import(AutoConfigurationImportSelector.class)
- @Import(AutoConfigurationImportSelector.class)
- 按需配置的例子
- 自動(dòng)配置流程
SpringBoot依賴(lài)管理特性
依賴(lài)管理
我們?cè)贖elloWorld中導(dǎo)入的父項(xiàng)目依賴(lài)
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version>
</parent>
spring-boot-starter-parent的父項(xiàng)目中的一個(gè)依賴(lài)如下:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.3.4.RELEASE</version>
</parent>
spring-boot-dependencies幾乎聲明了所有開(kāi)發(fā)中常用的依賴(lài)的版本號(hào),自動(dòng)版本仲裁機(jī)制
- 導(dǎo)入spring-boot-starter-parent父項(xiàng)目是用來(lái)做依賴(lài)管理的,這樣我們導(dǎo)入依賴(lài)無(wú)需關(guān)注版本號(hào),自動(dòng)版本仲裁
- 引入依賴(lài)默認(rèn)都可以不寫(xiě)版本
- 引入非版本仲裁的jar,要寫(xiě)版本號(hào)。
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>
-
可以修改默認(rèn)版本號(hào)
-
查看spring-boot-dependencies里面規(guī)定當(dāng)前依賴(lài)的版本 用的 key。
-
在當(dāng)前項(xiàng)目里面重寫(xiě)配置,如下面的代碼。
-
<properties><mysql.version>5.1.43</mysql.version>
</properties>
開(kāi)發(fā)導(dǎo)入starter場(chǎng)景啟動(dòng)器
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 見(jiàn)到很多 spring-boot-starter-* : *就某種場(chǎng)景
- web就是我們開(kāi)發(fā)web應(yīng)用的場(chǎng)景
- 只要引入starter,這個(gè)場(chǎng)景的所有常規(guī)需要的依賴(lài)我們都自動(dòng)引入
- 更多SpringBoot所有支持的場(chǎng)景
- 當(dāng)我們覺(jué)得不夠用,還可以自己創(chuàng)建starter啟動(dòng)器
- 見(jiàn)到的 *-spring-boot-starter: 第三方為我們提供的簡(jiǎn)化開(kāi)發(fā)的場(chǎng)景啟動(dòng)器。
所有場(chǎng)景啟動(dòng)器最底層的依賴(lài)
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.3.4.RELEASE</version><scope>compile</scope>
</dependency>
SpringBoot自動(dòng)配置特性
這里我們以我們導(dǎo)入的web場(chǎng)景下的starter為例子
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
自動(dòng)配好Tomcat
- 引入Tomcat依賴(lài)。
- 配置Tomcat
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><version>2.3.4.RELEASE</version><scope>compile</scope>
</dependency>
自動(dòng)配好SpringMVC
- 引入SpringMVC全套組件
- 自動(dòng)配好SpringMVC常用組件(功能)
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.9.RELEASE</version><scope>compile</scope>
</dependency>
- 自動(dòng)配好Web常見(jiàn)功能,如:字符編碼問(wèn)題
- SpringBoot幫我們配置好了所有web開(kāi)發(fā)的常見(jiàn)場(chǎng)景
public static void main(String[] args) {//1、返回我們IOC容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);//2、查看容器里面的組件String[] names = run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}
}
//部分輸出結(jié)果
tomcatWebServerFactoryCustomizer
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
characterEncodingFilter--------設(shè)置字符編碼的過(guò)濾器
localeCharsetMappingsCustomizer
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
multipartConfigElement
multipartResolver
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
org.springframework.aop.config.internalAutoProxyCreator
我們之前想解決中文亂碼問(wèn)題,需要我們自己配置字符編碼過(guò)濾器,有了SpringBoot,我們都不需要設(shè)置
默認(rèn)的包結(jié)構(gòu)
- 主程序(也就是被我們@SpringBootApplication修飾的類(lèi))所在包及其下面的所有子包里面的組件都會(huì)被默認(rèn)掃描進(jìn)來(lái)
- 自動(dòng)代替我們之前Ioc容器xml文件中配置的自動(dòng)掃描組件
- 無(wú)需以前的包掃描配置
- 想要改變掃描路徑
- @SpringBootApplication(scanBasePackages=“com.lun”)
- 為什么不能直接通過(guò)@ComponentScan 指定掃描路徑 因?yàn)槲覀兊?#64;SpringBootApplication()是一個(gè)合成注解
@SpringBootApplication(scanBasePackages="com.lsc")
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.lun")
各種配置擁有默認(rèn)值
- 默認(rèn)配置最終都是映射到某個(gè)類(lèi)上,如:
MultipartProperties
#設(shè)置服務(wù)器的端口號(hào)
server.port=8080
#設(shè)置文件上傳的最大大小
spring.servlet.multipart.max-file-size=10MB
配置文件的值最終會(huì)綁定每個(gè)類(lèi)上,因?yàn)檫@個(gè)類(lèi)會(huì)在容器中創(chuàng)建對(duì)象
按需加載所有自動(dòng)配置項(xiàng)
- 我們的SpringBoot非常多的starter
- 引入了哪些場(chǎng)景這個(gè)場(chǎng)景的自動(dòng)配置才會(huì)開(kāi)啟
- SpringBoot所有的自動(dòng)配置功能都在 spring-boot-autoconfigure 包里面
- 但是不是所有的配置都會(huì)生效,至于原理需要了解相關(guān)注解的原理
我們的spring-boot-starter-web中存在spring-boot-starter依賴(lài)
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.3.4.RELEASE</version><scope>compile</scope>
</dependency>
我們的spring-boot-starter中存在spring-boot-autoconfigure
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>2.3.4.RELEASE</version><scope>compile</scope>
</dependency>
SpringBoot注解底層注解
@Configuration
表示當(dāng)前這個(gè)類(lèi)是配置類(lèi),類(lèi)似我們xml配置文件的作用
/*** @author lsc* #Description MyConfig* #Date: 2023/2/11 11:11*/
@Configuration(proxyBeanMethods = true)
public class MyConfig {@Bean//給容器中添加組件。以方法名作為組件的id。返回類(lèi)型就是組件類(lèi)型。返回的值,就是組件在容器中的實(shí)例public User user01(){User user = new User(1, "劉頌成");user.setCat(tomcat());return user ;}@Bean("tomcat")//如果不想以方法名為名稱(chēng),可以自己自定義public Cat tomcat(){return new Cat("tomcat");}
}
- 配置類(lèi)里面使用@Bean標(biāo)注在方法上給容器注冊(cè)組件,默認(rèn)也是單實(shí)例的
- 配置類(lèi)本身也是組件
- proxyBeanMethods:代理bean的方法
-
Full(proxyBeanMethods = true)(保證每個(gè)@Bean方法被調(diào)用多少次返回的組件都是單實(shí)例的)(默認(rèn))
-
Lite(proxyBeanMethods = false)(每個(gè)@Bean方法被調(diào)用多少次返回的組件都是新創(chuàng)建的)
-
測(cè)試
@SpringBootApplication(scanBasePackages = "com.lsc")
public class MainApplication {public static void main(String[] args) {//1、返回我們IOC容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);//2、查看容器里面的組件String[] names = run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}//3、從容器中獲取組件Pet tom01 = run.getBean("tom", Pet.class);Pet tom02 = run.getBean("tom", Pet.class);System.out.println("組件:"+(tom01 == tom02));//4、com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca@1654a892MyConfig bean = run.getBean(MyConfig.class);System.out.println(bean);//如果@Configuration(proxyBeanMethods = true) 代理對(duì)象調(diào)用方法。SpringBoot總會(huì)檢查這個(gè)組件是否在容器中有。//保持組件單實(shí)例User user = bean.user01();User user1 = bean.user01();System.out.println(user == user1);//trueUser user01 = run.getBean("user01", User.class);Cat tom = run.getBean("tomcat", Cat.class);System.out.println("用戶的寵物:"+(user01.getPet() == tom));//true}
}
- 最佳實(shí)戰(zhàn)
- 配置 類(lèi)組件之間無(wú)依賴(lài)關(guān)系用Lite模式加速容器啟動(dòng)過(guò)程,減少判斷
- 配置 類(lèi)組件之間有依賴(lài)關(guān)系,方法會(huì)被調(diào)用得到之前單實(shí)例組件,用Full模式(默認(rèn))
@Import導(dǎo)入組件
-
@Bean、@Component、@Controller、@Service、@Repository,它們是Spring的基本標(biāo)簽,在Spring Boot中并未改變它們?cè)瓉?lái)的功能。
-
@ComponentScan表示掃描組件,來(lái)指定要掃描的包
@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false)
public class MyConfig {@Bean//給容器中添加組件。以方法名作為組件的id。返回類(lèi)型就是組件類(lèi)型。返回的值,就是組件在容器中的實(shí)例public User user01(){User user = new User(1, "劉頌成");user.setCat(tomcat());return user ;}@Bean("tomcat")//如果不想以方法名為名稱(chēng),可以自己自定義public Cat tomcat(){return new Cat("tomcat");}
}
- @Import自動(dòng)導(dǎo)入對(duì)應(yīng)的類(lèi),利用其無(wú)參構(gòu)造方法創(chuàng)建一個(gè)Bean給容器進(jìn)行管理
- @Import({User.class, DBHelper.class})給容器中自動(dòng)創(chuàng)建出這兩個(gè)類(lèi)型的組件、默認(rèn)組件的名字就是全類(lèi)名
測(cè)試類(lèi):
@SpringBootApplication(scanBasePackages = "com.lsc")
public class MainApplication {public static void main(String[] args) {//返回的是IOC容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class,args);String[] beanNamesForType = run.getBeanNamesForType(User.class);for (String s : beanNamesForType) {System.out.println(s);}DBHelper bean1 = run.getBean(DBHelper.class);System.out.println(bean1);}
}
//
com.lsc.springboot.pojo.User
user01
ch.qos.logback.core.db.DBHelper@73d6d0c
@Conditional條件裝配
條件裝配:滿足Conditional指定的條件,則進(jìn)行組件注入
- ConditionalOnBean,當(dāng)容器存在某個(gè)Bean,才會(huì)進(jìn)行注入以下的對(duì)象
- ConditionalOnMissingBean,當(dāng)容器不存在某個(gè)Bean,才會(huì)進(jìn)行注入以下的對(duì)象
用@ConditionalOnBean舉例說(shuō)明
/*** @author lsc* #Description MyConfig* #Date: 2023/2/11 11:11*/
@Configuration(proxyBeanMethods = true)
@ConditionalOnBean(name = "tomcat")//只有當(dāng)有tom名字的Bean時(shí),MyConfig類(lèi)的Bean才能生效。
public class MyConfig {@Bean//給容器中添加組件。以方法名作為組件的id。返回類(lèi)型就是組件類(lèi)型。返回的值,就是組件在容器中的實(shí)例public User user01(){User user = new User(1, "劉頌成");user.setCat(tomcat());return user ;}
// @Bean("tomcat")//如果不想以方法名為名稱(chēng),可以自己自定義public Cat tomcat(){return new Cat("tomcat");}
}
- 現(xiàn)在我們的容器中是不存在user01這個(gè)bean
- 對(duì)應(yīng)的條件裝配注解可以放在方法上也可以放在類(lèi)上
@SpringBootApplication(scanBasePackages = "com.lsc")
public class MainApplication {public static void main(String[] args) {//返回的是IOC容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class,args);//查看我們IOC容器中Bean的組件名稱(chēng)boolean tomcat = run.containsBean("tomcat");System.out.println("名稱(chēng)為tomcat的存在"+tomcat);boolean user01 = run.containsBean("user01");System.out.println("名稱(chēng)為user01的bean存在"+user01);}
}
//輸出結(jié)果
名稱(chēng)為tomcat的存在false
名稱(chēng)為user01的bean存在false
- 我們看到我們的tomcat這個(gè)bean不存在,所以連user01這個(gè)bean也沒(méi)有注入容器
@ImportResource導(dǎo)入Spring配置文件
比如,公司使用bean.xml文件生成配置bean,然而你為了省事,想繼續(xù)復(fù)用bean.xml,@ImportResource登場(chǎng)。
<?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 class="com.lsc.springboot.pojo.User" id="haha"><property name="id" value="2"></property><property name="name" value="sss"></property></bean><bean class="com.lsc.springboot.pojo.Cat" id="hehe"><property name="name" value="小貓"></property></bean>
</beans>
測(cè)試
public static void main(String[] args) {//1、返回我們IOC容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);boolean haha = run.containsBean("haha");boolean hehe = run.containsBean("hehe");System.out.println("haha:"+haha);//trueSystem.out.println("hehe:"+hehe);//true
}
@ConfigurationProperties配置綁定
如何使用Java讀取到properties文件中的內(nèi)容,并且把它封裝到JavaBean中,以供隨時(shí)使用
傳統(tǒng)方式
public class getProperties {public static void main(String[] args) throws FileNotFoundException, IOException {Properties pps = new Properties();pps.load(new FileInputStream("a.properties"));Enumeration enum1 = pps.propertyNames();//得到配置文件的名字while(enum1.hasMoreElements()) {String strKey = (String) enum1.nextElement();String strValue = pps.getProperty(strKey);System.out.println(strKey + "=" + strValue);//封裝到JavaBean。}}}
- 我們需要自己讀取文件的內(nèi)容,然后進(jìn)行判斷存入
Spring Boot一種配置配置綁定:
@ConfigurationProperties + @Component
假設(shè)有配置文件application.properties
mycar.brand=BYD
mycar.price=10000
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {private String brand;private Integer price;.......
}
- 因?yàn)橹挥斜籹pring管理,才能擁有SpringBoot強(qiáng)大的功能
@RestController
public class HelloWorldController {@AutowiredCar car;@RequestMapping("/car")public Car car(){return car;}
SpringBoot另一種配置配置綁定
@EnableConfigurationProperties + @ConfigurationProperties
- 開(kāi)啟Car配置綁定功能
- 把這個(gè)Car這個(gè)組件自動(dòng)注冊(cè)到容器中
@ConfigurationProperties(prefix = "mycar")
public class Car {
...
}
@EnableConfigurationProperties(Car.class)
public class MyConfig {
...
}
自動(dòng)配置
被@SpringBootApplication表示為主類(lèi)
@SpringBootApplication(scanBasePackages = "com.lsc")
public class MainApplication {public static void main(String[] args) {SpringApplication.run(MainApplication.class,args);}
@SpringBootApplication的源碼
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {...
}
重點(diǎn)分析@SpringBootConfiguration
,@EnableAutoConfiguration
,@ComponentScan
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;
}
@Configuration
代表當(dāng)前是一個(gè)配置類(lèi)。
@ComponentScan
指定掃描哪些Spring注解。
@EnableAutoConfiguration
這個(gè)注解需要重點(diǎn)分析
@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 {};
}
重點(diǎn)分析@AutoConfigurationPackage
,@Import(AutoConfigurationImportSelector.class)
。
自動(dòng)包規(guī)則原理
@AutoConfigurationPackage
標(biāo)簽名直譯為:自動(dòng)配置包,指定了默認(rèn)的包規(guī)則。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {String[] basePackages() default {};Class<?>[] basePackageClasses() default {};
}
- @Import({Registrar.class})利用Registrar給容器中導(dǎo)入一系列組件
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));}}
Registrar.class 作用:將主啟動(dòng)類(lèi)的所在包及包下面所有子包里面的所有組件掃描到Spring容器
初始加載自動(dòng)配置類(lèi)
@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector :自動(dòng)配置導(dǎo)入選擇器,那么它會(huì)導(dǎo)入哪些組件的選擇器呢?我們點(diǎn)擊去這個(gè)類(lèi)看源碼:
1、這個(gè)類(lèi)中有一個(gè)這樣的方法
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {AnnotationAttributes attributes = this.getAttributes(annotationMetadata);List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);configurations = this.removeDuplicates(configurations);Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);}}
2getAutoConfigurationEntry中調(diào)用了getCandidateConfigurations
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 correct.");return configurations;}
3 這個(gè)方法又調(diào)用了SpringFactoriesLoader 類(lèi)的靜態(tài)方法!我們進(jìn)入SpringFactoriesLoader類(lèi)loadFactoryNames() 方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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 factoryTypeName = ((String)entry.getKey()).trim();String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());int var10 = var9.length;for(int var11 = 0; var11 < var10; ++var11) {String factoryImplementationName = var9[var11];result.add(factoryTypeName, factoryImplementationName.trim());}}}cache.put(classLoader, result);return result;} catch (IOException var13) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);}}}
發(fā)現(xiàn)一個(gè)多次出現(xiàn)的文件:spring.factories,全局搜索它
spring.factories
文件里面寫(xiě)死了spring-boot一啟動(dòng)就要給容器中加載的所有配置類(lèi)
我們根據(jù)源頭打開(kāi)spring.factories , 看到了很多自動(dòng)配置的文件;這就是自動(dòng)配置根源所在!
這個(gè)spring.factories文件也是一組一組的key=value的形式,其中一個(gè)key是EnableAutoConfiguration類(lèi)的全類(lèi)名,而它的value是一個(gè)xxxxAutoConfiguration的類(lèi)名的列表,這些類(lèi)名以逗號(hào)分隔,如下圖所示:
@Import(AutoConfigurationImportSelector.class)
-
AutoConfigurationImportSelector類(lèi)中利用
getAutoConfigurationEntry(annotationMetadata);
給容器中批量導(dǎo)入一些組件 -
getAutoConfigurationEntry方法中通過(guò)調(diào)用
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
獲取到所有需要導(dǎo)入到容器中的配置類(lèi)-
SpringFactoriesLoader利用工廠加載
Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);
得到所有的組件-
從
META-INF/spring.factories
位置來(lái)加載一個(gè)文件。 -
默認(rèn)掃描我們當(dāng)前系統(tǒng)里面所有
META-INF/spring.factories
位置的文件
-
-
spring-boot-autoconfigure-2.3.4.RELEASE.jar
包里面也有META-INF/spring.factories
# 文件里面寫(xiě)死了spring-boot一啟動(dòng)就要給容器中加載的所有配置類(lèi)
# spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
...
雖然我們127個(gè)場(chǎng)景的所有自動(dòng)配置啟動(dòng)的時(shí)候默認(rèn)全部加載,但是xxxxAutoConfiguration
按照條件裝配規(guī)則(@Conditional
),最終會(huì)按需配置。
springboot所有的自動(dòng)配置都是在啟動(dòng)的時(shí)候掃描并加載,掃描了spring.properties配置文件,所有的自動(dòng)配置類(lèi)都在這里面,但是不定生效,因?yàn)橐袛鄺l件是否成立,只要導(dǎo)入了對(duì)應(yīng)的start,就有對(duì)應(yīng)的啟動(dòng)器,有了啟動(dòng)器我們自動(dòng)裝配就會(huì)生效,然后就配置成功
按需配置的例子
AopAutoConfiguration
類(lèi):
@Configuration(proxyBeanMethods = false
)
@ConditionalOnProperty(prefix = "spring.aop",name = "auto",havingValue = "true",matchIfMissing = true
)
public class AopAutoConfiguration {public AopAutoConfiguration() {}...
}
- prefix為配置文件中的前綴,
- name為配置的名字
- havingValue是與配置的值對(duì)比值,當(dāng)兩個(gè)值相同返回true,配置類(lèi)生效.
- matchIfMissing表示如果沒(méi)有這么配置的默認(rèn)值
自動(dòng)配置流程
以DispatcherServletAutoConfiguration
的內(nèi)部類(lèi)DispatcherServletConfiguration
為例子:
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有這個(gè)類(lèi)型組件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中沒(méi)有這個(gè)名字 multipartResolver 的組件
public MultipartResolver multipartResolver(MultipartResolver resolver) {// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;//給容器中加入了文件上傳解析器;
}
- 給@Bean標(biāo)注的方法傳入了對(duì)象參數(shù),這個(gè)參數(shù)的值就會(huì)從容器中找。
- 方法名為multipartResolver ,SpringMVC multipartResolver防止有些用戶配置的文件上傳解析器不符合規(guī)范
SpringBoot默認(rèn)會(huì)在底層配好所有的組件,但是如果用戶自己配置了以用戶的優(yōu)先。
總結(jié):
- SpringBoot先加載所有的自動(dòng)配置類(lèi) xxxxxAutoConfiguration
- 每個(gè)自動(dòng)配置類(lèi)按照條件進(jìn)行生效,默認(rèn)都會(huì)綁定配置文件指定的值。(xxxxProperties里面讀取,xxxProperties和配置文件進(jìn)行了綁定)
- 生效的配置類(lèi)就會(huì)給容器中裝配很多組件
- 只要容器中有這些組件,相當(dāng)于這些功能就有了
- 定制化配置
- 用戶直接自己@Bean替換底層的組件
- 用戶去看這個(gè)組件是獲取的配置文件什么值就去修改。
xxxxxAutoConfiguration —> 組件 —> xxxxProperties里面拿值 ----> application.properties
面試官問(wèn)你你可以這樣說(shuō),springboot是通過(guò)main方法下的SpringApplication.run方法啟動(dòng)的,啟動(dòng)的時(shí)候他會(huì)調(diào)用refshContext方法,先刷新容器,然后根據(jù)解析注解或者解析配置文件的形式注冊(cè)bean,還會(huì)它是通過(guò)啟動(dòng)類(lèi)的SpringBootApplication注解進(jìn)行開(kāi)始解析的,他會(huì)根據(jù)EnableAutoConfiguration開(kāi)啟自動(dòng)化配置,里面有個(gè)核心方法ImportSelect選擇性的導(dǎo)入,根據(jù)loadFactoryNames根據(jù)classpath路徑以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration開(kāi)頭的key去加載里面所有對(duì)應(yīng)的自動(dòng)化配置,他并不是把這一百二十多個(gè)自動(dòng)化配置全部導(dǎo)入,在他每個(gè)自動(dòng)化配置里面都有條件判斷注解,先判斷是否引入相互的jar包,再判斷容器是否有bean再進(jìn)行注入到bean容器