這篇文章在前一段時間看到一位大佬寫的,由於寫的邏輯清晰、通俗易懂,當時我就整理了一下筆記,但願往後本身也行寫出這樣的文章。若是想對spring瞭解和入門的同窗,相信認真閱讀了本篇文章定會收穫頗豐。java
特此說明:此篇文章主要目的是當作範文,以此鼓勵本身在不斷學習總結中創做出更多更好的優質原創文章;找了半天沒有找到原文章,若有打擾即刻刪除。程序員
默認題主說的Spring是Spring framework,而不是Spring家族spring
若是你從未獨立看過源碼(和我同樣),那麼你極可能至今都未曾注意某兩個概念。編程
你覺得我會說IOC和AOP?No。app
看到這裏,一部分讀者內心一驚:臥槽,說的啥玩意,Spring不就IOC和AOP嗎?!這兩個都不說,你這篇文章爲啥能寫這麼長?框架
不錯,我就是這麼長。其實我要講的是:模塊化
大部分人一聽到「請你談談對Spring的理解」,就會下意識搬出IOC和AOP兩座大山,趕忙糊弄過去。大概是這樣的:post
IOC學習
所謂的控制反轉。通俗地講,就是把本來須要程序員本身建立和維護的一大堆bean通通交由Spring管理。測試
所謂的面向切面編程。通俗地講,它通常被用來解決一些系統交叉業務的織入,好比日誌啦、事務啥的。打個比方,UserService的method1可能要打印日誌,BrandService的method2可能也須要。亦即:一個交叉業務就是要切入系統的一個方面。具體用代碼展現就是:
交叉業務的編程問題即爲面向切面編程。AOP的目標就是使交叉業務模塊化。作法是將切面代碼移動到原始方法的周圍:
而所謂的模塊化,我我的的理解是將切面代碼作成一個可管理的狀態。好比日誌打印,再也不是直接硬編碼在方法中的零散語句,而是作成一個切面類,經過通知方法去執行切面代碼。
我相信大部分培訓班出來的朋友也就言盡於此,講完上面內容就準備收拾打卡下班了。
怎麼說呢,IOC按上面的解釋,雖然很淺,但也馬馬虎虎吧。然而AOP,不少人對它的認識是很是片面的...
這樣吧,我問你一個問題,如今我本身寫了一個UserController,以及UserServiceImpl implements UserService,而且在UserController中注入Service層對象:
@Autowired
private UserService userService;
複製代碼
若是你聽不懂我要問什麼,說明你對Spring的AOP理解仍是太少了。
實際上,Spring依賴注入的對象並不必定是咱們本身寫的類的實例,也多是userServiceImpl的代理對象。下面分別演示這兩種狀況:
由於第二次我給UserServiceImpl加了@Transactional 註解。
看到這裏,我彷彿聽到有一部分兄弟默默說了句:臥槽...
可是,上面對IOC和AOP的理解,也僅僅是應用級別,是一個面。僅僅瞭解到這個程度,對Spring的瞭解仍是很是扁平的,不夠立體。
上帝說,要有光。因而特斯拉搞出了交流電。
Java說,萬物皆對象。可是Spring另外搞了BeanDefinition...
什麼BeanDefinition呢?其實它是bean定義的一個頂級接口:
哎呀臥槽,啥玩意啊。描述一個bean實例?我咋想起了Class類呢。
其實,二者並無矛盾。
大部分初學者覺得Spring解析或者@Bean後,就直接搞了一個bean存到一個大Map中,其實並非。
就比如什麼呢?你從海里吊了一條魚,可是你還沒想好清蒸仍是紅燒,那就乾脆先曬成魚乾吧。一條鹹魚,其實蘊藏着無線可能,由於它可能會翻身!
接下來,咱們討論一下鹹魚如何翻身。
最典型的例子就是AOP。上面AOP的例子中我說過了,若是不加@Transactional,那麼Controller層注入的就是普通的userServiceImpl,而加了之後返回的實際是代理對象。
爲何要返回代理對象?由於咱們壓根就沒在UserServiceImpl中寫任何commit或者rollback等事務相關的代碼,可是此時此刻代理對象卻能完成事務操做。毫無疑問,這個代理對象已經被Spring加了佐料。
那麼Spring是什麼時候何地加佐料的呢?說來話長。
大部分人把Spring比做容器,其實潛意識裏是將Spring徹底等同於一個Map了。其實,真正存單例對象的map,只是Spring中很小很小的一部分,僅僅是BeanFactory的一個字段,我更習慣稱它爲「單例池」。
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
複製代碼
因此,不少人把Spring理解成一個大Map,仍是太淺了。就拿ApplicationContext來說,它也實現了BeanFactory接口,可是做爲容器,其實它是用來包含各類各樣的組件的,而不是存bean:
後置處理器其實能夠分好多種,屬於Spring的擴展點之一。
@Autowired
ApplicationContext annotationConfigApplicationContext;
複製代碼
除了利用Spring自己的IOC容器自動注入之外,你還有別的辦法嗎?
咱們可讓Bean實現ApplicationContextAware接口:
Spring官方文檔:通常來講,您應該避免使用它,由於它將代碼耦合到Spring中,而且不遵循控制反轉樣式
這是我認爲Spring最牛逼的地方:代碼具備高度的可擴展性,甚至你本身都懵逼,爲何實現了一個接口,這個方法就被莫名其妙調用,還傳進了一個對象...
這其實就是後置處理器的工做!
什麼意思呢?
就是說啊,明面上咱們看得見的地方只要實現一個接口,可是背地裏Spring在本身框架的某一處搞了個for循環,遍歷全部的BeanPostProcessor,其中就包括處理實現了ApplicationContextAware接口的bean的後置處理器:ApplicationContextAwareProcessor。
上面這句話有點繞,你們停下來多想幾遍。
if (bean instanceof Aware) {
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
}
}
複製代碼
因此,此時此刻一個類實現ApplicationContextAware接口,有兩層含義:
大體瞭解Spring Bean的建立流程後,接下來咱們嘗試着用BeanPostProcessor返回當前Bean的代理對象。
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
</dependencies>
複製代碼
AppConfig
@Configuration//JavaConfig方式,即當前配置類至關於一個applicationConotext.xml文件
@ComponentScan//默認掃描當前配置類(AppConfig)所在包及其子包
public class AppConfig {
}
複製代碼
Calculator
public interface Calculator {
public void add(int a, int b);
}
複製代碼
CalCulatorImpl
@Component
public class CalculatorImpl implements Calculator {
public void add(int a, int b) {
System.out.println(a+b);
}
}
複製代碼
後置處理器MyAspectJAutoProxyCreator
使用步驟:
@Component
public class MyAspectJAutoProxyCreator implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
final Object obj = bean;
//若是當前通過BeanPostProcessors的Bean是Calculator類型,咱們就返回它的代理對象
if (bean instanceof Calculator) {
Object proxyObj = Proxy.newProxyInstance(
this.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy,Method method, Object[] args) throws Throwable {
System.out.println("開始計算....");
Object result = method.invoke(obj, args);
System.out.println("結束計算...");
return result;
}
}
);
return proxyObj;
}
//不然返回自己
return obj;
}
}
複製代碼
測試類
public class TestPostProcessor {
public static void main(String[] args) {
System.out.println("容器啓動成功!");
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
//打印當前容器全部BeanDefinition
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
System.out.println("============");
//取出Calculator類型的實例,調用add方法
Calculator calculator = (Calculator)applicationContext.getBean(Calculator.class);
calculator.add(1, 2);
}
複製代碼
先把MyAspectJAutoProxyCreator的@Component註釋掉,此時Spring中沒有咱們自定義的後置處理器,那麼返回的就是CalculatorImpl: