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); } }}