一、SpringBoot Helloworld
1、功能说明
发送一个请求,返回一个结果,最基本的web工程
2、搭建springboot工程
2.1、新建一个springboot-study工程
2.2、添加一个springboot-study-HelleWorld模块
2.2、在父pom里面添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
</dependencies>
2.3、完善父、子pom文件
在父pom里面添加插件,子pom暂时不需要添加任何东西。
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
2.4、编写主启动类
@SpringBootApplication
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class,args);
}
}
2.5、添加配置文件application.properties
server.port=8001
2.6、写一个controller进行测试
@RestController
public class HelloWorldController {
@RequestMapping(value = "/helloworld/test",method = RequestMethod.GET)
public String test(@RequestParam("id") String id) {
System.out.println("id:"+id);
return "helloworld"+id;
}
}
2.7、测试
启动:com.java.code.study.HelloWorldApplication
浏览器输入:http://localhost:8001/helloworld/test?id=springboot
二、利用springboot-study-Helloworld进行原理
1、主程序类
@SpringBootApplication
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class,args);
}
}
2、@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 {
...
}
3、先分析@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
通过注解可以知道@SpringBootConfiguration和@Configuration作用一样,表示是一个配置类,也就是我们的主启动类是一个配置类会被注入到spring容器里面。
4、再分析@EnableAutoConfiguration注解
回到@SpringBootApplication注解里面还有一个@EnableAutoConfiguration注解,看一下具体的定义:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
5、分析@EnableAutoConfiguration里面的@AutoConfigurationPackage注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
重点看一下org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar#registerBeanDefinitions方法,这个方法是Spring里面的方法,接着会调用里面的register方法,register方法把第二个参数代表的包里面的bean注入到spring容器里面,可以看一下把哪些包注入进去呐,可以采用debug方式启动一下看一下第二个参数的值。我这边就是:com.java.code.study。说明@AutoConfigurationPackage注解会把启动类所在包以及子包里面的bean进行扫描注册,注入的bean需要加注入的注解。
6、分析@EnableAutoConfiguration里面的@Import({EnableAutoConfigurationImportSelector.class})
这个注解注意注入EnableAutoConfigurationImportSelector类所以看一下这个类里面的内容,在它的父类里面有一个方法:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
configurations = this.sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return (String[])configurations.toArray(new String[configurations.size()]);
} catch (IOException var6) {
throw new IllegalStateException(var6);
}
}
}
这个方法会返回大量的自动配置类然后进行注入,重点是这些自动配置类是怎么获取的呐,主要看下面方法实现:
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;
}
接着再看里面loadFactoryNames方法的实现:
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
通过上面的代码可以知道会到classpath目录下加载META-INF/spring.factories文件,把这些文件里面的内容加载成properties对象。获取properties对象里面key为factoryClass全类名对应的值进行返回。回到上一个方法看一下factoryClass值是什么:
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
//org.springframework.boot.autoconfigure.EnableAutoConfiguration
所以下面我们就到classpath下面的META-INF/spring.factories文件文件里面找key为:org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的值:
例如:
org/springframework/boot/spring-boot-autoconfigure/1.5.10.RELEASE/spring-boot-autoconfigure-1.5.10.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,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
大家可以到spring.factories里面查看一下,有非常多类,那么这些类都会加载进去吗?哪些加载?怎么加载?和我们开发自己配置的值怎么进行交互等问题会在后续讲解。
代码源码:https://github.com/spingcode/springboot-study/tree/master