天天看點

源碼學習記錄

1、Spring源碼學習

到spring github上下載下傳spring的包下來,并解壓

如果用gradlew.bat運作下載下傳的gradle的版本,就會一直報錯;我自己換了gradle5就沒有問題了。

在源碼路徑下進入cmd界面,輸入gradle cleanidea eclipse執行

2、bean的加載

AbstractBeanFactory.java類的doGetBean方法擷取bean。

過程:final String beanName = transformedBeanName(name);擷取beanName;

Object sharedInstance = getSingleton(beanName);擷取beanName的執行個體

擷取執行個體的過程:調用了DefaultSingletonBeanRegistry.java類,singletonObjects存儲單例;singletonFactories(bean name to ObjectFactory);earlySingletonObjects(存儲單例)執行個體生成前;registeredSingletons注冊的單例

ObjectFactory

FactoryBean是spring架構的重要的接口。

BeanPostProcessor接口有兩個方法:postProcessBeforeInitialization;postProcessAfterInitialization用于初始化執行

DestructionAwareBeanPostProcessor接口繼承BeanPostProcessor接口,并新增了postProcessBeforeDestruction方法用于在bean銷毀前執行。

DisposableBean接口destroy方法,用來釋放類的資源,銷毀類。

InitializingBean 初始化類的接口

BeanFactory接口的結構:

源碼學習記錄

BeanDefinitionRegistry抽象出bean的注冊邏輯,而BeanFactory抽象出bean的管理邏輯;BeanDefinitionRegistry依賴于BeanDefinition。

ConfigurationMetaData加載配置;BeanDefinitionReader會對加載的ConfigurationMetaData進行解析和分析,并将分析後的資訊組裝為相應的BeanDefinition。

三、功能擴充

ApplicationContextAwareProcessor類實作了BeanPostProcessor接口

PropertyPlaceholderConfigurer類解析配置檔案中的${}

BeanFactoryPostProcessor任何實作了這個接口的bean都會在加載bean的配置後執行postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法

MessageSource消息處理的接口

ApplicationContext實作的預設行為是在啟動時将所有的單例bean提前進行執行個體化。

執行個體化的過程就是在方法finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)中完成的。

LifecycleProcessor接口控制bean的生命周期

ApplicationContext包含BeanFactory的所有功能,并提供了擴充功能。

ApplicationContext context = new ClassPathXmlApplicationContext(“com/abc/person.xml”);

setConfigLocations(configLocations);設定配置檔案的路徑

refresh();解析及功能實作

refresh()方法的功能包括:

prepareRefresh();準備重新整理的上下文的環境

obtainFreshBeanFactory();初始化BeanFactory并讀取xml檔案

prepareBeanFactory(beanFactory);對BeanFactory進行填充

postProcessBeanFactory(beanFactory);子類覆寫方法做額外的處理

invokeBeanFactoryPostProcessors(beanFactory);激活BeanFactory的處理器

registerBeanPostProcessors(beanFactory);注冊攔截Bean的處理器

initMessageSource();初始化Message源

initApplicationEventMulticaster();初始化消息廣播器

onRefresh();留給子類初始化其他的bean

registerListeners();注冊監聽器

finishBeanFactoryInitialization(beanFactory);初始化剩餘的單執行個體

finishRefresh();完成重新整理,并通知

resetCommonCaches(); 清除緩存

第七章、AOP

AopNamespaceHandler類注冊了對應的解析器

BeanDefinitionParser解析接口

jdk和cglib方式的總結:1、如果目标對象實作了接口,使用jdk動态代理實作aop;

2、如果目标對象實作了接口,可以強制使用cglib;

3、如果目标對象沒有實作接口,必須使用cglib,spring會自動在jdk和cglib之間切換

第八章、JDBC

execute方法是基礎的操作,update、query隻是傳入不同的PreparedStatementCallback參數來執行不同的邏輯。

PreparedStatement繼承了Statement 的所有功能,PreparedStatement 對象已預編譯過,安全性及執行速度都高于Statement ,不建議使用Statement 。

Bean的生命周期:對象的建立(Construct)、對象的初始化前(BeforeInit)、對象建立之後(PostConstruct)、對象的初始化(Init)、對象的初始化後(AfterInit)、對象銷毀前(PreDestroy)、對象的銷毀(Destroy)

Spring IOC 負責建立對象,管理對象(通過依賴注入(DI),裝配對象,配置對象,并且管理這些對象的整個生命周期。

WebXmlApplicationContext:此容器加載一個XML檔案,此檔案定義了一個WEB應用的所有bean。

Application contexts實作了MessageSource接口,該接口的實作以可插拔的方式提供擷取本地化消息的方法。

WebApplicationContext 繼承了ApplicationContext 并增加了一些WEB應用必備的特有功能,它不同于一般的ApplicationContext ,因為它能處理主題,并找到被關聯的servlet。

Spring 提供了以下 5 中标準的事件:

1.上下文更新事件(ContextRefreshedEvent):該事件會在 ApplicationContext 被初始化或者更新時釋出。也可以在調用 ConfigurableApplicationContext 接口中的 refresh()方法時被觸發。

2.上下文開始事件(ContextStartedEvent):當容器調用 ConfigurableApplicationContext的 Start()方法開始/重新開始容器時觸發該事件。

3.上下文停止事件(ContextStoppedEvent):當容器調用 ConfigurableApplicationContext的 Stop()方法停止容器時觸發該事件。

4.上下文關閉事件(ContextClosedEvent):當 ApplicationContext 被關閉時觸發該事件。容器被關閉時,其管理的所有單例 Bean 都被銷毀。

5.請求處理事件(RequestHandledEvent):在 Web 應用中,當一個 http 請求(request)結束觸發該事件。

Spring 架構中使用到了大量的設計模式,下面列舉了比較有代表性的:

1、代理模式:在 AOP 和 remoting 中被用的比較多。

2、單例模式:在 spring 配置檔案中定義的 bean 預設為單例模式。

3、模闆模式:用來解決代碼重複的問題。

比如. RestTemplate, JmsTemplate, JpaTemplate。

4、委派模式:Spring 提供了 DispatcherServlet 來對請求進行分發。

5、工廠模式:BeanFactory 用來建立對象的執行個體,貫穿于 BeanFactory / ApplicationContext

接口的核心理念。

IOC實作原理:

1、加載容器建立resource對象:首先加載一個spring的容器beanfactory,beanfactory的構造方法會建立一個實作了resource接口執行個體對象。resource對象通過setconfiguration()方法設定spring配置檔案位置。resource對象加載完成後,通過一個super關鍵字建立容器。

2、提取驗證模式:容器建立完成後,開始加載配置檔案。beanfactory通過一個getinputstream方法拿到配置檔案的輸入流。在加載之前,需要驗證xml的正确性和驗證模式。

3、提取資訊:将配置檔案的資訊轉化為document對象。document對象負責将類資訊轉化為spring的特殊結構beandefintion.beandefination會儲存bean的屬性,是否懶加載,是否單例,是否抽象類,是否私有類等

4、注冊:将收集到的所有bean存到一個currenthashmap中,用beanname做key,beandefintion作為value。 如果有相同的key值,并且spring不允許重名。就抛出異常。否則覆寫原來的對象

tomcat在啟動web容器的時候會啟動一個叫ServletContextListener的監聽器,每當在web容器中有ServletContextListener這個接口被執行個體化的時候,web容器會通知ServletContextListener被執行個體的對象去執行其contextInitialized()的方法進行相應的業務處理,而spring架構在設計的過程中ContextLoadListener這個類實作了ServletContextListener這個接口,是以每當有ContextLoadListener這個類被執行個體化的時候,web容器會通知他執行contextInitialized()這個方法。是以在其執行個體化的過程中,contextInitialized()會被調用,進而進行spring容器的啟動與建立的過程中。

2、SpringBoot源碼解析

springboot的啟動入口SpringApplication.run(Application.class, args);

StopWatch類記錄任務開始、結束的時間。

run()方法的流程過程:stopWatch.start();

this.configureHeadlessProperty(); //(設定系統屬性)

this.getRunListeners(args); //(生成監聽器)

listeners.starting(); //(啟動監聽器)

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //(生成參數)

this.prepareEnvironment(listeners, applicationArguments);//(生成配置的環境對象ConfigurableEnvironment)

this.configureIgnoreBeanInfo(environment); //(設定需要忽略的bean)

this.printBanner(environment); //(生成橫幅Banner)

this.createApplicationContext(); //(生成上下文對象ConfigurableApplicationContext)

this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); //異常報告集合

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); //預備加載監聽等資訊(準備容器)

this.refreshContext(context); //與spring的refresh()方法及功能一緻(重新整理容器)

this.afterRefresh(context, applicationArguments); //重新整理容器後的擴充接口

stopWatch.stop(); //統計任務時間

listeners.started(context); //監聽啟動

this.callRunners(context, applicationArguments);

listeners.running(context); //監聽運作

3、SpringCloud元件源碼解析

Eureka源碼解析

@EnableDiscoveryClient注解包含@Import(EnableDiscoveryClientImportSelector.class)注解,進入EnableDiscoveryClientImportSelector類。

EnableDiscoveryClientImportSelector類的selectImports(AnnotationMetadata metadata)方法。

EurekaClientConfigServerAutoConfiguration類

通過該類@ConditionalOnClass注解可知,EurekaClientConfigServerAutoConfiguration類的産生需要一些條件,需要EurekaInstanceConfigBean.class, EurekaClient.class,ConfigServerProperties.class這三個類先行産生。

消費者和提供者的主要功能點有:服務注冊、服務續約、服務下線、服務調用

com.netflix.discovery.DiscoveryClient中:register()服務注冊;renew()//服務續約(就是發送目前應用的心跳請求到注冊中心);getInstancesByVipAddress(String vipAddress, boolean secure)//服務調用(本質就是擷取調用服務名所對應的服務提供者執行個體資訊,包括IP、port等);unregister()//服務下線(發送取消注冊的HTTP請求到注冊中心)

DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,Provider backupRegistryProvider)服務發現的初始化方法

scheduler = Executors.newScheduledThreadPool(2,

new ThreadFactoryBuilder()

.setNameFormat(“DiscoveryClient-%d”)

.setDaemon(true)

.build());

heartbeatExecutor = new ThreadPoolExecutor( //心跳線程池

1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,

new SynchronousQueue(),

new ThreadFactoryBuilder()

.setNameFormat(“DiscoveryClient-HeartbeatExecutor-%d”)

.setDaemon(true)

.build());

cacheRefreshExecutor = new ThreadPoolExecutor(

1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,

new SynchronousQueue(),

new ThreadFactoryBuilder()

.setNameFormat(“DiscoveryClient-CacheRefreshExecutor-%d”)

.setDaemon(true)

.build()

);

initScheduledTasks()方法執行重新整理任務的定時器、用戶端預設注冊到eureka、執行心跳任務的定時器、将目前服務注冊到注冊中心

CacheRefreshThread類擷取注冊資訊,用戶端定時去重新整理服務資訊

HeartbeatThread發送HTTP請求到注冊中心,如果請求響應失敗,則重新調用注冊方法

在DiscoveryClient被建立的時候,在其構造方法中,啟動了三個線程池,然後分别啟動了三個定時器任務:注冊目前服務到注冊中心;持續發送心跳進行續約任務;定時重新整理注冊中心注冊細資訊到本地。

@EnableEurekaServer注解

@Import(EurekaServerMarkerConfiguration.class)// 主要就是注冊該類到SpringBoot中

很多的類并不是被顯示的加載到容器中,而是通過配置的方式,最經典的方式就是放到META-INF/spring.factories檔案中去加載。org.springframework.boot.autoconfigure.EnableAutoConfiguration=

org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration

EnableAutoConfiguration對應的value值清單中的類會在SpringBoot項目啟動的時候注冊到Spring容器中,那麼EurekaServerAutoConfiguration會被預設加載到Spring中,真正的動作應該都在這個類裡。

EurekaServerAutoConfiguration中

eurekaServerConfig(EurekaClientConfig clientConfig):建立并加載EurekaServerConfig的實作類,主要是Eureka-server的配置資訊

eurekaController():Eureka-server的可視化界面就是通過EurekaController提供的

peerAwareInstanceRegistry(ServerCodecs serverCodecs):接收用戶端的注冊等請求就是通過InstanceRegistry來處理

eurekaServerBootstrap(PeerAwareInstanceRegistry registry,EurekaServerContext serverContext):初始化Eureka-server,會同步其他注冊中心的資料到目前注冊中心

InstanceRegistry類

register(final InstanceInfo info, final boolean isReplication):接收用戶端注冊請求

cancel(String appName, String serverId, boolean isReplication):接收用戶端下線請求

renew(final String appName, final String serverId, boolean isReplication):接收用戶端續約請求

服務的注冊實際上是将服務資訊添加到一個map中,map的key是服務名稱,value也是一個map,是提供該服務的所有用戶端資訊;

服務的續約實際上是擷取map中該服務的用戶端資訊,然後修改其最新更新時間

服務的下線實際上是删除該map中該服務資訊,然後修改服務狀态

@FeignClient解析

包含的注解:name:指定FeignClient的名稱,如果項目使用了Ribbon,name屬性會作為微服務的名稱,用于服務發現;

url: url一般用于調試,可以手動指定@FeignClient調用的位址;

decode404:當發生http 404錯誤時,如果該字段位true,會調用decoder進行解碼,否則抛出FeignException;

configuration: Feign配置類,可以自定義Feign的Encoder、Decoder、LogLevel、Contract;

fallback: 定義容錯的處理類,當調用遠端接口失敗或逾時時,會調用對應接口的容錯邏輯,fallback指定的類必須實作@FeignClient标記的接口;

fallbackFactory: 工廠類,用于生成fallback類示例,通過這個屬性我們可以實作每個接口通用的容錯邏輯,減少重複的代碼;

path: 定義目前FeignClient的統一字首;

@FeignClient(name = “github-client”,

url = “https://api.github.com”,

configuration = GitHubExampleConfig.class,

fallback = GitHubClient.DefaultFallback.class)

public interface GitHubClient {

@RequestMapping(value = “/search/repositories”, method = RequestMethod.GET)

String searchRepo(@RequestParam(“q”) String queryStr);

/**
 * 容錯處理類,當調用失敗時,簡單傳回空字元串
 */
@Component
public class DefaultFallback implements GitHubClient {
    @Override
    public String searchRepo(@RequestParam("q") String queryStr) {
        return "";
    }
}
           

}

@EnableFeignClients和@FeignClient兩個注解就實作了Feign的功能。

@EnableFeignClients注入FeignClientsRegistrar類

registerDefaultConfiguration(metadata, registry);針對那些在@EnableFeignClients中添加了defaultConfiguration屬性的進行操作将這些類定義的bean添加到容器中

registerFeignClients(metadata, registry);注冊那些添加了@FeignClient的類或接口

我們最終是向Spring中注冊了一個bean,bean的名稱就是類或接口的名稱(也就是本例中的FeignService),bean的實作類是FeignClientFactoryBean,其屬性設定就是我們在@FeignClient中定義的屬性。

1)@EnableFeignClients注解将類FeignClientsRegistrar注冊到Spring中

2)FeignClientsRegistrar類主要是掃描包路徑下的所有類,将帶有@FeignClient注解的類或接口注冊到Spring中

3)如何注冊帶有@FeignClient的類或接口呢?就是生成一個BeanDefinitionHolder類,beanName為@FeignClient所在接口的名稱,beanDefinition為FeignClientFactoryBean,并将@FeignClient的屬性添加到FeignClientFactoryBean中

4)至此,我們已經把帶有@FeignClient注解的類或接口注冊到Spring中
           

FeignClientFactoryBean源碼結構分析,其實作了FactoryBean接口,那麼當從ApplicationContext中擷取該bean的時候,實際調用的是其getObject()方法。

getObject():擷取容器中的FeignContext實作,預設實作在FeignAutoConfiguration類中;主要使用構造者模式來建構一個Feign(構造者模式建構Feign.Builder);擷取負載均衡後的對象;

[email protected]注解将所有帶有@FeignClient的類或接口注冊到Spring中,注冊類為FeignClientFactoryBean

2.FeignClientFactoryBean.getObject()方法傳回的是一個代理類,InvocationHandler中包含類中每個方法對應的MethodHandler,也就是SynchronousMethodHandler,方法真正執行就是SynchronousMethodHandler.invoke()方法

3.LoadBalancerFeignClient.execute()方法進行業務的處理,在這一步操作中就用到了ribbon和Hystrix功能

4、tomcat解析

tomcat目錄結構及主要檔案:bin用于存放tomcat的啟動、停止等批處理腳步和shell腳步;conf存放tomcat的相關配置檔案;lib伺服器依賴庫目錄;logs預設的日志存放路徑;webapps預設的web應用部署目錄;work應用jsp代碼生成和編譯臨時目錄。

tomcat針對所有擁有生命周期管理特性的元件抽象了一個Lifecycle通用接口,該接口定義了生命周期管理的核心方法。

源碼學習記錄

Lifecycle接口狀态圖

源碼學習記錄

tomcat定義了pipeline(管道)和Valve(閥門)兩個接口。前者用于構造職責鍊,後者代表職責鍊上的每個處理器。

Pipeline中維護了一個基礎的Valve,始終位于Pipeline的末端,封裝了具體的請求處理和輸出響應的過程。

修改後的應用伺服器

源碼學習記錄

connector要完成如下幾項功能:監聽伺服器端口,讀取來自用戶端的請求;将請求資料按照指定協定進行解析;根據請求位址比對正确的容器進行處理;将響應傳回用戶端。

解決并發問題,加入Executor接口。

源碼學習記錄

伺服器的完整設計如圖

源碼學習記錄

Server表示整個Servlet容器,是以隻有一個Server執行個體,一個Server包含多個Service;

Service表示一個或多個Connector的集合,這些Connector共同享用Container來處理其請求。

Connector用于監聽并轉化Socket請求,讀取Socket請求交由Container處理,支援不同協定以及不同的I/O方式。

Container能夠執行用戶端請求并傳回響應的一類對象。不同級别的容器Engine、Host、Context、Wrapper。

Engine表示整個Servlet引擎。

Host表示Servlet引擎中的虛拟機,與伺服器的網絡名有關。

Context表示ServletContext,一個ServletContext表示一個獨立的Web應用

Wrapper表示Web應用中Servlet

Executor表示Tomcat元件之間可以共享的線程池

伺服器啟動時序圖

源碼學習記錄

Tomcat加載器

應用伺服器通常會自行建立類加載器以實作更靈活的控制,一方面對規範的實作,另一方面也有架構層面的考慮。

1、隔離性:web應用類庫互相隔離,避免依賴庫或者應用包互相影響。

2、靈活性:應用之間的類加載器互相獨立,針對一個應用進行重新部署,此時該應用的類加載器将會重新建立,而且不會影響其他應用。

3、性能:應用在加載類時,不會搜尋其他web應用包含的jar包,性能自然高于 應用伺服器隻有一個類加載器的情況。

Common:以System為父類加載器,預設指向catalina_home/lib下的包,負責加載伺服器内部和web應用可見的類。

Catalina:以Common類為父類加載器,負責加載隻有tomcat應用伺服器内部可見的類。

Shared:以Common類為父類加載器,負責加載web應用共享的類,這些類tomcat伺服器不會依賴

Web應用:以Shared類為父類加載器,加載/web-inf/classes目錄下的class和資源檔案以及jar包。該類加載器隻對目前Web應用可見,對其他Web應用均不可見。

spring的傳播特性: PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY, PROPAGATION_REQUIRES_NEW, PROPAGATION_NOT_SUPPORTED, PROPAGATION_NEVER, PROPAGATION_NESTED

事務隔離級别: ISOLATION_DEFAULT,ISOLATION_READ_UNCOMMITTED,ISOLATION_READ_COMMITTED,ISOLATION_REPEATABLE_READ,ISOLATION_SERIALIZABLE

源碼學習記錄

事務包spring-tx.jar

事務的定義接口:TransactionDefinition

TransactionAttribute接口繼承TransactionDefinition

TransactionDefinition 的一個實作類:DefaultTransactionDefinition

TransactionAttribute的實作類:DefaultTransactionAttribute

事務模闆類TransactionTemplate核心是裡面有PlatformTransactionManager 這個事務管理類,用它來對事務送出和復原。我們的業務邏輯隻要寫在TransactionCallback.doInTransaction()方法裡面既可以,每次執行這個方法前,先會transactionManager.getTransaction(this)開啟一 個事務,執行TransactionCallback.doInTransaction()異常的話會調用transactionManager.rollback(status)來復原事務,正确的話就會調用transactionManager.commit(status)送出事務

AOP核心概念如下:

通知(Advice):定義了切面(各處業務代碼中都需要的邏輯提煉成的一個切面)做什麼what+when何時使用。例如:前置通知Before、後置通知After、傳回通知After-returning、異常通知After-throwing、環繞通知Around.

連接配接點(Joint point):程式執行過程中能夠插入切面的點,一般有多個。比如調用方式時、抛出異常時。

切點(Pointcut):切點定義了連接配接點,切點包含多個連接配接點,即where哪裡使用通知.通常指定類+方法 或者 正規表達式來比對 類和方法名稱。

切面(Aspect):切面=通知+切點,即when+where+what何時何地做什麼。

引入(Introduction):允許我們向現有的類添加新方法或屬性。

織入(Weaving):織入是把切面應用到目标對象并建立新的代理對象的過程。

源碼學習記錄

AutoProxyRegistrar實作了ImportBeanDefinitionRegistrar接口,複寫registerBeanDefinitions方法

申明式事務,核心流程如下:

1.createTransactionIfNecessary():如果有必要,建立事務

2.InvocationCallback的proceedWithInvocation():InvocationCallback是父類的内部回調接口,子類中實作該接口供父類調用,子類TransactionInterceptor中invocation.proceed()。回調方法執行

3.異常復原completeTransactionAfterThrowing()

源碼學習記錄

AbstractPlatformTransactionManager實作了getTransaction()方法

TransactionSynchronizationManager事務同步管理器,該類維護了多個線程本地變量ThreadLocal

SqlSessionSynchronization是SqlSessionUtils的一個内部類,繼承自TransactionSynchronizationAdapter抽象類,實作了事務同步接口TransactionSynchronization。

源碼學習記錄

TransactionSynchronization接口定義了事務操作時的對應資源的(JDBC事務那麼就是SqlSessionSynchronization)

Java事務的類型有三種:JDBC事務、JTA(Java Transaction API)事務、容器事務。

JDBC 事務是用 Connection 對象控制的。JDBC 事務的一個缺點是事務的範圍局限于一個資料庫連接配接。一個 JDBC 事務不能跨越多個資料庫。

JTA是一種高層的,與實作無關的,與協定無關的API,應用程式和應用伺服器可以使用JTA來通路事務。 JTA允許應用程式執行分布式事務處理——在兩個或多個網絡計算機資源上通路并且更新資料,這些資料可以分布在多個資料庫上。JDBC驅動程式的JTA支援極大地增強了資料通路能力。

容器事務主要是J2EE應用伺服器提供的,容器事務大多是基于JTA完成,這是一個基于JNDI的,相當複雜的API實作。

繼續閱讀