點(diǎn)贊再看焰坪,養(yǎng)成習(xí)慣!
SpringBoot的故事從一個(gè)面試題開(kāi)始
Spring Boot南吮、Spring MVC 和 Spring 有什么區(qū)別琳彩?
分開(kāi)描述各自的特征:
Spring 框架就像一個(gè)家族,有眾多衍生產(chǎn)品例如 boot、security露乏、jpa等等碧浊。但他們的基礎(chǔ)都是Spring 的ioc和 aop,ioc 提供了依賴注入的容器瘟仿, aop解決了面向橫切面的編程箱锐,然后在此兩者的基礎(chǔ)上實(shí)現(xiàn)了其他延伸產(chǎn)品的高級(jí)功能。
Spring MVC提供了一種輕度耦合的方式來(lái)開(kāi)發(fā)web應(yīng)用劳较。它是Spring的一個(gè)模塊驹止,是一個(gè)web框架。通過(guò)Dispatcher Servlet, ModelAndView 和 View Resolver观蜗,開(kāi)發(fā)web應(yīng)用變得很容易臊恋。解決的問(wèn)題領(lǐng)域是網(wǎng)站應(yīng)用程序或者服務(wù)開(kāi)發(fā)——URL路由、Session墓捻、模板引擎抖仅、靜態(tài)Web資源等等。
Spring Boot實(shí)現(xiàn)了自動(dòng)配置砖第,降低了項(xiàng)目搭建的復(fù)雜度撤卢。它主要是為了解決使用Spring框架需要進(jìn)行大量的配置太麻煩的問(wèn)題,所以它并不是用來(lái)替代Spring的解決方案梧兼,而是和Spring框架緊密結(jié)合用于提升Spring開(kāi)發(fā)者體驗(yàn)的工具放吩。同時(shí)它集成了大量常用的第三方庫(kù)配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot應(yīng)用中這些第三方庫(kù)幾乎可以零配置的開(kāi)箱即用(out-of-the-box)羽杰。
所以渡紫,用最簡(jiǎn)練的語(yǔ)言概括就是:
Spring 是一個(gè)“引擎”;
Spring MVC 是基于Spring的一個(gè) MVC 框架;
Spring Boot 是基于Spring4的條件注冊(cè)的一套快速開(kāi)發(fā)整合包。
SpringBoot到底是怎么做到自動(dòng)配置的忽洛?
從代碼里看項(xiàng)目SpringBoot的項(xiàng)目啟動(dòng)類只有一個(gè)注解@SpringBootApplication和一個(gè)run方法腻惠。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
直接看@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
@SpringBootApplication:包含了@SpringBootConfiguration(打開(kāi)是@Configuration),@EnableAutoConfiguration欲虚,@ComponentScan注解集灌。
@Configuration
JavaConfig形式的Spring Ioc容器的配置類使用的那個(gè)@Configuration,SpringBoot社區(qū)推薦使用基于JavaConfig的配置形式复哆,所以欣喧,這里的啟動(dòng)類標(biāo)注了@Configuration之后,本身其實(shí)也是一個(gè)IoC容器的配置類梯找。
對(duì)比一下傳統(tǒng)XML方式和config配置方式的區(qū)別:
- XML聲明和定義配置方式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
">
<bean id="app" class="com..." />
- 用一個(gè)過(guò)濾器舉例唆阿,JavaConfig的配置方式是這樣:
@Configuration
public class DruidConfiguration {
@Bean
public FilterRegistrationBean statFilter(){
//創(chuàng)建過(guò)濾器
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
//設(shè)置過(guò)濾器過(guò)濾路徑
filterRegistrationBean.addUrlPatterns("/*");
//忽略過(guò)濾的形式
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
任何一個(gè)標(biāo)注了@Configuration的Java類定義都是一個(gè)JavaConfig配置類。
任何一個(gè)標(biāo)注了@Bean的方法锈锤,其返回值將作為一個(gè)bean定義注冊(cè)到Spring的IoC容器驯鳖,方法名將默認(rèn)成該bean定義的id闲询。
@ComponentScan
@ComponentScan對(duì)應(yīng)XML配置中的元素,@ComponentScan的功能其實(shí)就是自動(dòng)掃描并加載符合條件的組件(比如@Component和@Repository等)或者bean定義浅辙,最終將這些bean定義加載到IoC容器中扭弧。
我們可以通過(guò)basePackages等屬性來(lái)細(xì)粒度的定制@ComponentScan自動(dòng)掃描的范圍,如果不指定记舆,則默認(rèn)Spring框架實(shí)現(xiàn)會(huì)從聲明@ComponentScan所在類的package進(jìn)行掃描鸽捻。
注:所以SpringBoot的啟動(dòng)類最好是放在root package下,因?yàn)槟J(rèn)不指定basePackages泽腮。
@EnableAutoConfiguration
(核心內(nèi)容)看英文意思就是自動(dòng)配置御蒲,概括一下就是,借助@Import的幫助诊赊,將所有符合自動(dòng)配置條件的bean定義加載到IoC容器厚满。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
里面最關(guān)鍵的是@Import(EnableAutoConfigurationImportSelector.class),借助EnableAutoConfigurationImportSelector碧磅,@EnableAutoConfiguration可以幫助SpringBoot應(yīng)用將所有符合條件的@Configuration配置都加載到當(dāng)前SpringBoot創(chuàng)建并使用的IoC容器痰滋。該配置模塊的主要使用到了SpringFactoriesLoader。
SpringFactoriesLoader詳解
SpringFactoriesLoader為Spring工廠加載器续崖,該對(duì)象提供了loadFactoryNames方法,入?yún)閒actoryClass和classLoader即需要傳入工廠類名稱和對(duì)應(yīng)的類加載器团搞,方法會(huì)根據(jù)指定的classLoader严望,加載該類加器搜索路徑下的指定文件,即spring.factories文件逻恐,傳入的工廠類為接口像吻,而文件中對(duì)應(yīng)的類則是接口的實(shí)現(xiàn)類,或最終作為實(shí)現(xiàn)類复隆。
public abstract class SpringFactoriesLoader {
//...
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
...
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
....
}
}
所以文件中一般為如下圖這種一對(duì)多的類名集合拨匆,獲取到這些實(shí)現(xiàn)類的類名后,loadFactoryNames方法返回類名集合挽拂,方法調(diào)用方得到這些集合后惭每,再通過(guò)反射獲取這些類的類對(duì)象、構(gòu)造方法亏栈,最終生成實(shí)例台腥。
下圖有助于我們形象理解自動(dòng)配置流程(盜個(gè)圖)
AutoConfigurationImportSelector
繼續(xù)上面講的AutoConfigurationImportSelector.class。該類主要關(guān)注selectImports方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
該方法在springboot啟動(dòng)流程——bean實(shí)例化前被執(zhí)行绒北,返回要實(shí)例化的類信息列表黎侈。如果獲取到類信息,spring可以通過(guò)類加載器將類加載到j(luò)vm中闷游,現(xiàn)在我們已經(jīng)通過(guò)spring-boot的starter依賴方式依賴了我們需要的組件峻汉,那么這些組件的類信息在select方法中就可以被獲取到贴汪。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
方法中的getCandidateConfigurations方法,其返回一個(gè)自動(dòng)配置類的類名列表休吠,方法調(diào)用了loadFactoryNames方法扳埂,查看該方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
自動(dòng)配置器會(huì)跟根據(jù)傳入的factoryClass.getName()到項(xiàng)目系統(tǒng)路徑下所有的spring.factories文件中找到相應(yīng)的key,從而加載里面的類蛛碌。我們就選取這個(gè)mybatis-spring-boot-autoconfigure下的spring.factories文件
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
進(jìn)入org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中聂喇,又是一堆注解
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnBean({DataSource.class})
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisAutoConfiguration
{
private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
private final List<ConfigurationCustomizer> configurationCustomizers;
- @Spring的Configuration是一個(gè)通過(guò)注解標(biāo)注的springBean,
- @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class})這個(gè)注解的意思是:當(dāng)存在SqlSessionFactory.class, SqlSessionFactoryBean.class這兩個(gè)類時(shí)才解析MybatisAutoConfiguration配置類,否則不解析這一個(gè)配置類蔚携。我們需要mybatis為我們返回會(huì)話對(duì)象希太,就必須有會(huì)話工廠相關(guān)類
- @CondtionalOnBean(DataSource.class):只有處理已經(jīng)被聲明為bean的dataSource
- @ConditionalOnMissingBean(MapperFactoryBean.class)這個(gè)注解的意思是如果容器中不存在name指定的bean則創(chuàng)建bean注入,否則不執(zhí)行
以上配置可以保證sqlSessionFactory酝蜒、sqlSessionTemplate誊辉、dataSource等mybatis所需的組件均可被自動(dòng)配置,@Configuration注解已經(jīng)提供了Spring的上下文環(huán)境亡脑,所以以上組件的配置方式與Spring啟動(dòng)時(shí)通過(guò)mybatis.xml文件進(jìn)行配置起到一個(gè)效果堕澄。
只要一個(gè)基于SpringBoot項(xiàng)目的類路徑下存在SqlSessionFactory.class, SqlSessionFactoryBean.class,并且容器中已經(jīng)注冊(cè)了dataSourceBean霉咨,就可以觸發(fā)自動(dòng)化配置蛙紫,意思說(shuō)我們只要在maven的項(xiàng)目中加入了mybatis所需要的若干依賴,就可以觸發(fā)自動(dòng)配置途戒,但引入mybatis原生依賴的話坑傅,每集成一個(gè)功能都要去修改其自動(dòng)化配置類,那就得不到開(kāi)箱即用的效果了喷斋。所以Spring-boot為我們提供了統(tǒng)一的starter可以直接配置好相關(guān)的類唁毒,觸發(fā)自動(dòng)配置所需的依賴(mybatis)如下:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
因?yàn)閙aven依賴的傳遞性,我們只要依賴starter就可以依賴到所有需要自動(dòng)配置的類星爪,實(shí)現(xiàn)開(kāi)箱即用的功能浆西。也體現(xiàn)出Springboot簡(jiǎn)化了Spring框架帶來(lái)的大量XML配置以及復(fù)雜的依賴管理,讓開(kāi)發(fā)人員可以更加關(guān)注業(yè)務(wù)邏輯的開(kāi)發(fā)顽腾。
再貼個(gè)盜的圖SpringBoot的啟動(dòng)結(jié)構(gòu)圖
寫(xiě)在最后的話
今天近零,我們介紹了一個(gè)面試題引起的SpringBoot啟動(dòng)解析
想要獲得更多關(guān)于spring boot的資料,徹底吃透,可以私信我關(guān)鍵詞[資料],或者看下方,獲取更多java相關(guān)知識(shí).
~聯(lián)系方式~
wechat: Mlzg5201314zz
扣扣:2967728282///