demo
自己定義一個(gè)外部項(xiàng)目,core-bean,依賴如下,
<artifactId>core-bean</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
</dependencies>
然后定義一個(gè)Cat類,
public class Cat {
}
package core.bean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public Cat cat(){
return new Cat();
}
}
我們知道這樣就將Cat類裝配到Spring容器了。
再定義一個(gè)springboot項(xiàng)目,加入core-bean依賴,依賴如下:
<artifactId>springboot-enableAutoConfiguration</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.zhihao.miao</groupId>
<artifactId>core-bean</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
啟動(dòng)類啟動(dòng):
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context =SpringApplication.run(Application.class,args);
Cat cat = context.getBean(Cat.class);
System.out.println(cat);
}
}

發(fā)現(xiàn)Cat類并沒有納入到springboot-enableAutoConfiguration項(xiàng)目中。
解決方案,
在core-bean項(xiàng)目resource下新建文件夾META-INF,在文件夾下面新建spring.factories文件,文件中配置,key為自定配置類EnableAutoConfiguration的全路徑,value是配置類的全路徑
org.springframework.boot.autoconfigure.EnableAutoConfiguration=core.bean.MyConfig
啟動(dòng)springboot-enableAutoConfiguration項(xiàng)目,打印結(jié)果:

原理分析
進(jìn)入EnableAutoConfiguration注解源碼,發(fā)現(xiàn)是導(dǎo)入EnableAutoConfigurationImportSelector類,


跟到最后發(fā)現(xiàn)繼承了ImportSelector接口,之前我們講過Springboot @Enable*注解的工作原理ImportSelector接口的selectImports返回的數(shù)組(類的全類名)都會(huì)被納入到spring容器中。
其在AutoConfigurationImportSelector類中的selectImports實(shí)現(xiàn),進(jìn)入org.springframework.boot.autoconfigure.AutoConfigurationImportSelector類,

進(jìn)入getCandidateConfigurations方法

getCandidateConfigurations會(huì)到classpath下的讀取META-INF/spring.factories文件的配置,并返回一個(gè)字符串?dāng)?shù)組。
調(diào)試的時(shí)候讀取到了core.bean.MyConfig,也讀到了一些其他的配置,下面會(huì)講。

具體的就不細(xì)說了,有興趣的朋友可以自己調(diào)試一下。
META-INF/spring.factories還可以配置多個(gè)配置類。
比如我們?cè)赾ore-bean下在定義二個(gè)類,
package core.bean;
public class Dog {
}
package core.bean;
public class People {
}
package core.bean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Myconfig2 {
@Bean
public Dog dog(){
return new Dog();
}
}
修改META-INF/spring.factories下的配置,
org.springframework.boot.autoconfigure.EnableAutoConfiguration=core.bean.MyConfig,core.bean.Myconfig2,core.bean.People
修改springboot-enableAutoConfiguration項(xiàng)目的啟動(dòng)類:
package com.zhihao.miao;
import core.bean.Cat;
import core.bean.Dog;
import core.bean.People;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context =SpringApplication.run(Application.class,args);
Cat cat = context.getBean(Cat.class);
System.out.println(cat);
Dog dog = context.getBean(Dog.class);
System.out.println(dog);
People people = context.getBean(People.class);
System.out.println(people);
}
}
打印結(jié)果如下:

發(fā)現(xiàn)都納入到spring容器中了。
可以配置spring.boot.enableautoconfiguration=false禁用自動(dòng)配置,這樣不會(huì)啟動(dòng)自動(dòng)配置了,默認(rèn)是true。還可以排出一些自動(dòng)配置類,可以在EnableAutoConfiguration注解加入?yún)?shù),這邊不做過多解釋。

總結(jié),@EnableAutoConfiguration 作用
從classpath中搜索所有META-INF/spring.factories配置文件然后,將其中org.springframework.boot.autoconfigure.EnableAutoConfiguration key對(duì)應(yīng)的配置項(xiàng)加載到spring容器
只有spring.boot.enableautoconfiguration為true(默認(rèn)為true)的時(shí)候,才啟用自動(dòng)配置
@EnableAutoConfiguration還可以進(jìn)行排除,排除方式有2中,一是根據(jù)class來排除(exclude),二是根據(jù)class name(excludeName)來排除
其內(nèi)部實(shí)現(xiàn)的關(guān)鍵點(diǎn)有
1)ImportSelector 該接口的方法的返回值都會(huì)被納入到spring容器管理中
2)SpringFactoriesLoader 該類可以從classpath中搜索所有META-INF/spring.factories配置文件,并讀取配置
springboot內(nèi)部如何使用@EnableAutoConfiguration注解
我們點(diǎn)進(jìn)去spring-boot-autoconfigure中的META-INF下的spring.factories文件,發(fā)現(xiàn)spring.factories文件中配置了好多的配置類,在將這些依賴依賴到自己的項(xiàng)目中會(huì)將其都納入到spring容器中,不過這些類好多都是配合@Conditional***等注解一起工作的。

舉個(gè)例子:
在springboot-enableAutoConfiguration加入Gson依賴:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>

如果我們不在項(xiàng)目中配置,spring-boot-autoconfigure會(huì)自動(dòng)幫我們裝配一個(gè)對(duì)象實(shí)例名為gson的Gson實(shí)例。如果自己裝配那么就使用自己裝配的Gson實(shí)例。
啟動(dòng)測(cè)試類:
package com.zhihao.miao;
import com.google.gson.Gson;
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) {
ConfigurableApplicationContext context =SpringApplication.run(Application.class,args);
System.out.println(context.getBeansOfType(Gson.class));
}
}
此時(shí)自己沒有去配置Gson對(duì)象,

如果自己配置了,測(cè)試代碼如下,啟動(dòng):
package com.zhihao.miao;
import com.google.gson.Gson;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
@Bean
public Gson createGson(){
return new Gson();
}
public static void main(String[] args) {
ConfigurableApplicationContext context =SpringApplication.run(Application.class,args);
System.out.println(context.getBeansOfType(Gson.class));
}
}
