注:本文通篇将SpringBoot以sb代替。
文章目录
- Spring和SpringBoot的关系和区别?
- 谈谈你对SpringBoot的理解,它有哪些特性?
- SpringBoot的核心注解
- 说说你对SpringBoot自动配置的理解
- 为什么SpringBoot的jar包可以直接运行?
- SpringBoot的启动原理(还在改进)
- 会不会自定义Starter?大概实现过程?
- SpringBoot读取配置文件的原理是什么?加载顺序是怎么样的?
- SpringBoot的默认日志实现框架是什么?怎么切换成别的?
- 说说你在开发的时候怎么在SpringBoot上进行扩展?
- SpringBoot是如何选择使用Jetty还是Tomcat的?
Spring和SpringBoot的关系和区别?
他们都是Spring生态的产品。
Spring Framework是一个容器框架。
SpringBoot他不是一个框架,他是一个可以快速构建基于Spring的脚手架,里面包含了Spring和各自框架,为开发Spring生态其他框架铺平道路。他是为了更快的去开发Spring生态的整个系统而诞生的。
2者不是一个层面的东西,没有可比性。
谈谈你对SpringBoot的理解,它有哪些特性?
从本质上来说,Spring Boot就是Spring,它做了那些没有它你自己也会去做的Spring Bean配置。Spring Boot使用“约定优于配置”的理念让你的项目快速地运行起来,使用Spring Boot很容易创建一个能独立运行、准生产级别、基于Spring框架的项目,使用Spring Boot你可以不用或者只需要很少的Spring配置。
简而言之,Spring Boot本身并不提供Spring的核心功能,而是作为Spring的脚手架框架,以达到快速构建项目、预置三方配置、开箱即用的目的。Spring Boot有如下的优点:
- 可以快速构建项目,内置许多starter自动配置,开箱即用;
- 可以对主流开发框架的无配置集成;
- 项目可独立运行,无需外部依赖Servlet容器;
- 提供运行时的应用监控;
- 内置web容器,无需依赖外部web服务器,省略了web.xml,直接运行jar文件就可以启动web项目
- 可以极大地提高开发、部署效率;
- 管理了第三方依赖的版本,减少版本冲突问题
- 简化开发,采用JavaConfig的方式可以使用零xml的方式开发项目
- 可以与云计算天然集成。
SpringBoot的核心注解
- @SpringBootApplication注解:这个注解标识了一个SpringBoot工程,它实际上是另外三个注解的组合,这三个注解是@SpringBootConfiguration,@EnableAutoConfguration,@ComponentScan。
- @SpringBootConfiguration:这个注解实际就是一个@Configuration,表示启动类也是一个配置类。
- @EnableAutoConfguration:向Spring容器中导入了一个Selector,用来加载ClassPath下spring.factories中所定义的自动配置类,将这些自动加载为配置Bean。
- @Conditional也很关键,如果没有它我们无法在自定义应用中进行定制开发
@ConditionalOnBean
@ConditionalOnClass
@ConditionalOnExpression
说说你对SpringBoot自动配置的理解
自动装配简单来说就是自动吧第三方的bean加载到IOC容器中去。不需要开发人员在去写bean相关的配置。在sb项目中只要在启动类上加上@SpringBootApplication注解就可以实现自动装配。这个注解是一个复合注解,真正去实现自动装配的注解是@EnableAutoConfiguration。自动装配的实现主要依靠三个核心的技术。
第一个是引入starter。启动依赖组件的时候,这个组件里面必须包含@Configuration配置类,而在这个配置类中我们需要通过@Bean这个注解去声明要加载到IOC容器中的对象。
第二个是这个配置类是放在第三方的jar包中的。然后通过sb中的约定优于配置这样的一个理念,去吧这个配置类的全路径放在classpath:/META-INF/spring.factories文件里面。这样sb就可以知道第三方jar包里面这个配置类的位置。这个步骤主要使用到了spring中的SpringFactoriesLoader来完成的。
第三个是sb拿到所有第三方jar包里面的声明的配置类之后,在通过Spring提供的ImportSelector这样的一个接口,来实现对这些配置类的动态加载。从而去完成自动装配这样一个动作。
在我看来呢,sb是约定优于配置这一理念的产物,所以在很多的地方都会看到这一思想。它的出现让开发人员可以更加聚焦于业务代码的开发,而不需要去关心和业务无关的配置。其实自动装配的思想,在SpringFrameword3.x中的@Enable注解已经有了体现。这是一个模块
驱动注解,也就是我们只要添加这个注解,就能自动开启某个功能。而不需要针对这个功能去做bean的配置。enbale的底层也是自动帮我们去完成一个模块相关bean的注入的。
为什么SpringBoot的jar包可以直接运行?
- SpringBoot提供了一个插件spring-boot-maven-plugin用于把程序打包成一个可执行的jar包。
- Spring Boot应用打包之后,生成一个Fat jar(jar包中包含jar,比较臃肿,所以叫Fat.jar),包含了应用依赖的jar包和Sprirg Boot loader相关的类。
- java -jar会去找jar中的manifest文件,在那里面找到真正的启动类。
这是因为Java已经定义了java -jar这个命令去加载jar包中的manifest文件,并且以这个文件中的Main-Class后面的路径来找到启动类。 - Fat jar的启动Main函数是JarLauncher,它负责创建一个LaunchedURLClassLoader来加载boot-lib下面的jar,并以一个新线程启动应用的Main函数。
SpringBoot的启动原理(还在改进)
会不会自定义Starter?大概实现过程?
我们需要在我们自定义的starter中的resources下创建文件夹META-INF并在METE-INF下创建文件spring.factories。
然后在里面设定你要自动配置的配置类的全路径。格式为
之后我们编写我们的配置类,并且在配置类中通过@Bean注解把要注入IOC容器中的bean进行声明。
SpringBoot读取配置文件的原理是什么?加载顺序是怎么样的?
通过事件监听的方式读取配置文件:ConfigFileApplicationListener
优先级从高到低,高优先级的配置覆盖低优先级的配置,所有配置会形成互补配置。
SpringBoot的默认日志实现框架是什么?怎么切换成别的?
默认的日志框架是logback。
SpringBoot项目的日志门面使用的是slf4j,而SpringBoot默认使用的是logback提供的桥接器,因此SpringBoot默认会使用logback进行日志功能。
想要切换日志,那么需要做如下操作:
1:将logback的场景启动器排除(slf4j只能运行有一个桥接器)
2:添加新的日志的场景启动器
3:添加新的日志的配置文件
目前springboot已经为我们提供了logfj2和logback的starter,因此我们可以直接开箱即用。
但是如果要使用logfj,那么就需要首先排除logbakc的依赖,然后添加log4j的桥接器,然后再添加log4j的配置文件。
说说你在开发的时候怎么在SpringBoot上进行扩展?
如果想要对某个功能进行扩展,那么首先得了解这个功能是如何进行配置的,因此我可能会先去查看这个功能的官方文档,之后大致了解之后,会查看底层源码。
这里我以AOP为例吧。
我们知道AOP默认使用的是cglib来进行动态代理,那么如果我想要它使用JDK提供的动态代理怎么办呢?
那么此时就得去查看AOP对应的配置类了。
我知道SpringBoot项目中的配置类一般名称为XxxAutoConfiguration。
因此我就会搜索AopAutoConfiguration。然后查找启动的可能的配置属性。
一般查看@Condition注解里面可能会有某个属性的开关。
那么如果找到了,那么只需要去配置文件中进行属性的修改配置即可。
当然,有些自动配置类提供对外的扩展接口,实现接口也可以进行扩展,比如我们的SpringMvc。
如果我们要对SpringMvc进行扩展,那么我们只要实现WebMvcConfigurer这个接口即可。
所以我一般都会选择看源码这种方式。IDEA非常方便,搜索对应的XxxAutoConfiguration即可。
SpringBoot是如何选择使用Jetty还是Tomcat的?
我们知道,我们是可以获取到SpringBoot要进行加载的Bean的类型的,那么如果我们获取到了所有要加载的Bean,那么此时就可以通过这些Bean对象来选择是使用Jetty还是Tomcat。
比如我定义了Tomcat,那么我就选择加载Tomcat,反之我可以加载Jetty。如果都定义或者都没有定义,那么会进行报错。其中没有名字报错是因为你明明把这个项目声明为了一个Web项目,结果你没有对应的服务器,或者就是你只能选择一个服务器,结果你给我定义了多个服务器,那么我选那个?所以会报错。那么如果流程正常,我就会创建这个Bean,也就是Tomcat或者Jetty服务器。
但是其实SpringBoot默认把Jetty,Tomcat,Undertow三个Web服务器都声明为了Bean,那么很明显会出现冲突。所以此时就通过@Condition进行条件判断来选择加载那个Bean。
所以其大致流程为:
首先通过getWebServerFactory方法来获取一个ServletWebServerFactory对象,其中Jetty和Tomcat都实现了这个接口。
那么如果返回的是Tomcat的ServerFatory,那么之后的getWebServer方法返回的就是Tomcat这个web服务,反之就是Jetty。在这个方法中,Tomcat和Jetty都会在其中创建自己,然后作为一个对象返回。
那么我们知道SpringBoot默认使用的是Tomcat,那么要做到这样,只要保证我们的依赖中只有Tomcat而没有Jetty和Undertow即可。
所以此时我们引入的web-starter中,其中默认就是帮助我们引入了Tomcat这个依赖,所以,默认使用的就是Tomcat。而我们只需要使用exclude去排除tomcat依赖,并且引入我们需要的服务器,就可以转换服务器的类型。
并且,我们知道默认的Tomca的端口是8080,原因是因为SpringBoot项目中默认把这个端口写死在了Tomcat服务器的父类中。那么如果我们想要修改这个端口,就需要使用Spring提供的后置处理----BeanPostProcess来进行对端口的修改。
最终追述到ServerProperties这个配置类即可。其中有一个port属性,这个属性就会覆盖原有的Tomcat的端口。