文章目录
- 1、约定大于配置
- 2、自动装配原理
- 2.1、`@SpringBootApplication`
- 2.2、`@EnableAutoConfiguration`
- 2.3、`@Import`
- 2.4、`AutoConfigurationImportSelector.class`
- 2.5、`selectImports()方法`
- 3、简单Demo
- 3.1、装配目标
- 3.2、配置属性类
- 3.3、自动装配类
- 3.4、spring.factories文件
- 3.5、使用组件
Spring Boot 是由 Pivotal 团队提供的全新框架,其采用
约定大于配置的方式来大量的减少了普通Spring MVC应用的初始搭建、配置过程。
1、约定大于配置
约定大于配置是一种软件设计范式,简单来说就是简化配置,即用户只需要在Maven-pom文件中添加相关依赖包,在application.yml中配置必要部分(如数据库链接、redis链接、rabbitmq链接等)就行了,可以直接启动项目而不用写一行xml配置。
通过Maven快速创建Spring Boot项目的过程简介如下:
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注解的作用详见:
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依赖的作用具体见:
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演示,不足之处欢迎斧正。