目錄導航
前言
前面的章節我們講了源碼分析專題,分佈式專題,工程專題。從本節開始,進入微服務專題的講解,共計16小節,分別是:
- 微服務專題01-Spring Application
- 微服務專題02-Spring Web MVC 視圖技術
- 微服務專題03-REST
- 微服務專題04-Spring WebFlux 原理
- 微服務專題05-Spring WebFlux 運用
- 微服務專題06-雲原生應用(Cloud Native Applications)
- 微服務專題07-Spring Cloud 配置管理
- 微服務專題08-Spring Cloud 服務發現
- 微服務專題09-Spring Cloud 負載均衡
- 微服務專題10-Spring Cloud 服務熔斷
- 微服務專題11-Spring Cloud 服務調用
- 微服務專題12-Spring Cloud Gateway
- 微服務專題13-Spring Cloud Stream (上)
- 微服務專題14-Spring Cloud Bus
- 微服務專題15-Spring Cloud Stream 實現
- 微服務專題16-Spring Cloud 整體回顧
本節內容重點爲:
- 自定義 SpringApplication:介紹
SpringApplication
與SpringApplicationBuilder
的API調整 - 配置 Spring Boot 源:理解 Spring Boot 配置源
- SpringAppliation 類型推斷:Web 應用類型、Main Class 推斷
- Spring Boot 事件:介紹 Spring Boot 事件與 Spring Framework 事件之間的差異和聯繫
自定義 SpringApplication
如圖所示,在https://start.spring.io/官網上快速搭建一個SpringBoot項目,通常把它稱爲SpringBoot的腳手架~
最簡單的SpringBoot項目也會包括SpringApplication啓動類,所以通常我們將SpringApplication作爲一切的起點,首先看看這個類是如何定義的
SpringApplication
SpringApplication
Spring Boot 驅動 Spring 應用上下文的引導類
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
這裏有三個註解值得注意:
@ComponentScan
: 它是版本引入的? Spring Framework 3.1
@EnableAutoConfiguration
: 激活自動裝配 @Enable
-> @Enable
開頭的
@EnableWebMvc
@EnableTransactionManagement
@EnableAspectJAutoProxy
@EnableAsync
@SpringBootConfiguration
: 等價於 @Configuration
-> Configuration Class 註解
@Component
的“派生性”
所謂派生性就是通俗意義上講的長得像~,你看@Component
-> @ComponentScan
這裏將component稱之爲元註解,因爲componentScan是它派生出來的
關於對此註解的處理,以及掃描實現過程,可以參考以下內容:
Component 的處理類 -> ConfigurationClassParser
Component 的掃描類 ->
-
ClassPathBeanDefinitionScanner
-
ClassPathScanningCandidateComponentProvider
protected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ... }
同理於 Dubbo
@Service
-> 2.5.7 -> -
new AnnotationTypeFilter(Service.class);
Spring 註解編程模型
https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model
這裏再看一下在SpringBoot常見的註解,諸如@Service
、@Repository
、@Controller
、@Configuration
在spring源碼裏,其實都是引入了@component
,作爲這些註解的元註解。
-
@Component
-
@Service
@Component public @interface Service { ... }
-
@Repository
@Component public @interface Repository { ... }
-
@Controller
@Component public @interface Controller { ... }
-
@Configuration
@Component public @interface Configuration { ... }
-
Spring 模式註解:Stereotype Annotations
所謂的模式註解:@component
邏輯上與@Service
、@Repository
、@Controller
、@Configuration
都是一樣,只是物理層面上不同,但都是爲了找到BeanDefinition。
Spring 註解驅動示例
區別於ClassPathXmlApplicationContext
以xml配置文件驅動,註解驅動的上下文是 AnnotationConfigApplicationContext
,這是在 Spring Framework 3.0 開始引入的
在springcloud的微服務架構中,會多次提到版本問題,因爲不同版本之間差異性可能很大,比如springcloud的1.0與2.0差異就很明顯,所以平時應注意版本問題!
@Configuration
public class SpringAnnotationDemo {
public static void main(String[] args) {
// @Bean @Configuration
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 註冊一個 Configuration Class = SpringAnnotationDemo
context.register(SpringAnnotationDemo.class);
// 上下文啓動
context.refresh();
System.out.println(context.getBean(SpringAnnotationDemo.class));
}
}
無論是xml配置還是註解配置,其目的只有一個,就是找 BeanDefinition
現在我們來分析一下,@SpringBootApplication
這個註解,下面是通過源碼顯示的調用關係
@SpringBootApplication
@SpringBootConfiguration
@Configuration
@Component
@SpringBootApplication
標註當前一些功能。我們知道,註解不能像Java類一樣繼承,就通過以上的這樣的方式層層調用,而註解的功能基本相似,我們就把註解的這種特性稱之爲派生性
SpringApplication
Spring Boot 應用的引導
基本寫法
SpringApplication springApplication = new SpringApplication(MicroservicesProjectApplication.class);
Map<String,Object> properties = new LinkedHashMap<>();
properties.put("server.port",0);
springApplication.setDefaultProperties(properties);
springApplication.run(args);
SpringApplicationBuilder
new SpringApplicationBuilder(MicroservicesProjectApplication.class) // Fluent API
// 單元測試是 PORT = RANDOM
.properties("server.port=0") // 隨機向 OS 要可用端口
.run(args);
Spring Boot 引導示例
@SpringBootApplication
public class MicroservicesProjectApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MicroservicesProjectApplication.class);
Map<String,Object> properties = new LinkedHashMap<>();
properties.put("server.port",0);
springApplication.setDefaultProperties(properties);
ConfigurableApplicationContext context = springApplication.run(args);
// 有異常?實際上沒有
System.out.println(context.getBean(MicroservicesProjectApplication.class));
}
}
調整示例爲 非 Web 程序
@SpringBootApplication
public class MicroservicesProjectApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MicroservicesProjectApplication.class);
Map<String, Object> properties = new LinkedHashMap<>();
properties.put("server.port", 0);
springApplication.setDefaultProperties(properties);
// 設置爲 非 web 應用
springApplication.setWebApplicationType(WebApplicationType.NONE);
ConfigurableApplicationContext context = springApplication.run(args);
// 有異常?
System.out.println(context.getBean(MicroservicesProjectApplication.class));
// 輸出當前 Spring Boot 應用的 ApplicationContext 的類名
System.out.println("當前 Spring 應用上下文的類:" + context.getClass().getName());
}
}
輸出結果:
當前 Spring 應用上下文的類:org.springframework.context.annotation.AnnotationConfigApplicationContext
配置 Spring Boot 源
SpringAppliation 類型推斷
當不加以設置 Web 類型,那麼它採用推斷:
-> SpringAppliation()
-> deduceWebApplicationType()
第一次推斷爲 WebApplicationType.SERVLET
deduceWebApplicationType方法的邏輯爲:
-
WebApplicationType.NONE
: 非 Web 類型Servlet
不存在- Spring Web 應用上下文
ConfigurableWebApplicationContext
不存在spring-boot-starter-web
不存在spring-boot-starter-webflux
不存在
-
WebApplicationType.REACTIVE
: Spring WebFluxDispatcherHandler
spring-boot-starter-webflux
存在
Servlet
不存在spring-boot-starter-web
不存在
-
WebApplicationType.SERVLET
: Spring MVCspring-boot-starter-web
存在
注意deduceWebApplicationType方法這裏的幾個private final String的常量,分別是:
人工干預 Web 類型
設置 webApplicationType 屬性 爲 WebApplicationType.NONE
Spring Boot 事件
Spring 事件demo:
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// 添加事件監聽器
// context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {
// @Override
// public void onApplicationEvent(ApplicationEvent event) {
// System.err.println("監聽事件:" + event);
//
// }
// });
// 添加自義定監聽器
context.addApplicationListener(new ClosedListener());
context.addApplicationListener(new RefreshedListener());
// 啓動 Spring 應用上下文
context.refresh();
// 一個是 ContextRefreshedEvent
// 一個是 PayloadApplicationEvent
// Spring 應用上下文發佈事件
context.publishEvent("HelloWorld"); // 發佈一個 HelloWorld 內容的事件
// 一個是 MyEvent
context.publishEvent(new MyEvent("HelloWorld 2020"));
// 一個是 ContextClosedEvent
// 關閉應用上下文
context.close();
}
private static class RefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("上下文啓動:" + event);
}
}
private static class ClosedListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
System.out.println("關閉上下文:" + event);
}
}
private static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
Spring 內部發送事件
開啓上下文涉及的類的層級關係
ContextRefreshedEvent
ApplicationContextEvent
ApplicationEvent
開啓上下文調用鏈
refresh()
-> finishRefresh()
-> publishEvent(new ContextRefreshedEvent(this));
關閉上下文涉及的類的層級關係
ContextClosedEvent
ApplicationContextEvent
ApplicationEvent
關閉上下文調用鏈
close()
-> doClose()
-> publishEvent(new ContextClosedEvent(this));
自定義事件
PayloadApplicationEvent
*Spring 事件都是ApplicationEvent
類型,Spring事件理解爲消息
發送 Spring 事件通過 ApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)
Spring 事件的類型
ApplicationEvent
Spring 事件監聽器ApplicationListener
Spring 事件廣播器ApplicationEventMulticaster
實現類:SimpleApplicationEventMulticaster
ApplicationEvent
相當於消息內容
ApplicationListener
相當於消息消費者、訂閱者
ApplicationEventMulticaster
相當於消息生產者、發佈者
Spring Boot 事件監聽示例
demo1:
public static void main(String[] args) {
ApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
// 添加監聽器
multicaster.addApplicationListener(event -> {
if (event instanceof PayloadApplicationEvent) {
System.out.println("接受到 PayloadApplicationEvent :"
+ PayloadApplicationEvent.class.cast(event).getPayload());
}else {
System.out.println("接收到事件:" + event);
}
});
// 發佈/廣播事件
multicaster.multicastEvent(new MyEvent("Hello,World"));
multicaster.multicastEvent(new PayloadApplicationEvent<Object>("2", "Hello,World"));
}
private static class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
運行結果:
demo2:
@EnableAutoConfiguration
public class SpringBootEventDemo {
public static void main(String[] args) {
new SpringApplicationBuilder(SpringBootEventDemo.class)
.listeners(event -> { // 增加監聽器
System.err.println("監聽到事件 : " + event.getClass().getSimpleName());
})
.run(args)
.close();
; // 運行
}
}
運行結果:
按照時間順序打印的順序如下:
- ApplicationStartingEvent(1)
- ApplicationEnvironmentPreparedEvent(2)
- ApplicationPreparedEvent(3)
- ContextRefreshedEvent
- ServletWebServerInitializedEvent
- ApplicationStartedEvent(4)
- ApplicationReadyEvent(5)
- ContextClosedEvent
- ApplicationFailedEvent (特殊情況)(6)
Spring Boot 事件監聽器
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
ConfigFileApplicationListener
監聽 ApplicationEnvironmentPreparedEvent
事件,從而加載 application.properties
或者 application.yml
文件
Spring Boot 很多組件依賴於 Spring Boot 事件監聽器實現,本質是 Spring Framework 事件/監聽機制
SpringApplication
利用
- Spring 應用上下文(
ApplicationContext)
生命週期控制 註解驅動 Bean - Spring 事件/監聽(
ApplicationEventMulticaster
)機制加載或者初始化組件
後記
本節思考:
q1:webApplicationType分爲三種都有什麼實用地方?
q2:框架底層的事件是單線程麼?業務實現是否可以使用事件去實現?如果使用事件實現會不會是不是會有性能問題?
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Nullable
private Executor taskExecutor;
...
}
更多架構知識,歡迎關注本套Java系列文章:Java架構師成長之路