Spring用了挺久了,但是Spring源碼一直不得其精髓,后來(lái)在B站上找視頻,看到個(gè)手寫一個(gè)簡(jiǎn)易的Spring,以此加深對(duì)Spring的理解。
Bean的生命周期
一個(gè)Spring容器的開(kāi)始是從創(chuàng)建ApplicationContext開(kāi)始的,然后通過(guò)ApplicationContext.getBean("beanName")創(chuàng)建一個(gè)實(shí)例Bean。
ApplicationContext applicationContext = new ApplicationContext(AppConfig.class);
System.out.println(applicationContext.getBean("testService"));
所以需要在ApplicationContext的構(gòu)造方法里面實(shí)現(xiàn)以下幾個(gè)點(diǎn):
- 從AppConfig類中獲取ComponentScan注解的參數(shù),這個(gè)參數(shù)就是表示我要到哪些包下面找類;
- 根據(jù)1步驟找到的絕對(duì)路徑,掃路徑下的所有文件,包含Component注解的類全部撈出來(lái)解析;
- 如果Component有參數(shù),就以參數(shù)為Bean名稱,沒(méi)有就小寫類名首字母。解析類的特征存儲(chǔ)進(jìn)BeanDefinition對(duì)象封裝起來(lái),并且存放到BeanDefinitionMap中,方便后續(xù)處理和創(chuàng)建Bean;
- 把BeanDefinitionMap里的所有標(biāo)注為單例的BeanDefinition撈出來(lái)先創(chuàng)建好單例Bean,創(chuàng)建好單例Bean后存儲(chǔ)在單例池里面,這里的單例池用的還是ConcurrentHashMap
下面就是簡(jiǎn)易的ApplicationContext實(shí)現(xiàn):
public ApplicationContext(Class className) {
this.className = className;
// 我們拿到配置類的名稱之后,就要去配置類里面拿信息,第一個(gè)要拿的,就是配置類可以掃哪些包,即獲取ComponentScan的信息
if (className.isAnnotationPresent(ComponentScan.class)) {
// 判斷一下有沒(méi)有ComponentScan的注解,有就拿出來(lái)
ComponentScan componentScan = (ComponentScan) className.getAnnotation(ComponentScan.class);
// 拿出配置的參數(shù)值,就是com.my.code.service這個(gè)值
String path = componentScan.value();
path = path.replace(".", "/");
// 根據(jù)相對(duì)路徑找絕對(duì)路徑
ClassLoader classLoader = ApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if (file.isDirectory()) {
// 判斷一下是不是文件夾
File[] files = file.listFiles();
for (File f : files) {
String fileName = f.getAbsolutePath();
if (fileName.endsWith(".class")) {
// 判斷這個(gè)類是不是有Component注解,用反射;先將絕對(duì)路徑換成相對(duì)路徑
String classPathName = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
classPathName = classPathName.replace("\\", ".");
try {
Class<?> clazz = classLoader.loadClass(classPathName);
// 獲取到類之后,判斷是否有Component注解
if (clazz.isAnnotationPresent(Component.class)) {
Component component = clazz.getAnnotation(Component.class);
String beanName = component.value();
if (beanName.equals("")) {
// 如果不寫Component的參數(shù),默認(rèn)是類名的首字母小寫
beanName = Introspector.decapitalize(clazz.getSimpleName());
}
// 有這個(gè)注解,我們不直接創(chuàng)建Bean,而是先創(chuàng)建BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition();
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
beanDefinition.setScope("singleton");
}
beanDefinition.setType(clazz);
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
System.err.println("反射獲取.class文件出錯(cuò)");
e.printStackTrace();
}
}
}
}
}
// 開(kāi)始創(chuàng)建單例對(duì)象
for (String beanName : beanDefinitionMap.keySet()) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")) {
Object bean = createBean(beanName, beanDefinition);
singletonObjectsMap.put(beanName, bean);
}
}
}
可以直接根據(jù)全限定類名從類加載器里面獲取類,然后用isAnnotationPresent方法來(lái)判斷是否有某個(gè)注解
以上就是完成了包掃描,加載好了Bean的相關(guān)信息,單例Bean也已經(jīng)創(chuàng)建好了。只需要調(diào)用getBean就能夠獲取了。這里只考慮是否是單例Bean而討論。實(shí)現(xiàn)思路還是很直接,拿BeanDefinitionMap里的信息,判斷是單例還是多例,單例就從單例池里取,沒(méi)有重新創(chuàng)建,有就拿出返回;多例就直接創(chuàng)建一個(gè)返回回去。這里的創(chuàng)建Bean有點(diǎn)復(fù)雜,所以單獨(dú)拎出去寫一個(gè)新方法。getBean實(shí)現(xiàn)如下:
public Object getBean(String beanName) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
throw new NullPointerException();
} else {
String scope = beanDefinition.getScope();
if (scope.equals("singleton")) {
Object bean = singletonObjectsMap.get(beanName);
if (bean == null) {
bean = createBean(beanName, beanDefinition);
assert bean != null;
singletonObjectsMap.put(beanName, bean);
}
return bean;
} else {
return createBean(beanName, beanDefinition);
}
}
}
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class<?> clazz = beanDefinition.getType();
try {
Object instance = clazz.getConstructor().newInstance();
// 對(duì)instance對(duì)象里面的屬性自動(dòng)配置,找有Autowired注解的屬性
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.isAnnotationPresent(Autowired.class)) {
declaredField.setAccessible(true);
declaredField.set(instance, getBean(declaredField.getName()));
}
}
return instance;
} catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
這是創(chuàng)建Bean的實(shí)現(xiàn),里面涉及到Autowired的自動(dòng)注入注解,還有一個(gè)循環(huán)依賴的解決。
AOP的實(shí)現(xiàn):先創(chuàng)建一個(gè)BeanPostProcessor,里面搞個(gè)后置處理器,在后置處理器里面創(chuàng)建一個(gè)代理類,替換原先的bean并且返回。這里JDK代理只能代理接口。
@Override
public Object postProcessAfterInitialization(String beanName, Object bean) {
// AOP的核心,就是在后置處理器里面創(chuàng)建了一個(gè)新的代理對(duì)象,這個(gè)代理對(duì)象不是原來(lái)的bean,而是在原有bean上加工過(guò)的對(duì)象
Object proxyInstance = Proxy.newProxyInstance(TestBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("加入操作XXXX");
return method.invoke(bean, args);
}
});
return proxyInstance;
}
AOP的本質(zhì)就是在創(chuàng)建這個(gè)Bean的時(shí)候,在后置處理器里面,創(chuàng)建一個(gè)同接口的實(shí)現(xiàn)類,這個(gè)實(shí)現(xiàn)類有自己加的新功能,也有原先的方法,所有的方法都被植入新操作,并以代理類代替了原先的實(shí)現(xiàn)類。
綜上我們可以總結(jié)如下的Bean的生命周期:
ApplicationContext的構(gòu)造方法里面,開(kāi)始掃@Component注解的類。

Spring事務(wù)底層原理
事務(wù)開(kāi)啟會(huì)做兩件事,一件是Spring自己建立一個(gè)JDBC連接,然后就是關(guān)閉自動(dòng)提交。如果將提交和連接交給jdbcTemplate,那么就會(huì)出現(xiàn)jdbctemplate執(zhí)行一個(gè)sql就提交一個(gè),遇到異常拋出時(shí),sql都已經(jīng)提交了,回滾不回來(lái)了。所以需要spring自己管理一個(gè)連接。
spring事務(wù)失效的原理
@Transactional
public void test(){
jdbcTemplate.execute("insert into t1 values(1,1,1)");
a();
}
@Transactional
public void a(){
jdbcTemplate.execute("insert into t1 values(2,2,2)");
}
當(dāng)調(diào)用Spring里的Bean(testService)的test()方法時(shí),test()的@Transactional是會(huì)生效的,但是test()里面的a()方法的事務(wù)注解是不會(huì)生效的。
原因:在產(chǎn)生代理對(duì)象替代原普通對(duì)象時(shí),普通對(duì)象里面的自動(dòng)注入的屬性在代理對(duì)象里面是沒(méi)有用的,也就是自動(dòng)注入注不到代理對(duì)象里面,CGlib的解決方法是在代理對(duì)象里面加一個(gè)同類型的target對(duì)象,這個(gè)target就是上面提到的普通對(duì)象,也可以認(rèn)為是被代理的對(duì)象。這個(gè)時(shí)候再執(zhí)行普通對(duì)象里面的test()方法和a()方法。于是問(wèn)題就出現(xiàn)了:test()方法的事務(wù)注解是被解析過(guò)的并且在代理對(duì)象里是有事務(wù)的操作,所以不會(huì)失效,a()方法就不會(huì)了,他沒(méi)有被事務(wù)解析過(guò),調(diào)用的時(shí)候是調(diào)的target這個(gè)普通對(duì)象,你寫的@Transactional他理都不理,等于沒(méi)寫。解決方法就是在這個(gè)類里面再自動(dòng)注入自己,spring會(huì)幫忙解決循環(huán)依賴的問(wèn)題。
三級(jí)緩存
一級(jí)緩存:?jiǎn)卫?,存放已?jīng)完成初始化后的完整Bean;
二級(jí)緩存:因?yàn)槌霈F(xiàn)循環(huán)依賴,而不得不提前進(jìn)行AOP產(chǎn)生代理對(duì)象,存放在二級(jí)緩存中;二級(jí)緩存是為了保證這個(gè)代理對(duì)象單例唯一;
三級(jí)緩存:如果二級(jí)緩存中沒(méi)有找到提前出現(xiàn)的代理對(duì)象,就會(huì)將執(zhí)行反射創(chuàng)建代理對(duì)象的操作封裝進(jìn)函數(shù)式接口中,存放在三級(jí)緩存里面;