首先我们来看一看,springboot启动类
@RestController//@ResponseBody+@Controller
@SpringBootApplication
public class HelloWorldApplication {

public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}

@RequestMapping("/")
public String helloSpring() {
System.out.println("Hello");
return "Hello springBoot";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
没错引起我们关注的就是@SpringBootApplication这个注解,它是一个组合注解,接下来我们来看看@SpringBootApplication这个注解组成。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented //配置类
@Inherited //子类如果想使用父类注解信息,必须要使用此注解
@SpringBootConfiguration //springboot配置
@EnableAutoConfiguration //自动配置
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

/**
* Exclude specific auto-configuration classes such that they will never be applied.
* 排除指定的自动配置
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
Class<?>[] exclude() default {};

/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* 排除自动的配置类名
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
String[] excludeName() default {};

/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* 扫描指定的基本包
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};

/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
可以看到@SpringBootApplication注解主要由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan等注解组成,核心功能由@EnableAutoConfiguration注解提供。
来看看它的源码

@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};

/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
可以看到这里的关键是@Import注解导入的配置功能。还有排除自动配置使用的“exclude”方法是在这里定义的。

接着看看@Import注解

@Deprecated
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {

@Override
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
return getEnvironment().getProperty(
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
true);
}
return true;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
看到它继承了AutoConfigurationImportSelector这个类,还有是否是当前类的一个判断方法,是的话返回当前类的配置信息。
接着看继承类AutoConfigurationImportSelector

public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {

private static final String[] NO_IMPORTS = {};

private static final Log logger = LogFactory
.getLog(AutoConfigurationImportSelector.class);

private ConfigurableListableBeanFactory beanFactory;

private Environment environment;

private ClassLoader beanClassLoader;

private ResourceLoader resourceLoader;

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
//从配置文件中加载信息
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);

AnnotationAttributes attributes = getAttributes(annotationMetadata);
//得到所有的候选配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//去重
configurations = removeDuplicates(configurations);
//最初按字母排序,再按order排序
configurations = sort(configurations, autoConfigurationMetadata);
//返回不需要的配置项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
//剔除不需要的配置
configurations.removeAll(exclusions);
//最后在进行一步过滤
configurations = filter(configurations, autoConfigurationMetadata);
//导入自动配置的监听器不为空,设置并初始化beanClassLoader,beanFactory,environment,resourceLoader
fireAutoConfigurationImportEvents(configurations, exclusions);
//返回最终需要的配置类
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}

protected boolean isEnabled(AnnotationMetadata metadata) {
return true;
}

/**
* Return the appropriate {@link AnnotationAttributes} from the
* {@link AnnotationMetadata}. By default this method will return attributes for
* {@link #getAnnotationClass()}.
* @param metadata the annotation metadata
* @return annotation attributes
*/
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes,
"No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}

/**
* Return the source annotation class used by the selector.
* @return the annotation class
*/
protected Class<?> getAnnotationClass() {
return EnableAutoConfiguration.class;
}

/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//此方法用来扫描META-INF/spring.factories文件中的jar包
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), 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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
好至此认识了@SpringBootApplication
总结一下:

首先是@enableautoconfiguration,@SpringBootApplication被@enableautoconfiguration注解.
@enableautoconfiguration又被@import注解
@import导入了EnableAutoConfigurationImportSelector(导入选择器),真正起作用的是AutoConfigurationImportSelector,getCandidateConfigurations方法用来扫描META-INF、spring.factories下的jar包,selectImports方法返回需要的配置类。
核心注解
先来看看spring.factories文件

打开一个AutoConfiguration文件,都会有以下条件注解

来看一个具体的条件注解,@ConditionOnWebAppLication

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {

}
1
2
3
4
5
6
7
来看看它的具体实现类

class OnWebApplicationCondition extends SpringBootCondition {

private static final String WEB_CONTEXT_CLASS = "org.springframework.web.context."
+ "support.GenericWebApplicationContext";

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
//判断是否是元注解类
boolean required = metadata
.isAnnotated(ConditionalOnWebApplication.class.getName());
//进行条件匹配结果
ConditionOutcome outcome = isWebApplication(context, metadata, required);
if (required && !outcome.isMatch()) {
return ConditionOutcome.noMatch(outcome.getConditionMessage());
}
if (!required && outcome.isMatch()) {
return ConditionOutcome.noMatch(outcome.getConditionMessage());
}
//返回匹配成功的条件信息
return ConditionOutcome.match(outcome.getConditionMessage());
}

private ConditionOutcome isWebApplication(ConditionContext context,
AnnotatedTypeMetadata metadata, boolean required) {
ConditionMessage.Builder message = ConditionMessage.forCondition(
ConditionalOnWebApplication.class, required ? "(required)" : "");
//是否有webApplicationClasse实例
if (!ClassUtils.isPresent(WEB_CONTEXT_CLASS, context.getClassLoader())) {
return ConditionOutcome
.noMatch(message.didNotFind("web application classes").atAll());
}
//容器是否有名为scopes的session
if (context.getBeanFactory() != null) {
String[] scopes = context.getBeanFactory().getRegisteredScopeNames();
if (ObjectUtils.containsElement(scopes, "session")) {
return ConditionOutcome.match(message.foundExactly("'session' scope"));
}
}
//验证当前容器环境
if (context.getEnvironment() instanceof StandardServletEnvironment) {
return ConditionOutcome
.match(message.foundExactly("StandardServletEnvironment"));
}
//当前ResourceLoader为WebApplicationContext
if (context.getResourceLoader() instanceof WebApplicationContext) {
return ConditionOutcome.match(message.foundExactly("WebApplicationContext"));
}
return ConditionOutcome.noMatch(message.because("not a web application"));
}

}

---------------------

springboot学习笔记(二)的更多相关文章

  1. SpringBoot学习笔记<二>注解

    此篇为项目作结之笔记,关于注解. 项目启动入口@SpringBootApplication[必选]  @ServletComponentScan[可选] 注解后: Servlet.Filter.Lis ...

  2. SpringBoot学习笔记二之Spring整合Mybatis

    原文链接: https://www.toutiao.com/i6803235766274097678/ 在learn-admin-component子工程中加入搭建环境所需要的具体依赖(因为比较长配置 ...

  3. springboot 学习笔记(二)

    springboot 学习笔记(二) 快速创建一个springboot工程,并引入所需要的依赖 1.利用Spring initializr 来创建一个springboot项目,登陆http://sta ...

  4. Springboot学习笔记(六)-配置化注入

    前言 前面写过一个Springboot学习笔记(一)-线程池的简化及使用,发现有个缺陷,打个比方,我这个线程池写在一个公用服务中,各项参数都定死了,现在有两个服务要调用它,一个服务的线程数通常很多,而 ...

  5. SpringBoot学习笔记(3):静态资源处理

    SpringBoot学习笔记(3):静态资源处理 在web开发中,静态资源的访问是必不可少的,如:Html.图片.js.css 等资源的访问. Spring Boot 对静态资源访问提供了很好的支持, ...

  6. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  7. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  8. [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  9. JMX学习笔记(二)-Notification

    Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...

  10. java之jvm学习笔记二(类装载器的体系结构)

    java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...

随机推荐

  1. GitHub Pages使用入门

    1.什么是GitHub Pages GitHub Pages可以当做项目的Websites,GitHub Pages有两种最基本的用法: 作为你自己(或者组织)的网站(访问地址示例:http://us ...

  2. 【转】wait和waitpid详解

    发现进程有关的编程题里面的包含知识量实在是太庞大,这是关于wait和waitpid区别的,以前只是粗略知道它们的区别,这是网上看到的比较全的对比 转自http://blog.chinaunix.net ...

  3. html5--7-33 阶段练习5

    html5--7-33 阶段练习5 总结: 1.JS中可以递归函数 2.js中数组对象array的使用 学习要点 综合运用学过的知识完成三个综合小练习,巩固学过的知识. 阶段小练习5-1:使用递归算法 ...

  4. [Django基础] django解决静态文件依赖问题以及前端引入方式

    一.静态文件依赖 学习django的时候发现静态文件(css,js等)不能只在html中引入,还要在项目的settings中设置,否则会报以下错误 [11/Sep/2018 03:18:15] &qu ...

  5. RDA GUI

    创建服务:“sysapp_table.h” / "dvb_guiobj_table.h" / "atv_guiobj_table.h" “服务”与“回调”   ...

  6. Swift4 内存管理, 可选链, KeyPath

    创建: 2018/03/09 完成: 2018/03/09 参照型数据与ARC  ARC ● Swift里, 只有类实例与闭包实例是参照型 ● 生成时参照值为1, 被代入等每次+1, 减少每次-1 ● ...

  7. 洛谷P3642 [APIO2016]烟火表演

    传送门 题解 fy大佬好强……我根本看不懂…… //minamoto #include<bits/stdc++.h> #define ll long long using namespac ...

  8. 初学Linux应该注意的事项

    相比于windows linux严格区分大小写 linux所有内容都是以文件形式保存 linux不靠扩展名区分文件类型(靠权限),linux下文件扩展名主要是方便管理员分类 linux所有的存储设备都 ...

  9. php安装的扩展php -m可以看到,但是phpinfo()看不到,php-fpm关闭了重新打开还是不行?

    问答 问答详情   php安装的扩展php -m可以看到,但是phpinfo()看不到,php-fpm关闭了重新打开还是不行? centos apache linux html php 3.2k 次浏 ...

  10. 使用方向变换(directional transform)图像分块压缩感知

    论文的思路是先介绍分块压缩感知BCS,然后介绍使用投影和硬阈值方法的迭代投影方法PL,接着将PL与维纳滤波器结合形成SPL(平滑PL),并且介绍了稀疏表示的几种基,提出了两种效果较好的稀疏基:CT与D ...