數據結構
數組
Array、Array List...
特點
char[] cs = new char[5];
char[] cs2 = new char[]{'fish','bone'};
cs[0] = "fish";
...
- 內存地址連續(xù)购披,使用前需要指定數組長度杖挣。
- 有下標。依據下標來獲取刚陡、設置值惩妇。查詢效率高。
- 增刪操作效率較低筐乳。(需要考慮下標越界問題歌殃,動態(tài)擴縮容)
總結
增刪操作效率較低主要指:
- 新增。
- 初始長度假如是5蝙云,不停的新增氓皱,當增加到第6個元素時,則需要從觸發(fā)<font color="red">擴容</font>操作(一般會提前觸發(fā)不會等數組滿了才觸發(fā))勃刨,擴容操作則需要重新建立一個數組波材,將之前數據中元素<font color="red">移動</font>過來。
- 初始長度范圍內的新增效率很高身隐。
- 修改廷区。
- 如果要修改某個元素,比如將數組中'bone'修改為‘bones’則需要遍歷查抄贾铝,然后修改隙轻。
- 直接通過下標修改效率比較高埠帕。
- 刪除。
- 如果刪除雖然元素不在玖绿,但是內存依然存在敛瓷,且空間是連續(xù)的。比較占用內存镰矿。
綜上所述琐驴,數組適用于,查詢操作較多秤标。修改绝淡、新增、刪除操作較少的定長數據情況。
鏈表
單向鏈表|雙向鏈表(Linked List)
特點
- 靈活的空間要求,存儲空間不要求連續(xù)惠奸。
- 不支持下標訪問扛拨,查詢效率低泽疆,支持順序遍歷檢索(next)。
- 針對增刪操作效率會比較高,只需要操作節(jié)點,無需移動元素丝格。
總結
鏈表適合于新增,刪除操作較多的情況棵譬,由于無法通過下標快速獲取到某個位置元素显蝌,所以對查詢操作不夠友好。
數組订咸、鏈表問題
-
在<font color="red">連續(xù)新增</font>100W條數據的情況下曼尊,ArrayList和LinkedList哪一個效率更快?為什么脏嚷?
- <font color="blue">錯誤回答:</font>LinkedList,因為他是鏈表結構骆撇,鏈表接口新增效率高。ArrayList是數組接口父叙,新增慢神郊。
- <font color="blue">分析:</font>這個問題主要考慮理解深度,不能簡單因為鏈表新增快就回答LinkedList高每。所有的快慢都是需要分情況的屿岂。
- <font color="blue"> 正確回答:</font> ArrayList效率高。因為LinkedList每次新增都要new Node()節(jié)點鲸匿。此外還需要操作prev、next兩個節(jié)點阻肩。而ArrayList如果初始就把長度固定則不需要創(chuàng)建那么多對象出來带欢。所以數組的效率就要高于鏈表运授。
public static void main(String[] args) { long startTime=System.currentTimeMillis(); //獲取開始時間 arrayTest(); long endTime=System.currentTimeMillis(); //獲取結束時間 System.out.println("程序運行時間1: "+(endTime-startTime)+"ms"); long startTime1 = System.currentTimeMillis(); //獲取開始時間 linkTest(); long endTime1 = System.currentTimeMillis(); //獲取結束時間 System.out.println("程序運行時間2: "+(endTime1-startTime1)+"ms"); } private static void linkTest() { LinkedList linkedList = new LinkedList(); for(int i=0;i<1000000;i++){ linkedList.add(i); } } private static void arrayTest() { ArrayList array = new ArrayList(1000000); for(int i=0;i<1000000;i++){ array.add(i); } }
// 執(zhí)行結果 程序運行時間1: 18ms 程序運行時間2: 28ms Process finished with exit code 0
樹(Tree)
二叉樹
只有左右兩個叉的樹結構。
二叉樹中包含:
- 平衡二叉樹
- AVL樹乔煞,通過自旋轉實現平衡吁朦。
- 不平衡二叉樹
- 二叉查找樹一般都是非平衡的。
- 完全平衡二叉樹
- 不完全平衡二叉樹(紅黑樹)渡贾。
特點
- 某節(jié)點的左側樹節(jié)點值僅包含小于該節(jié)點的值逗宜。
- 某節(jié)點的右側樹節(jié)點值僅包含大于該節(jié)點的值。
- 左右樹節(jié)點每個也必須是二叉樹空骚。
- 順序排列纺讲。
紅黑樹
自平衡二叉樹。(黑平衡二叉樹)
特點
- 每個節(jié)點要么是紅囤屹,要么是黑熬甚。
- 根節(jié)點必須是黑色。
- 每個葉子節(jié)點【NIL】必須是黑色肋坚。
- 每個紅色節(jié)點的子節(jié)點必須是黑色乡括。
- 任意節(jié)點向下到任意葉子節(jié)點【NIL】的路徑包含相同數量的黑色節(jié)點(黑平衡二叉樹)。
BTree
平衡多路查找樹智厌,節(jié)點不只是2個诲泌。
每個節(jié)點都會存儲數據。
B+Tree
1kb = 1024b , 1字節(jié) = 8 位铣鹏。 UUID 32位
平衡多路查找樹敷扫,節(jié)點不只是2個。
是對BTree的優(yōu)化吝沫,數據僅存儲在葉子結點呻澜,且葉子節(jié)點以鏈表的形式存在。
集合
TreeMap
特點
- 本質是紅黑樹的實現
- 有序的
HashMap
數組+鏈表惨险。數組+紅黑樹
鏈表數據長度>8的時候轉化為紅黑樹羹幸。
HashSet
HashSet底層是HashMap,本質是一個沒有重復元素的集合,通過hashmap實現辫愉。
HashSet hashSet = new HashSet();
hashSet.add(1);
//源碼
public HashSet(){
map = new HashMap<>();
}
public boolean add(E e){
return map.put(e,PRESENT)==null;
}
TreeSet
TreeSet底層是TreeMap,本質是將數據保存在TreeMap中栅受,key為添加的數據,value是一個固定的值恭朗。
Spring
Spring 特點
Spring 主要為了使開發(fā)人員能夠更專注也業(yè)務本身屏镊,盡可能的減少其他非業(yè)務的東西而出現。
為了實現他這個目的痰腮,Spring 一方面提供了三大核心功能(IOC而芥,DI,AOP)幫助開發(fā)人員管理Bean的生命周期膀值。
另一方面 將自己變成一個萬能膠棍丐,利用自身提供的一些擴展點误辑,能夠對市面上的大部分框架和中間建進行集成,方便使用歌逢。
同時也不斷的優(yōu)化自身巾钉,從最開始的XML到現在的注解一切都是為了方便開發(fā)人員,提高開發(fā)效率秘案。
IOC(控制反轉)
Spring的IOC主要是為了實現對Bean的統(tǒng)一管理砰苍,將Bean的初始化、創(chuàng)建阱高,銷毀都交由Spring的IOC容器來實現赚导。
BeanFactory(IOC容器的定義類)
1. 對IOC的基本行為做了定義。(getBean,constainsBean,isSingleton,isPropotype,isTypeMatch,getType)
2. 主要實現類ListableBeanFactory讨惩、HierarchialBeanFactory(有繼承關系)辟癌、AutowireCapableBeanFactory定義自動裝配規(guī)則。
3. 最終實現類是DefaultListableBeanFactory荐捻,該類包含beanDefinitionMap等集合黍少。
ApplicationContext(高級的IOC容器)
1. 除了提供IOC容器的基本操作外(AbstractApplicationContext)還提供了一些附加功能。
2. 主要實現類ClassPathXMLApplicaitonContext处面、AnnotationConfigApplicaitonContext
BeanDefinition
1. Bean對象的描述類厂置,描述了Bean對象的基本信息以及相互關系(類名、作用域魂角、屬性昵济、依賴關系)。
2. 主要實現類AbstuctBeanDefinition野揪、RootBeanDefinition访忿、GenericBeanDenifition。
BeanDefinitionReader
1. Bean對象的解析類斯稳。
2. AbstractBeanDefinitionReader海铆、XmlBeanDefinitionReader、PropertiesBeanDefinitionReader
IOC的過程實際是為了完成BeanDefinition的注冊挣惰,放在beanDefinitionMap中卧斟。
首先這個過程中初始化的配置定義是多樣的,有XML憎茂、注解等珍语,所以需要使用不同的策略來去讀取。這里就包含兩個主要的核心類ClassPathXMLApplicaitonContext竖幔、AnnotationConfigApplicaitonContext板乙,以及對應的解析器XmlBendifinitionReader、AnnotatedBeanDefinitionReader拳氢,為了方便管理又使用了BeanDefinition來統(tǒng)一配置標準亡驰。
他的核心流程大概分為三個階段:定位—》加載—》注冊晓猛。
Web IOC為例:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--Spring前端控制器饿幅,攔截所有請求-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
首先從web.xml配置的DispatcherServlet為起點(tomcat啟動時也會調用ContextLoaderListener類contextInitialized方法)凡辱,首先調用HttpServlet類的init()方法,init()方法或者contextInitialized()中會調用initWebApplicationContext方法栗恩,進行初始化透乾,然后會調用到AbstractApplicationContext的refresh()方法,這個也是Spring的核心類磕秤。refresh()方法中會調用obtainFreshBeanFactory()來完成IOC容器的注冊和依賴注入乳乌。在obtainFreshBeanFactory()方法中會調用子類的refreshBeanFactory()方法,進行creatBeanFactory()市咆、loadBeanDefinitions()汉操、doLoadBeanDefinition()、registerBeanDefinition()蒙兰、doRegisterBeanDefinitions()最終調用的是DefaultListableBeanFactory類的registerBeanDefinition()方法來完成Bean的注冊磷瘤。
DI
BeanWrapper( Bean的包裝,包含Bean的屬性搜变、方法采缚,數據 )
核心流程大致分為兩個階段:實例化-》依賴注入
DI操作由BeanFactory的getBean()方法開始,實際調用的是AbstructBeanFactoty()的getBean()方法挠他,在這里會直接進行doGetBean()的操作扳抽。在doGetBean()方法中需要調用一個getSingleton()方法,該方法參數需要一個函數接口殖侵,最終調用的是函數接口實現的creatBean方法AbstractAutowireCapableBeanFactory類(多例的會直接調用creatBean方法),然后會調用doCreateBean()方法贸呢,在這會實例化BeanWrapper對象,這里邊有幾個核心方法createBeanInstance()創(chuàng)建bean實例拢军,addSingletonFactory向容器中緩存單例模式的Bean對象楞陷,以防循環(huán)依賴,populateBean并將Bean實例對象封裝朴沿,并且執(zhí)行DI操作猜谚,填充Bean屬性。initializeBean執(zhí)行一些BeanPostProcessor赌渣,Before,After處理魏铅、AOP,以及InitializingBean的afterPropertiesSet方法坚芜。
循環(huán)依賴
思考:什么是值傳遞览芳?什么是引用傳遞?
Spring中解決循環(huán)依賴其實是利用了引用傳遞的特性鸿竖,允許對象初始化未填充屬性階段先讓其他依賴對象完成引用沧竟,后續(xù)再賦值铸敏。
問題:
哪些情況可以解決?哪些不可以解決悟泵?
- 構造函數循環(huán)依賴杈笔。(NO)
- 多例Prototype Field Setter 類型循環(huán)依賴。(NO)
- 單例Singleton Field Setter 類型循環(huán)依賴糕非。(Yes)
Spring 如何解決循環(huán)依賴
Spring在解決循環(huán)依賴問題時蒙具,主要通過三級緩存,利用引用傳遞的特性朽肥。在依賴注入之前禁筏,將A未完成初始化的bean放入第三級緩存中,key<BeanName,ObjectFactory<?>>(ObjectFactory為函數式接口實現衡招,調用getEarlyBeanReference方法),執(zhí)行DI操作時需要完成B對象實例化篱昔,B對象執(zhí)行DI操作依賴A時,可以直接從三級緩存中獲取到未完成的A對象完成B對象的實例化過程始腾,由于引用傳遞州刽,后續(xù)A對象在實例化完成后,B對象中的A對象也完成實例操作窘茁。
假如A依賴B怀伦,B依賴A,此時先初始化A:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
//真正實現向IOC容器獲取Bean的功能山林,也是觸發(fā)依賴注入功能的地方
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//如果指定的是別名房待,將別名轉換為規(guī)范的Bean名稱
final String beanName = transformedBeanName(name);
Object bean;
// 先從緩存中取是否已經有被創(chuàng)建過的單態(tài)類型的Bean
// 對于單例模式的Bean整個IOC容器中只創(chuàng)建一次,不需要重復創(chuàng)建
// 此時A 明顯獲取不到A 對象
// 當此時為實例化B,為B實例注入A對象調用doGetBean(A)方法情況時驼抹,則可以拿到A對象桑孩,然后返回,具體看最下方代碼
Object sharedInstance = getSingleton(beanName);
//IOC容器創(chuàng)建單例模式Bean實例對象
if (sharedInstance != null && args == null) {
// 省略...
}
else {
// 省略 ...
try {
if (mbd.isSingleton()) {
//這里使用了一個匿名內部類框冀,創(chuàng)建Bean實例對象流椒,并且注冊給所依賴的對象
sharedInstance = getSingleton(beanName, () -> {
try {
//創(chuàng)建一個指定Bean實例對象,如果有父級繼承明也,則合并子類和父類的定義
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
//顯式地從容器單例模式Bean緩存中清除實例對象
destroySingleton(beanName);
throw ex;
}
});
//獲取給定Bean的實例對象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
}
...
return (T) bean;
}
}
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// ...
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// ...
beforeSingletonCreation(beanName);
// ...
try {
// 調用函數式接口getObject()實現方法宣虾,等價于 createBean(beanName, mbd, args);
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
}
finally {
// 執(zhí)行后續(xù)方法
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
}
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
//真正創(chuàng)建Bean的方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
//封裝被創(chuàng)建的Bean對象
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 創(chuàng)建實例(此時未賦值屬性)
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 獲得過早曝光對象bean。
final Object bean = instanceWrapper.getWrappedInstance();
// ...
//向容器中緩存單例模式的Bean對象温数,以防循環(huán)引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//向第三級緩存添加 對象實例
//同時生成 函數接口實現绣硝,一并添加到singletonObjects中,后續(xù)會調用撑刺。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
// Assert.notNull(singletonFactory, "Singleton factory must not be null");
// synchronized (this.singletonObjects) {
// if (!this.singletonObjects.containsKey(beanName)) {
// this.singletonFactories.put(beanName, singletonFactory);
// this.earlySingletonObjects.remove(beanName);
// this.registeredSingletons.add(beanName);
// }
// }
//}
}
// Initialize the bean instance.
//Bean對象的初始化鹉胖,依賴注入在此觸發(fā)
//這個exposedObject在初始化完成之后返回作為依賴注入完成后的Bean
Object exposedObject = bean;
try {
//將Bean實例對象封裝,并且Bean定義中配置的屬性值賦值給實例對象
// 此時發(fā)現A依賴于B,回去執(zhí)行B的getBean(),然后同A初始化.
// 在B實例化到此處時,發(fā)現B引用了A實例,那么就會嘗試獲取A實例對象(調用getBean(A)方法)甫菠。
populateBean(beanName, mbd, instanceWrapper);
//初始化Bean對象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
//獲取指定名稱的已注冊的單例模式Bean對象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
//根據名稱獲取的已注冊的Bean和正在實例化的Bean是同一個
if (exposedObject == bean) {
//當前實例化的Bean初始化完成
exposedObject = earlySingletonReference;
}
//當前Bean依賴其他Bean挠铲,并且當發(fā)生循環(huán)引用時不允許新創(chuàng)建實例對象
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
}
}
}
// Register bean as disposable.
//注冊完成依賴注入的Bean
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
}
//DefaultSingletonBeanRegistry
//Spring利用三級緩存來解決循環(huán)依賴,singletonObjects(1),earlySingletonObjects(2),singletonFactories(3),
// singletonsCurrentlyInCreation 這個緩存也非常重要,在bean開始創(chuàng)建時存入,創(chuàng)建完成后移除,只存儲正在創(chuàng)建中的bean。
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 用于存儲完全實例化好的bean 對象寂诱。
// 首先嘗試從實例化好緩存中 獲取對象 此時(循環(huán)依賴情況)肯定獲取不到拂苹。
Object singletonObject = this.singletonObjects.get(beanName);
// 追加判斷 該對象正在創(chuàng)建中(是的)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 未創(chuàng)建完成,提前曝光的bean對象,用于解決循環(huán)依賴
// 嘗試從提前曝光的Bean中獲取對象,對于 初始化A(A依賴B,B依賴A) 顯然獲取不到A對象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 嘗試再從三級緩存單例工廠中獲取刹衫,此時A(A依賴B,B依賴A)很明顯也獲取不到醋寝。 返回null.
// 此時為初始化A對象(在A對象初始化中,發(fā)現依賴B带迟,初始化B,發(fā)現B依賴A囱桨,然后嘗試獲取A對象情況
// 能夠從singletonFactory中拿到A對象仓犬。
// addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 這里 getObject()方法,實際調用getEarlyBeanReference(beanName, mbd, bean)
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
//AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
Aop
Aop核心
- 切面 Aspect舍肠,面向規(guī)則搀继,具有相同規(guī)則的<font color="red">方法</font> (methonCache)集合。
- 通知 Advice翠语,調用(回調)操作。
- 切點 PointCurd,需要代理的具體方法崇猫。
- 目標對象 Target泛范,需要代理的對象()。
- 代理 Proxy谍夭,JDK 黑滴,CGLIB。
- 前置通知紧索、后置通知袁辈、異常通知。
概述
簡單來說珠漂,AOP就是通過一定的規(guī)則晚缩,來當做切面。在Spring 實例化DI階段媳危,基于規(guī)則判斷是否滿足荞彼,滿足則使用代理來代替目標對象。在使用代理對象济舆,調用方法時卿泽,會從methodCache中獲取后續(xù)執(zhí)行鏈(chain),如果存在,則表示改方法有AOP代理需求签夭,執(zhí)行后續(xù)鏈操作齐邦。如果沒有直接利用反射調用目標類方法。
在chain不為null時第租,Spring 使用責任鏈模式措拇,來執(zhí)行操作(有一定的執(zhí)行順序)。
// 緩存切點 攔截器
Map<MethodCacheKey, List<Object>> methodCache;
注意
AOP是基于一定的規(guī)則來匹配方法(通過攔截器實現)慎宾,是方法層面的代理和織入丐吓。但是由于代理針對的是類,所以在DI操作進行規(guī)則驗證時趟据,只需要驗證類的層面滿足就可以券犁。在生成代理對象后調用方法時,才會對方法層面進行驗證(chain)汹碱,通過反射來實現方法的調用粘衬。
AOP采用策略模式,使用JDK(有實現接口時)咳促,和CGLIB兩種方式來實現代理稚新。
-
AOP操作的開始點是在DI執(zhí)行之后。
populateBean(beanName, mbd, instanceWrapper); //初始化Bean對象 // 包含AOP 初始化跪腹,AOP屬于后置處理褂删。 實現了 BeanPostProcessor接口。 exposedObject = initializeBean(beanName, exposedObject, mbd); //AOP 代理類 public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { }
MVC
Spring擴展點
InitializingBean 類
// 在完成bean 的注入(populateBean())之后 調用
// 針對單個bean使用冲茸。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
調用流程見BeanPostProcessor 類屯阀。
BeanPostProcessor 類
public interface BeanPostProcessor {
//在Bean 完成定義BeanDefinition、完成依賴注入getBean()->populateBean(),調用無參構造函數完成實例化前執(zhí)行
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//在Bean 完成定義BeanDefinition噪裕、完成依賴注入getBean(),調用無參構造函數完成實例化后執(zhí)行蹲盘。
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 所有的類都會觸發(fā)
}
調用詳解
//AbstractAutowireCapableBeanFactory
// getBean() 流程
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ... 省略 無關代碼
// Instantiate the bean.
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 這里處理 特殊的 MergedBeanDefinitionPostProcessor
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 這里完成 bean 初始化DI 工作
populateBean(beanName, mbd, instanceWrapper);
// 這里 initializeBean 方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
// ... 省略無關代碼
return exposedObject;
}
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//下邊 看看這個方法干了點啥
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
// 判斷是否 是InitializingBean 的接口,如果實現了InitializingBean膳音,則先執(zhí)行 afterPropertiesSet 方法召衔。
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
//不知道這個initMethodName是什么東西。反正if判斷不會過
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
BeanFactoryPostProcessor 類
@FunctionalInterface
public interface BeanFactoryPostProcessor {
// 所有類都會觸發(fā)
/**
* Modify(修飾 改變) the application context's internal(內部的) bean factory after its standard(使標準化) initialization.
* 在應用程序上下文標準工廠類 定義后祭陷,修飾(改變)工廠內部的bean對象 初始化過程苍凛。
* All bean definitions will have been loaded, but no beans will have been instantiated yet.
(調用時機)在所有的bean定義都被加載,但沒有實例化之前兵志。
* This allows for overriding or adding
* properties even to eager-initializing beans.
這允許重寫或添加屬性甚至可以初始化bean醇蝴。
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
調用時機
//
registerBeanPostProcessors
//PostProcessorRegistrationDelegate
final class PostProcessorRegistrationDelegate {
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
//beanFactory基本都實現了 BeanDefinitionRegistry 接口
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 先執(zhí)行 BeanDefinitionRegistryPostProcessor。其實是BeanFactoryPostProcessor的子接口之一
// public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
// 其實是BeanFactoryPostProcessor的子接口之一
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 其實是BeanFactoryPostProcessor的子接口之一
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// 這里才是真正獲取實現類的地方想罕。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
// 判斷是否有執(zhí)行過
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
// 是否實現 PriorityOrdered 接口悠栓,實現了優(yōu)先執(zhí)行
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
//是否實現 Ordered 接口霉涨,實現了優(yōu)先執(zhí)行
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// 開始真正執(zhí)行流程
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
}
// PostProcessorRegistrationDelegate
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
}
}
FactoryBean
MyBatis
執(zhí)行流程
- 解析配置,通過SqlSessionFactoryBuilder創(chuàng)建SqlSessionFactory惭适。
- 通過SqlSessionFactory獲取SqlSession,執(zhí)行操作前需要open笙瑟。
- 通過SqlSession獲取Mapper代理對象。
- 執(zhí)行操作調用excute執(zhí)行器癞志,執(zhí)行xml的sql往枷。
- 接收后需要關閉SqlSession。
public void testSelectList() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}
如何實現Mapper 調用xml方法凄杯?
SqlSessionFactory
SqlSessionFactory在創(chuàng)建過程中错洁,會做一系列的初始化操作,包括插件(分頁插件)的初始化戒突,實體對象和jdbc類型處理器的初始化屯碴,以及最重要的mapper初始化。
對于mapper來說妖谴,實際是由MapperRegistry類(Configuration)的knownMappers屬性來對每一個mapper接口(xml中的namespace)綁定一個MapperProxyFactory對象窿锉,作為后續(xù)sql操作的處理類。
MapperProxyFactory其實并沒有目標對象的概念膝舅,他僅僅只是提供一個入口,這里主要是為了將mapper接口中的方法匹配到xml的sql(statement)窑多,進而執(zhí)行仍稀。(所以無論是否實現mapper接口,均不會調用實現類9∠ⅰ)
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 用于解析 mybatis-config.xml技潘,同時創(chuàng)建了 Configuration 對象 >>
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析XML,最終返回一個 DefaultSqlSessionFactory >>
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
public class XMLConfigBuilder extends BaseBuilder {
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// XPathParser千康,dom 和 SAX 都有用到 >>
// 注意 這里直接獲取 mybatis配置文件 configuration 節(jié)點下內容
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 對于全局配置文件各種標簽的解析
propertiesElement(root.evalNode("properties"));
// 解析 settings 標簽
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 類型別名
typeAliasesElement(root.evalNode("typeAliases"));
// 插件
pluginElement(root.evalNode("plugins"));
// 用于創(chuàng)建對象
objectFactoryElement(root.evalNode("objectFactory"));
// 用于對對象進行加工
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 反射工具箱
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// settings 子標簽賦值享幽,默認值就是在這里提供的 >>
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 創(chuàng)建了數據源 >>
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析引用的Mapper映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 不同的定義方式的掃描,最終都是調用 addMapper()方法(添加到 MapperRegistry)拾弃。這個方法和 getMapper() 對應
// package 包
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// resource 相對路徑
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 解析 Mapper.xml值桩,總體上做了兩件事情 >>
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// url 絕對路徑
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// class 單個接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
}
public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
// 總體上做了兩件事情,對于語句的注冊和接口的注冊
if (!configuration.isResourceLoaded(resource)) {
// 1豪椿、具體增刪改查標簽的解析奔坟。
// 一個標簽一個MappedStatement。 >>
// 獲取mapperbiao標簽 準備注冊 接口 和語句
configurationElement(parser.evalNode("/mapper"));
// 加載 xml 的 <mapepr>節(jié)點資源
configuration.addLoadedResource(resource);
// 2搭盾、把namespace(接口)和工廠類綁定起來咳秉,放到一個map。
// 一個namespace 一個 MapperProxyFactory >>
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
private void bindMapperForNamespace() {
// 獲取nameSpace
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// 獲取接口類型
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
// 添加到 MapperRegistry鸯隅,本質是一個 map澜建,里面也有 Configuration >>
configuration.addMapper(boundType);
}
}
}
}
}
// mybatis 全局配置類
public class Configuration {
//Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
// 存儲NameSpace 和 mapper代理工廠對象的對照關系。
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
//MapperRegistry 類中方法
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// Map<Class<?>, MapperProxyFactory<?>> 存放的是接口類型,和對應的工廠類的關系
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// 注冊了接口之后炕舵,根據接口何之,開始解析所有方法上的注解,例如 @Select >>
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
OpenSession
openSession 實際返回defaultSqlSession對象幕侠。在這個過程中主要功能有:創(chuàng)建事務帝美、初始化執(zhí)行器、二級緩存晤硕、注冊Excute插件(裝飾器 + 責任鏈+jdk動態(tài)代理模式悼潭,一層一層的裝飾excute對象,并返回結果)舞箍。
public class DefaultSqlSessionFactory implements SqlSessionFactory {
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 獲取環(huán)境相關配置
final Environment environment = configuration.getEnvironment();
// 獲取事務工廠
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 創(chuàng)建事務 默認jdbc
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根據事務工廠和默認的執(zhí)行器類型舰褪,創(chuàng)建執(zhí)行器 >>
// 執(zhí)行器中 初始化 插件、二級緩存等
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
// 默認 SimpleExecutor
executor = new SimpleExecutor(this, transaction);
}
// 二級緩存開關疏橄,settings 中的 cacheEnabled 默認是 true
// mybatis 一級緩存是基于一次會話(openSession),
// 二級緩存 是基于一個nameSpace,所以每一個命名空間都需要一個CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 植入插件的邏輯占拍,至此,四大對象已經全部攔截完畢 裝飾器 + 責任鏈模式+jdk動態(tài)代理
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
//MyBatis 允許你在映射語句執(zhí)行過程中的某一點進行攔截調用捎迫。默認情況下晃酒,MyBatis 允許使用插件來攔截的方法調用包括:
// Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
// ParameterHandler (getParameterObject, setParameters)
// ResultSetHandler (handleResultSets, handleOutputParameters)
// StatementHandler (prepare, parameterize, batch, update, query)
}
getMapper
獲取代理對象,執(zhí)行后續(xù)操作窄绒。最終返回代理對象為 MapperProxy贝次。
public class DefaultSqlSession implements SqlSession {
public <T> T getMapper(Class<T> type) {
// 這里configuration對象是在 創(chuàng)建DefaultSqlSession時傳入的,全局對象引用彰导。
// mapper的代理對象是存放在 MapperRegistry 類 中 knownMappers屬性中
// Configuration中包含MapperRegistry的引用(MapperRegistry也包含Configuration引用)
// 次數實際調用的 是 mapperRefistry 中的方法蛔翅。(符合DDD領域驅動設計規(guī)范)
return configuration.getMapper(type, this);
}
}
//MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
// mapper 接口 動態(tài)代理實現類。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 1:類加載器:2:被代理類實現的接口位谋、3:實現了 InvocationHandler 的觸發(fā)管理類
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 初始化 目標代理類
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
Sql執(zhí)行 mapper.Method()
由于此時mapper已經是MapperProxy代理對象山析,所以在執(zhí)行方法時會調用MapperProxy的invoke方法
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// toString hashCode equals getClass等方法,無需走到執(zhí)行SQL的流程
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 提升獲取 mapperMethod 的效率掏父,到 MapperMethodInvoker(內部接口) 的 invoke
// 普通方法會走到 PlainMethodInvoker(本類的 內部類) 的 invoke
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// Java8 中 Map 的方法笋轨,根據 key 獲取值,如果值是 null损同,則把后面Object 的值賦給 key
// 如果獲取不到翩腐,就創(chuàng)建
// 獲取的是 MapperMethodInvoker(接口) 對象,只有一個invoke方法
return methodCache.computeIfAbsent(method, m -> {
// 判斷 執(zhí)行 method是否是默認方法膏燃。
// 一般都不是(沒見過需要寫default方法的mapper接口情況)茂卦。
if (m.isDefault()) {
// 接口的默認方法(Java8),只要實現接口都會繼承接口的默認方法组哩,例如 List.sort()
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 創(chuàng)建了一個 MapperMethod
// configuration對象 被傳來傳去
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// SQL執(zhí)行的真正起點
return mapperMethod.execute(sqlSession, args);
}
}
}
核心點
緩存
mybatis中分為一級緩存和二級緩存等龙,一級緩存默認開啟(基于一次會話)处渣。二級緩存需要手動開啟,在nameSpace中<setting>標簽設置蛛砰,cacheEnabled=true罐栈,二級緩存基于一個命名空間。一般新增泥畅,修改荠诬,刪除操作會更新緩存。為了防止遞歸查詢重復處理緩存位仁,還使用了queryStack來統(tǒng)計執(zhí)行次數柑贞。二級緩存模式使用LRU最少使用策略來做內存淘汰。
Spring+Mybatis
Spring集成Mybatis后聂抢,如何完成mapper代理對象的創(chuàng)建和注入钧嘶?
Spring集成Mybatis后,sqlSessionFactory怎么創(chuàng)建琳疏,怎么獲得sqlSession有决,怎么實現打開關閉?
Spring集成Mybatis是通過mybatis-spring.jar包來實現的空盼,基于一些Spring提供的擴展點(FactoryBean,InitializingBean)书幕,mybatis完成了與Spring的集成。
Spring集成Mybatis依賴三個核心配置揽趾。
<!-- Spring bean初始化時會去加載 SqlSessionFactoryBean類實現sqlSessionFactory創(chuàng)建 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.gupaoedu.crud.dao"/>
</bean>
<!--配置一個可以執(zhí)行批量的sqlSession按咒,全局唯一,單例 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"></constructor-arg>
<constructor-arg value="BATCH"></constructor-arg>
</bean>
- SqlSessionFactoryBean主要為了創(chuàng)建SqlSessionFactory但骨。在創(chuàng)建過程中,最終調用mybatis的創(chuàng)建過程智袭,做一系列的初始化操作奔缠,包括插件(分頁插件)的初始化,實體對象和jdbc類型處理器的初始化吼野,以及最重要的mapper初始化(將mapper與MapperProxy代理關聯起來校哎,getMapper時拿到代理對象)。
- MapperScannerConfigurer主要是為了掃描xml文件瞳步,通過掃描xml文件獲得dao接口闷哆,并將dao接口注冊到IOC容器中,同時替換IOC容器BeanDefinition目標類单起,統(tǒng)一指向MapperFactoryBean(實現了FactoryBean接口)抱怔。也就是說在DI操作getBean,Dao層實例時,實際調用MapperFactoryBean的getObject方法嘀倒。這個方法最終返回的是MapperProxy代理對象屈留。
- SqlSessionTemplate主要為了解決DefaultSqlSession非線程安全的問題局冰。我們在使用DefaultSqlSession時都需要open或者close掉sqlSession。為了在Spring中方便的解決這個問題灌危,提供了SqlSession的另外一個實現類SqlSessionTemplate(線程安全的)康二。線程安全的原因就是因為使用了代理模式,在創(chuàng)建SqlSessionTemplate時實際返回的是SqlSessionInterceptor代理類勇蝙,這里的invoke方法中會open/close操作sqlSession沫勿。
SqlSessionFactory創(chuàng)建
我們在使用Spring集成Mybatis時,會在Spring的配置文件中使用如下配置:
// 實現了 InitializingBean Spring在完成 Bean 配置后調用 afterPropertiesSet
// 實現了 FactoryBean 在創(chuàng)建bean實例
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
final Configuration targetConfiguration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
//默認走這里 先初始化Configuration
targetConfiguration = new Configuration();
}
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
if (hasLength(this.typeAliasesPackage)) {
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
.filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
.filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
}
if (!isEmpty(this.typeAliases)) {
Stream.of(this.typeAliases).forEach(typeAlias -> {
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
});
}
if (!isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach(plugin -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
});
}
if (hasLength(this.typeHandlersPackage)) {
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
}
if (!isEmpty(this.typeHandlers)) {
Stream.of(this.typeHandlers).forEach(typeHandler -> {
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
});
}
if (!isEmpty(this.scriptingLanguageDrivers)) {
Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
targetConfiguration.getLanguageRegistry().register(languageDriver);
LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
});
}
Optional.ofNullable(this.defaultScriptingLanguageDriver)
.ifPresent(targetConfiguration::setDefaultScriptingLanguage);
if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
try {
targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
//配置文件中 通過mapperLocation配置 xml文件位置
if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
// 此處進行 掃描加載
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
// 調用mybatis的方法
xmlMapperBuilder.parse();
} catch (Exception e) {
}
}
}
} else {
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
}
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
//XMLMapperBuilder類味混,上邊xmlMapperBuilder.parse()产雹。在configutation中 初始化MapperProxyFactory
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
}
Mapper代理注入
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
// ClassPathBeanDefinitionScanner
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// 只做兩件事情,第一掃描全部 mapper.xml
// 替換mapper目標對象
// 注意 首先調用的是子類的doScan方法惜傲。
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
//ClassPathMapperScanner
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 再調用父類 ClassPathBeanDefinitionScanner doScan方法 掃描接口注冊IOC
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
// 修改beanDefinitions 的定義
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
// 將實體類 使用mapperFactoryBeanClass類替換洽故。
// Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
// 調用 sqlSessionTemplete getMapper方法
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
}
}
//SqlSessionDaoSupport
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
// SqlSessionTemplate
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// open Session
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
// close Session
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
SqlSessionTemplate
public class SqlSessionTemplate implements SqlSession, DisposableBean {
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
}
Spring Boot
簡介
- Spring的升級版。功能基于Spring盗誊。比如:注解时甚,事件,定時器等
- 約定優(yōu)于配置的體現產物哈踱』氖剩基于約定的規(guī)則簡化了一些常用配置。
- 引入Web-Stater默認內置tomcat啟動开镣,創(chuàng)建MVC刀诬,提供配置文件。
- 自動注入規(guī)則邪财。
- Starter啟動
- 去XML配置(Spring 提供)
- @Component/@Controller/@Service陕壹。(省略<bean>)
- @Configuration/@Component-Scan/@Import。(去XML核心)
- @Enable驅動树埠。(重點)
- @Conditional條件注解糠馆。(重點 Spring 4.X)
- EnableAutoConfiguration自動裝配。
特性:Stater怎憋、自動裝配又碌、SpringBoot-Cli、Actuator
Enable驅動
自動完成相關組件的Bean配置绊袋。
核心是通過@Import或者其他方式向IOC容器注入Bean毕匀。
是Spring去XML配置,約定優(yōu)于配置的集中體現癌别,通過配置Enable皂岔,使得開發(fā)者不用關注大量的配置內容。
如@EnableScheduling
正常加載:
public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
private static final String ASYNC_EXECUTION_ASPECT_CLASS_NAME = "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect";
public AnnotationDrivenBeanDefinitionParser() {
}
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
// .... 省略無關代碼
if (registry.containsBeanDefinition(
"org.springframework.context.annotation.internalScheduledAnnotationProcessor"
)) {
parserContext.getReaderContext().error(
"Only one ScheduledAnnotationBeanPostProcessor may exist within the context.",
source);
} else {
builder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor"
);
builder.getRawBeanDefinition().setSource(source);
scheduler = element.getAttribute("scheduler");
if (StringUtils.hasText(scheduler)) {
builder.addPropertyReference("scheduler", scheduler);
}
registerPostProcessor(
parserContext, builder,
"org.springframework.context.annotation.internalScheduledAnnotationProcessor"
);
}
parserContext.popAndRegisterContainingComponent();
return null;
}
}
@Enable驅動:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class) // Improt導入類
@Documented
public @interface EnableScheduling {
}
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
自動裝配
在Spring Boot 項目中存在一系列的Stater組件规个,通過在pom.xml中引入就可以直接通過@Autowired注入使用jar包中的類凤薛。比如RedisTemplete姓建。這種形式叫做自動裝配。
實現自動裝配需要考慮幾個問題缤苫。
-
如果需要直接注入速兔,那么前提是IOC容器中要有該實例對象。那么IOC容器是怎么做到實例化的活玲?
這里可以做一個猜想涣狗,想要將類交給IOC容器那么基于正常的方式來說需要這樣做
@Configuration public class AutoConfiguration{ @Bean public RedisTemplete redistemplete(){ return new RedisTemplete(); } .... }
如何告訴Spring需要加載哪些配置類?
動態(tài)裝載
Spring 提供了兩種方式來實現動態(tài)配置的裝載舒憾。
- Selector: ImportSelector镀钓、 DeferredImportSelector
- Registory:ImportBeanDefinitionRegistrar
我們通過實現ImportSelector接口selectImports()方法來返回要加載的配置類,這樣可以實現配置類或者Bean對象的動態(tài)裝配镀迂。
通過動態(tài)裝載我們可以將配置類交給Spring去掃描丁溅,完成依賴注入。
那么Spring如何知道我的配置類到底在哪里探遵?—》約定優(yōu)于配置
Spring定義規(guī)則:將需要掃描的配置類通過配置文件的形式(spring.factories)放在META-INF目錄下窟赏。
Spring-Boot通過@EnableAutoConfiguration注解來進行掃描加載。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //導入掃描類
public @interface EnableAutoConfiguration {
...
}
------------------------------------------
public class AutoConfigurationImportSelector implements DeferredImportSelector, ... {
....
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
...
//掃描配置
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
----------------------------------------
//最終調用 SpringFactoriesLoader類的loadSpringFactories方法
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
SpringFactoriesLoader其實類似SPI(擴展點)
SPI機制
Service provider interface
需要滿足以下條件
- 創(chuàng)建目錄META-INF/services
- 在目錄下創(chuàng)建擴展點的全路徑名
- 文件中要寫擴展點箱季,類的實現
- 文件編碼UTF-8
- ServiceLoader去加載
條件控制
@Condition一系列注解涯穷。
官方的Stater(Spring-boot-stater-XXX)不需要額外的spring.factories文件,在spring核心包中統(tǒng)一進行配置藏雏。所以官方的配置類主要是通過@Condition注解來進行條件加載的拷况。
手寫Stater組件
pom.xml引入Sping-boot jar包(需要用到注解@Configuration/@Condition等注解)同時設置<option>屬性。
-
引入外部化配置掘殴。
-
創(chuàng)建配置對象使用@ConfigurationProperties注解赚瘦,同時定義配置前綴。
@ConfigurationProperties(prefix = "fish.bone") public class propertiesConfig(){ private Sting url = "test URL"; ... }
-
外部化配置提示奏寨。
- 導入jar包(spring-boot-configuration-processor)
-
META-INF目錄下創(chuàng)建additional-spring-configuration-metadata.json文件
{ "properties": [ { "name": "server.compression.enabled", //配置名稱 spring.application.port "description": "Whether response compression is enabled.", // 配置描述 啟動端口 "defaultValue": false //設置默認值 }, { "name": "server.compression.excluded-user-agents", "description": "Comma-separated list of user agents for which responses should not be compressed." } ] }
-
-
編寫配置類
- 配置類加入@EnableConfigurationProperties注解加載外部化配置對象蚤告。
-
創(chuàng)建spring.factories文件
Condition 除了注解外 還可以在 MATA-INF目錄下創(chuàng)建 spring-autoconfigure-metadata.properties 來定義注解條件。
MySQL
執(zhí)行流程
sql->緩存(8.0停用)->解析器->預處理器->優(yōu)化器->執(zhí)行計劃->執(zhí)行器
索引機制
Innodb 使用B+Tree作為數據結構服爷。
聚集索引
聚集索引是指數據庫表行中數據的物理順序與鍵值的邏輯(索引)順序相同,有且僅有一個获诈,因為一個表的物理順序只有一種仍源。自增主鍵索引。
為什么推薦使用自曾主鍵作為索引舔涎,就是因為他的順序與表的物理順序一致笼踩。當我這個索引為聚集索引時,對表的新增修改刪除影響會降到最小亡嫌,拿新增來舉例他肯定是在末尾新增嚎于。而非自增主鍵來說我就是一個隨機的接口掘而,當插入到中間位置時,有可能會造成整個索引結構的重新排列于购。
覆蓋索引
覆蓋索引袍睡,表示查詢的數據剛好是索引字段中包含。比如A+B聯合索引肋僧,在查詢select a,b斑胜、a、b 時均能遇到覆蓋索引嫌吠。
索引下推
對于存儲引擎來說止潘,存儲引擎只能通過索引來進行數據過濾。當聯合索引為AB辫诅, where a = '' and b='%as' 此時b用不到索引凭戴,正常來說存儲引擎會只判斷a條件,然后返回server進行過濾炕矮。但是實際情況是會直接過濾ab兩個條件狮杨,這種情況就是索引下推。
回表
非主鍵索引查詢狮暑,且沒有用到覆蓋索引的情況都會產生回表庭呜。
數據庫事務
事務的四大特性
原子性 Atomicity (undolog)
隔離性 Isolation (加鎖、快照 LBCC+MVCC)
持久性 Durability (Redolog+雙寫緩沖)
一致性 Consistency
undolog(回滾日志钢颂,事務回滾),Redolog(執(zhí)行日志+雙寫緩沖钞它,崩潰恢復)。
增刪改操作會自動開始事務殊鞭。手動開始事務:begin; start trancation;事務開始后斷開連接會自動回滾事務遭垛。
一個事務持有的鎖會在事務結束后釋放
事務并發(fā)的三大問題(讀一致性問題)
臟讀()
事務開啟,但未提交的數據操灿,叫做臟數據锯仪。
一個事務讀取到了其他事務未提交的數據,造成前后兩次讀不一致情況趾盐。
不可重復讀(undate/delete)
一個事務讀取到其他事務已提交的數據庶喜,造成在一個事務里邊兩次查詢到不一致的情況。
幻讀(insert)
一個事務讀取到其他事務已提交插入的數據救鲤,造成的前后兩次讀不一致久窟。(只有插入已提交的情況才叫做幻讀)
事務隔離級別
Read Uncommitted(RU未提交讀) -- 沒有解決任何問題
一個事務能夠讀取到其他事務沒有提交的數據,會出現臟讀本缠。
Read Committed(RC已提交讀) -- 解決臟讀問題
一個事務能夠讀取到另外一個事務已提交的數據斥扛。沒有解決可重復讀問題。
Repeatable Read(RR可重復讀) -- 解決不可重復讀問題
一個事務中多次讀取同樣的數據結果是一樣的丹锹,未解決幻讀問題稀颁。
Serializable(串行化)
能夠解決所有問題
如何解決事務并發(fā)的讀一致性問題
- 在讀取數據前加鎖芬失,防止其他事務對其修改。(性能太差匾灶,讀操作都要加鎖棱烂,太粗暴 LBCC Lock Based Concurrency Control )
- 生成一個時間點的快照,并為這個快照來提供一定級別的一致性讀取粘昨。(MVCC Multi Version Concurrency Control)多版本并發(fā)控制垢啼。
MVCC核心思想
- 一個事務能夠看到的數據版本。
- 第一次查詢前已經提交的事務的修改张肾。
- 本事務的修改芭析。
- 一個事務不能看到的數據版本。
- 在本事務第一次查詢之后創(chuàng)建的事務(事務ID比我的事務ID大)
- 未提交的事務的修改吞瞪。
MVCC實現原理
DB_TRX_ID: 插入或更新的最后一個事務的ID馁启,事務編號是自動遞增的。
DB_ROLL_PTR:回滾指針芍秆。(刪除版本)
數據庫鎖
表鎖與行鎖
鎖定粒度惯疙、加鎖效率、沖突概率妖啥、并發(fā)性能霉颠。
加鎖方式
lock tables xxx read , lock tables xxx write ,unlock tables
共享鎖(行鎖)
Shared Lock
共享鎖又稱為讀鎖,簡稱S鎖荆虱。顧名思義S鎖就是多個事務對同一條數據可以共享一把鎖蒿偎,都能訪問,但是不能修改怀读。
加鎖方式
select * from table where id = 1 lock in share mode ; commit / rollback;
排它鎖(行鎖)
Exclusive lock
排它鎖又稱謝所诉位,簡稱X鎖。排它鎖不能與其他鎖并存菜枷,如果一個事務獲取了一行數據的排它鎖苍糠,那么其他事務將不能再獲取該行的鎖,只有獲取了排它鎖的事務可以對這一行數據進行讀取和修改啤誊。
加鎖方式
自動:delete / update / insert table 都會默認加鎖X鎖岳瞭。
手動: select * from table where id = 1 for update ; commit / rollback;
意向共享鎖(Intention Shared/Exclusive Lock )
意向鎖分為意向共享鎖,意向排它鎖蚊锹。因為為表加鎖的前提是表中任意一行都沒有被鎖住寝优,這也就意味著加表鎖之前需要先判斷是否有其他行鎖存在。為了提高加表鎖的效率枫耳,使用了意向鎖,在加共享鎖和排它鎖的時候需要先為表加上一個意向共享孟抗、排他鎖迁杨。這樣只需要判斷表有沒有意向鎖就可以快速的鎖表钻心。
意向鎖由存儲引擎自己維護。
鎖的粒度铅协。
記錄鎖
鎖的粒度捷沸,行鎖鎖住了一條記錄(索引)。
對于要鎖住的行在表中存在的情況狐史,就是記錄鎖痒给。比如數據庫有id為1的一行數據。加鎖通過 select * from table where id = 1 for update ;此時的鎖就是記錄鎖骏全。
間隙鎖
[圖片上傳中...(Lock-2.png-79d15f-1620027539643-0)]
- 間隙鎖與間隙鎖不沖突苍柏。(在不新增的情況下,間隙鎖都能夠加鎖成功姜贡。但是如果要新增則不能成功试吁。)
- 間隙鎖只阻塞插入。
- Gap Lock只在RR中存在楼咳。
鄰間鎖
由此可見查詢大于5小于9的情況下熄捍,系統(tǒng)鎖住了大于4小于等于10的范圍,這樣就解決了幻讀的問題母怜。所以InnoDB在RR狀態(tài)就做到了對于幻讀的解決余耽。