download:Go读书社区web开发与高性能架构优化

@SpringBootApplication注解分析
以下是注解@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 {
可能发现它是由泛滥注解组合而成的,上面具体分析下这里每个注解所起到的作用。

@Target Target通过ElementType来指定注解可使用范畴的枚举会合(FIELD/METHOD/PARAMETER...)
@Retention Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值:

RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其余使
@Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包含注解的. 但如果申明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包含在生成的文档中
@Inherited 容许子类继承父类的注解,仅限于类注解有用,对于方法和属性有效。
@SpringBootConfiguration 注解实际上和@Configuration有雷同的作用,配备了该注解的类就能够以JavaConfig的形式实现一些配置,可能不再使用XML配置。
@ComponentScan 这个注解实现的是主动扫描的功能,相当于Spring XML配置文件中的:<context:component-scan>,可使用basePackages属性指定要扫描的包,及扫描的条件。如果不设置则默认扫描@ComponentScan注解所在类的同级类和同级目录下的所有类,所以咱们的Spring Boot我的项目,一般会把入口类放在顶层目录中,这样就能够保障源码目录下的所有类都能够被扫描到。
@EnableAutoConfiguration 这个注解是让Spring Boot的配置能够如此简化的关键性注解。我把EnableAutoConfiguration的实现端上来了,大家来鉴赏一下!

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@AutoConfigurationPackage 注解用于保存主动配置类以供之后的使用,比如给JPA entity扫描器,用来扫描开发人员通过注解@Entity定义的entity类。通俗的讲就是,注册bean定义到容器中。
@Import(AutoConfigurationImportSelector.class)是EnableAutoConfiguration注解中最要害的来,它借助AutoConfigurationImportSelector,可能帮助SpringBoot利用将所有符合条件的@Configuration配置都加载到以后SpringBoot创建并使用的IoC容器中。对于@Import注解要说的内容还比较多,改天再聊。
对于注解的话题就先谈到这里,上面开启撸代码环节。

3 剖析代码
查看SpringApplication的源代码可能发现SpringApplication的启动由两部分组成:

new SpringApplication(primarySources):创建SpringApplication对象
run(args):调用run方法
3.1 实例化SpringApplication对象
源码如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {    this.resourceLoader = resourceLoader;//1、初始化资源加载器    Assert.notNull(primarySources, "PrimarySources must not be null");//2、断言资源加载类不能为 null    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//3、初始化加载资源类会合并去重    this.webApplicationType = deduceWebApplicationType();//4、 推断利用类型是Standard还是Web    setInitializers((Collection) getSpringFactoriesInstances(            ApplicationContextInitializer.class));//5、设置利用上下文初始化器    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//6、设置监听器     this.mainApplicationClass = deduceMainApplicationClass();//7、推断利用入口类}

上面将针对源码中的重要实现进行粗疏的分析。

3.1.1 初始化资源加载器
ResourceLoader接口,在 Spring 中用于加载资源,通过它可能获取一个Resouce 对象。使用spring的敌人都知道它加载资源的形式由多种,上面就挑两个罕用的继承ResourceLoader的接口与实现类说起。

DefaultResourceLoader : 作为 ResourceLoader 接口的间接实现类,该类实现了基本的资源加载功能,可能实现对单个资源的加载。
ResourcePatternResolver :该接口继承了 ResourceLoader,定义了加载多个资源的方法, 可能实现对多个资源的加载。
1、DefaultResourceLoader
下面介绍过该类通过实现 ResourceLoader 接口实现了加载单个资源的功能。它的子类通过继承它来实现具体的资源拜访策略。上面来探究下该类如何加载单个资源:

public Resource getResource(String location) {//这里是三种识别location加载出Resource的形式。    Assert.notNull(location, "Location must not be null");    //1.先看有没有自定义的ProtocolResolver,如果有则先根据自定义的ProtocolResolver解析location失去Resource    for (ProtocolResolver protocolResolver : this.protocolResolvers) {        Resource resource = protocolResolver.resolve(location, this);        if (resource != null) {            return resource;        }    }    //2.根据路径是否匹配"/"或"classpath:"来解析失去ClassPathResource    if (location.startsWith("/")) {        return getResourceByPath(location);    }else if (location.startsWith(CLASSPATH_URL_PREFIX)) {//classpath        return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());    }else {        try {            //默认传入的location是一个URL路径,加载失去一个UrlResource            URL url = new URL(location);            return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));        }        catch (MalformedURLException ex) {            // 如果以上三种情况都不满足,则按照“/”来处理            return getResourceByPath(location);        }    }}