starter-* POM依赖
使用SpringBoot开发时,在pom.xml文件中引入的依赖一般都是形如spring-boot-starter-*
。starter依赖是居于某个场景或者功能的,我们引入一个starter依赖之后,它会间接引入实现这个场景或功能所需的其他依赖。我们可以把这些starters称为场景启动器,只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器。这里以spring-boot-starter-web
为例分析。
org.springframework.boot
spring-boot-starter-web
我们来看下
spring-boot-starter-web
的pom文件,它定义了一个父类spring-boot-starters
org.springframework.boot
spring-boot-starters
1.5.4.RELEASE
spring-boot-starter-web
Spring Boot Web Starter
http://projects.spring.io/spring-boot/
Pivotal Software, Inc.
http://www.spring.io
${basedir}/../..
spring-boot-starters
的打包类型为pom,它定义好了SpringBoot中所有的starter,同时它的父类为spring-boot-parent
org.springframework.boot
spring-boot-parent
1.5.4.RELEASE
../spring-boot-parent
spring-boot-starters
pom
Spring Boot Starters
Spring Boot Starters
http://projects.spring.io/spring-boot/
Pivotal Software, Inc.
http://www.spring.io
${basedir}/..
spring-boot-starter
spring-boot-starter-activemq
spring-boot-starter-amqp
spring-boot-starter-aop
spring-boot-starter-artemis
spring-boot-starter-batch
spring-boot-starter-cache
spring-boot-starter-cloud-connectors
spring-boot-starter-data-cassandra
spring-boot-starter-data-couchbase
spring-boot-starter-data-elasticsearch
spring-boot-starter-data-gemfire
spring-boot-starter-data-jpa
spring-boot-starter-data-ldap
spring-boot-starter-data-mongodb
spring-boot-starter-data-neo4j
spring-boot-starter-data-redis
spring-boot-starter-data-rest
spring-boot-starter-data-solr
spring-boot-starter-freemarker
spring-boot-starter-groovy-templates
spring-boot-starter-hateoas
spring-boot-starter-integration
spring-boot-starter-jdbc
spring-boot-starter-jersey
spring-boot-starter-jetty
spring-boot-starter-jooq
spring-boot-starter-jta-atomikos
spring-boot-starter-jta-bitronix
spring-boot-starter-jta-narayana
spring-boot-starter-logging
spring-boot-starter-log4j2
spring-boot-starter-mail
spring-boot-starter-mobile
spring-boot-starter-mustache
spring-boot-starter-actuator
spring-boot-starter-parent
spring-boot-starter-security
spring-boot-starter-social-facebook
spring-boot-starter-social-twitter
spring-boot-starter-social-linkedin
spring-boot-starter-remote-shell
spring-boot-starter-test
spring-boot-starter-thymeleaf
spring-boot-starter-tomcat
spring-boot-starter-undertow
spring-boot-starter-validation
spring-boot-starter-web
spring-boot-starter-websocket
spring-boot-starter-web-services
spring-boot-parent
的父pom为spring-boot-dependencies
,该pom文件中定义了我们所需要具体jar包的version。
5.14.5
2.7.7
1.9.53
1.5.5
1.8.10
2.6.0
3.9.3
2.1.4
2.3.5
3.1.4
1.3.3
1.9.3
3.2.2
1.10
1.4
2.1.1
2.1
1.6
2.4.2
2.3.7
2.1.0
1.3.2
10.13.1.1
1.6.1
3.1.4
2.10.4
3.2.2
1.50.5
3.2.1
2.3.26-incubating
2.4.5
8.2.4
3.0.0
2.9
2.4.11
2.8.0
1.4.195
1.3
3.7.7
3.7.1
1.1.3
5.0.12.Final
5.3.5.Final
2.5.1
2.3.13
2.4.11
2.3.5
2.21
4.1.3
4.5.3
4.4.6
8.2.6.Final
2.8.8
2.7.8
3.21.0-GA
1.0.0
1.5.6
1.2
1.1.0.Final
1.1.6
2.2.13
3.3.1.Final
7.6.0.Final
2.0.6
2.9.0
2.25.1
2.0.4
9.4.5.v20170502
2.2.0.v201112011158
8.0.33
1.1-rev-1
1.13
4.2.2
2.9.9
1.3.6
3.9.2
20140107
1.4.0
2.2.0
1.2
1.3.1
4.12
3.5.3
2.7
1.1.11
1.16.16
1.5.9
6.1.0.jre7
1.10.19
3.4.2
5.1.42
5.5.24.Final
1.9.22
2.1.3
9.4.1212.jre7
4.1.4
2.0.8.RELEASE
2.0.7.RELEASE
2.53.1
2.21
2.2.2
3.1.0
1.1.1
1.7.25
1.17
5.5.4
1.0-groovy-2.4
4.3.9.RELEASE
1.7.3.RELEASE
1.2.4.RELEASE
3.0.7.RELEASE
Ingalls-SR4
0.23.0.RELEASE
4.3.10.RELEASE
1.2.2.RELEASE
1.1.6.RELEASE
2.3.1.RELEASE
1.2.7.RELEASE
1.1.5.RELEASE
1.2.0.RELEASE
1.1.3.RELEASE
1.2.0.RELEASE
4.2.3.RELEASE
1.0.8.RELEASE
2.0.14.RELEASE
1.3.1.RELEASE
1.1.4.RELEASE
2.0.3.RELEASE
1.0.2.RELEASE
1.1.2.RELEASE
2.4.0.RELEASE
3.15.1
3.1.0
${javax-mail.version}
2.1.5.RELEASE
2.1.3.RELEASE
2.1.2.RELEASE
1.4.0
1.3
2.1.0.RELEASE
8.5.15
1.4.15.Final
3.2.1
9f96c74
0.32-1
1.6.3
1.4.01
1.10
1.5.0
2.2.2
1.8
2.6
2.6.1
3.1
2.10
2.8.2
2.10
1.4
2.18.1
2.5.2
1.10
2.2
2.6
2.10.4
2.7
2.4.3
3.5.1
2.4
2.18.1
2.6
2.2
这就解释了springboot为我们定义好了依赖包的版本,在开发过程中jar包冲突是个常见且让人头痛的问题,而使用springboot的话由于其帮我们选择好了依赖包的版本,所以很好的解决了包冲突等繁琐问题。如果spring-boot-parent
没有定义的jar包,而此时项目中需要使用的话,需要我们自己定义好版本。
程序入口
@SpringBootApplication
public class StartSpringBootMain {
public static void main(String[] args) throws Exception {
SpringApplication.run(StartSpringBootMain.class, args);
}
}
我们在主程序类中加入@SpringBootApplication
注解就能启用SpringBoot,下面就来看看这个注解类
@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 {
......
}
其中@ComponentScan是我们熟悉的组件扫描,其作用是使@Controller、@Service、@Componet等组件生效。这里它排除了两个Filter类TypeExcludeFilter
和AutoConfigurationExcludeFilter
。
@SpringBootConfiguration
注解相当于@Configuration
,表示应用该注解的类为配置类,相当于applicationContext.xml文件。
@Configuration
public @interface SpringBootConfiguration {
}
@EnableAutoConfiguration
注解表示开启自动配置的功能,之前需要开发人员手动进行的配置,SpringBoot帮我们自动配置。它的源码如下
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
......
}
@AutoConfigurationPackage
自动配置包
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
AutoConfigurationPackages.Registrar
的源码如下
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
new PackageImport(metadata).getPackageName()
会获取主配置类(@SpringBootApplication标注的类)所在的包名,然后会将该包及下面所有子包里面的所有组件扫描到Spring容器中。
@Import
注解的含义为给Spring容器中注入一个对象,@Import(EnableAutoConfigurationImportSelector.class)
表示给Spring容器中注入EnableAutoConfigurationImportSelector
对象,该对象的字面意思为“开启自动配置导入选择器”。同时它还继承自AutoConfigurationImportSelector
类
@Deprecated
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {
AutoConfigurationImportSelector
类的作用是将所有需要导入的组件以全类名的方式添加到容器中;其通过selectImports方法筛选出选哟导入的组件的全类名。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//这句获取自动配置类
List configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
getCandidateConfigurations
方法用来获取自动配置类,该方法内部的实现最终会委托给SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
方法,loadFactoryNames方法的源码如下:
public static List loadFactoryNames(Class> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration 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);
}
}
可以看到其在Spring Boot启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;
我们调式源码的时候可以看到加载的自动配置类96个之多
而这些类是定义在spring.factories文件中有定义的。
今天先分析到这里,还存在没有理清楚的地方,后续有时间的话再更新吧。