在上一章,我们对Springboot进行了入门,接下来我们就来看一下一些细节。
3.1 pom.xml研究
- 分析思路,只要是maven项目就先分析pom.xml
- pom.xml中分析三个要素:依赖,插件,parent
3.1.1 父依赖 parent
-
父依赖代码
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
-
父依赖作用分析-资源过滤,插件管理
<build> <!--资源配置--> <resources> <resource> <filtering>true</filtering> <directory>${basedir}/src/main/resources</directory> <!--此处包含了yml配置--> <includes> <include>**/application*.yml</include> <include>**/application*.yaml</include> <include>**/application*.properties</include> </includes> </resource> ...... </resources> <!--插件配置--> <pluginManagement> <plugins> ...... 打包插件配置 <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> <configuration> <mainClass>${start-class}</mainClass> </configuration> </plugin> ...... </plugins> </pluginManagement> </build>
-
继续看一下父依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.5.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent> <!--进行了很多依赖的版本配置--> <properties> <activemq.version>5.15.11</activemq.version> <antlr2.version>2.7.7</antlr2.version> <appengine-sdk.version>1.9.78</appengine-sdk.version> <artemis.version>2.10.1</artemis.version> <aspectj.version>1.9.5</aspectj.version> <assertj.version>3.13.2</assertj.version> <atomikos.version>4.0.6</atomikos.version> <awaitility.version>4.0.2</awaitility.version> <bitronix.version>2.1.4</bitronix.version> <build-helper-maven-plugin.version>3.0.0</build-helper-maven-plugin.version> <byte-buddy.version>1.10.8</byte-buddy.version> <caffeine.version>2.8.1</caffeine.version> .... </properties> <!--进行了很多依赖管理,意味着我们以后要用是直接引用,不用加版本了。比如: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> <version>2.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> <version>2.2.5.RELEASE</version> </dependency> ...... </dependencies> </dependencyManagement>
3.1.2 启动器
在Springboot中,需要集成某个模块只需要引入对应场景启动器模块就ok,比如web支持spring-boot-starter-web,并且在parent已经配置了版本,直接引入就ok
<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>
....
3.1.3 打包插件配置
由于在parent中对插件进行了配置,所以我们只需要以下方式引入就ok。
3.1.4 总结
总结:依赖spring-boot-starter-parent可以实现
-
引入这个之后相关的引入就不需要添加version配置,spring boot会自动选择最合适的版本进行添加。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
-
并且给我做了打包等插件的配置。以后只需声明插件就ok了,不需要设置版本。
<build> <!--插件--> <plugins> <!--打包插件:将项目打成jar包或者war包--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
所以非常方便!
3.2 启动类分析
3.2.1 javaconfig介绍
- 传统的XML形式 applicationContext.xml
<bean id="..." class="..." />
<import class="...">
<mvc:componentScan="">
- 注解的配置形式 applicationContext
@Configuragtion
@import
@componentScan()
class applicationContext{
//方法名就是xml中对应的id,返回值类型就是对应的class
@Bean
public User user(){
}
}
3.2.2 启动类@SpringBootApplication
@SpringBootApplication //一个注解
public class Springboot01HelloApplication {
//一个方法
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloApplication.class, args);
}
}
-
@SpringBootApplication:代表这个类是一个启动类
-
咱们进入这个注解,可以看到代码如下:
... @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(...) // 对应xml中的很多scan标签 public @interface SpringBootApplication { ... }
-
@ComponentScan:扫描包,将扫描当前包及其子包中加了@Controller,@Service,@Component等注解的bean放到spring中去
-
@SpringBootConfiguration:就是一个配置类
... @Configuration //这是一个配置类 public @interface SpringBootConfiguration { boolean proxyBeanMethods() default true; } @Component //这是一个组件 public @interface Configuration { ... }
-
@EnableAutoConfiguration:开启自动配置功能
在这个下面我们可以看到代码里有两个注解
@AutoConfigurationPackage //自动包配置 @Import(AutoConfigurationImportSelector.class) //导入一个配置注入选择器
①Springboot应用入口类通过@SpringBootApplication注解简介使用@EnableAutoConfiguration(是父注解)
②使用@EnableAutoConfiguration后,会扫描所有jar中的spring.factories文件,加载所有的配置类(加了 @Configuration的类)
③配置类(加了@Configuration的类)使用XxxProperties以@Bean的方式完成对应场景的配置
④XxxPropereis里面的配置信息有默认值,但是我们可以在application.properties/application.yml中获取
3.2.3 启动类中的run方法分析
-
Tomcat配置分析
项目中配置了 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 进去看 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.2.5.RELEASE</version> <scope>compile</scope> </dependency>
ServletWebServerFactoryConfiguration类中,TomcatServletWebServerFactory 会被作为bean被spring管理起来。根据注解@ConditionalOnClass。。条件,因为没有引入jetty相关的jar,spring中不会有JettyServletWebServerFactory这个bean。
@Configuration(proxyBeanMethods = false) class ServletWebServerFactoryConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedTomcat { @Bean TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.getTomcatConnectorCustomizers() .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatContextCustomizers() .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatProtocolHandlerCustomizers() .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } /** * Nested configuration if Jetty is being used. */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedJetty { @Bean JettyServletWebServerFactory JettyServletWebServerFactory( ObjectProvider<JettyServerCustomizer> serverCustomizers) { JettyServletWebServerFactory factory = new JettyServletWebServerFactory(); factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList())); return factory; } }
-
启动分析
首先从main函数开始
//main 函数
@SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
}
//点击run
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
//继续点击run
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
//run
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//略。。。。。
refreshContext(context);//点击进入。。。。。。。。。。。
afterRefresh(context, applicationArguments);
//略。。。。
return context;
}
点击refreshContext(context);方法
点击-->refresh(context);-->((AbstractApplicationContext) applicationContext).refresh();-->进入类AbstractApplicationContext的refresh方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
//略。。。。
onRefresh();//点击进入
//略
}
进入类ServletWebServerApplicationContext的onRefresh()方法
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();//点击进入。。。。。。
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
//点击createWebServer()方法
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
//点击进入
ServletWebServerFactory factory = getWebServerFactory();
//点击进入
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
getWebServerFactory() 会TomcatServletWebServerFactory。所以factory.getWebServer(getSelfInitializer())中的factory是指TomcatServletWebServerFactory,点击进入实现类TomcatServletWebServerFactory的getWebServer(getSelfInitializer())方法。
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
//TOmcat关联Spring容器
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);//点击进入。。。。。。。。
}
//点击进入getTomcatWebServer(tomcat)
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);//点击进入
}
//点击new TomcatWebServer
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();//点击进入
}
//点击进入
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
// 略。。。。。。。
this.tomcat.start();//。。。。启动tomcat
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
//略。。。。。。。。。。。。。。
}
}
3.2.3 小结
当运行run时,会创建Spring容器,并关联到内置tomcat,并完成Tomcat启动。
欢迎入群交流和获取源代码和视频资料。
Q群:1049572529 微信群:添加“liuhuaxinting1314”微信入群!发起请求请备注“csdn”!