第一托启,要形成的習(xí)慣:
1.有空時(shí)隔一段時(shí)間要做幾道算法題宅倒,C語言和JAVA都可以,主要是訓(xùn)練思維屯耸。
2.定期閱讀spring的源碼拐迁。因?yàn)閟pring是框架蹭劈,重設(shè)計(jì),能夠培養(yǎng)大局觀
3.閱讀底層的書籍唠亚,如linux方面链方,虛擬機(jī)方面,這是內(nèi)功灶搜。越高級(jí)的語言只是招式。
4.不要忘記做了一半的東西工窍,如搜索引擎方面割卖,redis方面,可以過一段時(shí)間再做患雏,因?yàn)榈綍r(shí)候自己的境界有提升鹏溯,深入程度也會(huì)有所增加。
第二淹仑,進(jìn)入正題丙挽,了解原理,再看源碼匀借。
1.看源碼看熟了颜阐,以后再遇到問題,就可以通過源碼去了解原理了吓肋。
2.spring的AOP凳怨,原理懂了,代碼相當(dāng)簡(jiǎn)單是鬼。這也是為什么我記得我還是個(gè)菜鳥的時(shí)候肤舞,面試人家經(jīng)常問我這個(gè)。
3.先有個(gè)大局觀均蜜,畫張整體的spring結(jié)構(gòu)圖李剖。
? ? 以下是備受吐槽的手繪時(shí)間:
(如果你覺得我左手字寫的實(shí)在是不能再難看了的話,我有空可以展示一下右手字囤耳。)
AOP面向切面編程是面向?qū)ο蟮难a(bǔ)充篙顺。它利用一種橫切技術(shù),將一些公共行為封裝成叫做“方面”的可重用模塊紫皇,解耦慰安,增加可維護(hù)性。
AOP將系統(tǒng)分為核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)兩部分聪铺。核心關(guān)注點(diǎn)就是主業(yè)務(wù)流程化焕,橫切關(guān)注點(diǎn)就是上面提到的“方面”。那么看AOP的源碼就是要看橫切關(guān)注點(diǎn)是怎樣和核心關(guān)注點(diǎn)整合來發(fā)揮作用的铃剔。
主業(yè)務(wù)流程歸根到底是一個(gè)java方法撒桨,而且是對(duì)象的方法查刻。
在AOP中被稱為被通知或被代理對(duì)象POJO。AOP的作用就是將核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)組合起來凤类,術(shù)語叫做“增強(qiáng)”穗泵。最后實(shí)際用的是增強(qiáng)后的代理對(duì)象。
對(duì)核心關(guān)注點(diǎn)進(jìn)行增強(qiáng)就涉及到在哪些地方增強(qiáng)的問題谜疤。如方法調(diào)用或者異常拋出時(shí)做增強(qiáng)這些時(shí)機(jī)叫做連接點(diǎn)Joinpoint佃延。一個(gè)通知將被引發(fā)的連接點(diǎn)集合叫做切入點(diǎn),理解時(shí)就可以想正則表達(dá)式夷磕,通配符來指定多個(gè)履肃,而不是單單一個(gè)連接點(diǎn)。
在連接點(diǎn)都做了哪些增強(qiáng)呢坐桩?增強(qiáng)的內(nèi)容AOP術(shù)語叫“通知”Advice尺棋。
Spring里定義了四種Advice:BeforeAdvice,AfterAdvice绵跷,ThrowAdvice膘螟,DynamicIntroducationAdvice。
許多AOP框架包括spring都是以攔截器作為通知模型碾局。維護(hù)一個(gè)圍繞連接點(diǎn)的攔截器鏈荆残。其中DynamicIntroducationAdvice是可以引入方法或者字段到核心關(guān)注點(diǎn)。
這里有個(gè)Introduction擦俐,AOP術(shù)語叫引入脊阴。將增強(qiáng)后的AOP代理組裝到系統(tǒng)叫做織入。
上面就是AOP的核心概念了蚯瞧『倨冢總結(jié)一下:
AOP要做的事情就是:生成代理對(duì)象,然后織入埋合。
生成代理對(duì)象是經(jīng)常會(huì)被問到的一個(gè)問題:Spring提供了兩種方式來生成代理對(duì)象备徐,JDKProxy和Cglib。
具體使用哪種方式由AopProxyFactory根據(jù)AdvisedSupport對(duì)象的配置來決定甚颂。
默認(rèn)的策略是如果目標(biāo)類是接口蜜猾,則使用JDK動(dòng)態(tài)代理技術(shù),否則使用Cglib來生成代理振诬。
Cglib是基于字節(jié)碼技術(shù)的蹭睡,使用的是ASM。asm是一個(gè)java字節(jié)碼操縱框架赶么,它能被用來動(dòng)態(tài)生成類或者增強(qiáng)既有類的功能肩豁。
ASM可以直接產(chǎn)生二進(jìn)制class文件,也可以在類被加載入JVM之前動(dòng)態(tài)改變類行為。
下面重點(diǎn)來看看JDK動(dòng)態(tài)代理技術(shù)清钥。這是我還是個(gè)很菜很菜的菜鳥時(shí)為數(shù)不多能看懂的源碼琼锋。因?yàn)橹翱催^Java設(shè)計(jì)模式,寫過類似的例子祟昭,所以會(huì)比較順暢缕坎。今天先講這一部分。
下面是調(diào)用測(cè)試類:
package?dynamic.proxy;?
import?java.lang.reflect.InvocationHandler;
import?java.lang.reflect.Method;
import?java.lang.reflect.Proxy;
/**
?*?實(shí)現(xiàn)自己的InvocationHandler
?*?@author?zyb
?*?@since?2012-8-9
?*
?*/
public?class?MyInvocationHandler?implements?InvocationHandler?{
?//?目標(biāo)對(duì)象?
?private?Object?target;
?/**
?*?構(gòu)造方法
?*?@param?target?目標(biāo)對(duì)象?
?*/
?public?MyInvocationHandler(Object?target)?{
?super();
?this.target?=?target;
?}
?/**
?*?執(zhí)行目標(biāo)對(duì)象的方法
?*/
?public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
?//?在目標(biāo)對(duì)象的方法執(zhí)行之前簡(jiǎn)單的打印一下
?System.out.println("------------------before------------------");
?//?執(zhí)行目標(biāo)對(duì)象的方法
?Object?result?=?method.invoke(target,?args);
?//?在目標(biāo)對(duì)象的方法執(zhí)行之后簡(jiǎn)單的打印一下
?System.out.println("-------------------after------------------");
?return?result;
?}
?/**
?*?獲取目標(biāo)對(duì)象的代理對(duì)象
?*?@return?代理對(duì)象
?*/
?public?Object?getProxy()?{
?return?Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),?
?target.getClass().getInterfaces(),?this);
?}
}
package?dynamic.proxy;
/**
?*?目標(biāo)對(duì)象實(shí)現(xiàn)的接口篡悟,用JDK來生成代理對(duì)象一定要實(shí)現(xiàn)一個(gè)接口
?*?@author?zyb
?*?@since?2012-8-9
?*
?*/
public?interface?UserService?{
?/**
?*?目標(biāo)方法?
?*/
?public?abstract?void?add();
}
package?dynamic.proxy;?
/**
?*?目標(biāo)對(duì)象
?*?@author?zyb
?*?@since?2012-8-9
?*
?*/
public?class?UserServiceImpl?implements?UserService?{
?/*?(non-Javadoc)
?*?@see?dynamic.proxy.UserService#add()
?*/
?public?void?add()?{
?System.out.println("--------------------add---------------");
?}
}
package?dynamic.proxy;?
import?org.junit.Test;
/**
?*?動(dòng)態(tài)代理測(cè)試類
?*?@author?zyb
?*?@since?2012-8-9
?*
?*/
public?class?ProxyTest?{
?@Test
?public?void?testProxy()?throws?Throwable?{
?//?實(shí)例化目標(biāo)對(duì)象
?UserService?userService?=?new?UserServiceImpl();
?//?實(shí)例化InvocationHandler
?MyInvocationHandler?invocationHandler?=?new?MyInvocationHandler(userService);
?//?根據(jù)目標(biāo)對(duì)象生成代理對(duì)象
?UserService?proxy?=?(UserService)?invocationHandler.getProxy();
?//?調(diào)用代理對(duì)象的方法
?proxy.add();
?}
}
執(zhí)行結(jié)果如下:
------------------before---------------
--------------------add---------------
-------------------after-----------------
很簡(jiǎn)單谜叹,核心就是 invocationHandler.getProxy();這個(gè)方法調(diào)用的Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(), this); 怎么生成對(duì)象的。
/**
?*?Returns?an?instance?of?a?proxy?class?for?the?specified?interfaces
?*?that?dispatches?method?invocations?to?the?specified?invocation
?*?handler.
?*
?*?
{@code?Proxy.newProxyInstance}?throws
?*?{@code?IllegalArgumentException}?for?the?same?reasons?that
?*?{@code?Proxy.getProxyClass}?does.
?*
?*?@param?loader?the?class?loader?to?define?the?proxy?class
?*?@param?interfaces?the?list?of?interfaces?for?the?proxy?class
?*?to?implement
?*?@param?h?the?invocation?handler?to?dispatch?method?invocations?to
?*?@return?a?proxy?instance?with?the?specified?invocation?handler?of?a
?*?proxy?class?that?is?defined?by?the?specified?class?loader
?*?and?that?implements?the?specified?interfaces
?*?@throws?IllegalArgumentException?if?any?of?the?restrictions?on?the
?*?parameters?that?may?be?passed?to?{@code?getProxyClass}
?*?are?violated
?*?@throws?SecurityException?if?a?security?manager,?s,?is?present
?*?and?any?of?the?following?conditions?is?met:
?*?
?*?
?the?given?{@code?loader}?is?{@code?null}?and
?*?the?caller's?class?loader?is?not?{@code?null}?and?the
?*?invocation?of?{@link?SecurityManager#checkPermission
?*?s.checkPermission}?with
?*?{@code?RuntimePermission("getClassLoader")}?permission
?*?denies?access;
?*?
?for?each?proxy?interface,?{@code?intf},
?*?the?caller's?class?loader?is?not?the?same?as?or?an
?*?ancestor?of?the?class?loader?for?{@code?intf}?and
?*?invocation?of?{@link?SecurityManager#checkPackageAccess
?*?s.checkPackageAccess()}?denies?access?to?{@code?intf};
?*?
?any?of?the?given?proxy?interfaces?is?non-public?and?the
?*?caller?class?is?not?in?the?same?{@linkplain?Package?runtime?package}
?*?as?the?non-public?interface?and?the?invocation?of
?*?{@link?SecurityManager#checkPermission?s.checkPermission}?with
?*?{@code?ReflectPermission("newProxyInPackage.{package?name}")}
?*?permission?denies?access.
?*?
?*?@throws?NullPointerException?if?the?{@code?interfaces}?array
?*?argument?or?any?of?its?elements?are?{@code?null},?or
?*?if?the?invocation?handler,?{@code?h},?is
?*?{@code?null}
?*/
?@CallerSensitive
?public?static?Object?newProxyInstance(ClassLoader?loader,
?Class[]?interfaces,
?InvocationHandler?h)
?throws?IllegalArgumentException
?{
?Objects.requireNonNull(h);
?final?Class[]?intfs?=?interfaces.clone();
?final?SecurityManager?sm?=?System.getSecurityManager();
?if?(sm?!=?null)?{
?checkProxyAccess(Reflection.getCallerClass(),?loader,?intfs);
?}
?/*
?*?Look?up?or?generate?the?designated?proxy?class.
?*/
?Class?cl?=?getProxyClass0(loader,?intfs);
?/*
?*?Invoke?its?constructor?with?the?designated?invocation?handler.
?*/
?try?{
?if?(sm?!=?null)?{
?checkNewProxyPermission(Reflection.getCallerClass(),?cl);
?}
?final?Constructor?cons?=?cl.getConstructor(constructorParams);
?final?InvocationHandler?ih?=?h;
?if?(!Modifier.isPublic(cl.getModifiers()))?{
?AccessController.doPrivileged(new?PrivilegedAction()?{
?public?Void?run()?{
?cons.setAccessible(true);
?return?null;
?}
?});
?}
?return?cons.newInstance(new?Object[]{h});
?}?catch?(IllegalAccessException|InstantiationException?e)?{
?throw?new?InternalError(e.toString(),?e);
?}?catch?(InvocationTargetException?e)?{
?Throwable?t?=?e.getCause();
?if?(t?instanceof?RuntimeException)?{
?throw?(RuntimeException)?t;
?}?else?{
?throw?new?InternalError(t.toString(),?t);
?}
?}?catch?(NoSuchMethodException?e)?{
?throw?new?InternalError(e.toString(),?e);
?}
?}
這個(gè)代碼是JDK1.8中的恰力,里面用到了1.8的一些語法叉谜,如果不太了解,建議先看看這本書踩萎。
代碼看著不少,實(shí)際上都在進(jìn)行一些安全校驗(yàn)很钓,包裝之類的香府,真正有用的就兩句:
Class cl = getProxyClass0(loader, intfs);這句話查找或者生成代理類。跟進(jìn)去:
/**
?*?Generate?a?proxy?class.?Must?call?the?checkProxyAccess?method
?*?to?perform?permission?checks?before?calling?this.
?*/
?private?static?Class?getProxyClass0(ClassLoader?loader,
?Class...?interfaces)?{
?if?(interfaces.length?>?65535)?{
?throw?new?IllegalArgumentException("interface?limit?exceeded");
?}
?//?If?the?proxy?class?defined?by?the?given?loader?implementing
?//?the?given?interfaces?exists,?this?will?simply?return?the?cached?copy;
?//?otherwise,?it?will?create?the?proxy?class?via?the?ProxyClassFactory
?return?proxyClassCache.get(loader,?interfaces);
?}
對(duì)码倦,就是從緩存里把接口拿將出來企孩。然后用return cons.newInstance(new Object[]{h}) 這一句將接口用invocationHandler進(jìn)行包裝。
具體源碼可以跟進(jìn)去看袁稽,不詳述勿璃。想必看到這里,JDK動(dòng)態(tài)代理的原理都已經(jīng)很明白了推汽。
這里要說一點(diǎn)理論性的東西:
AOP解決的問題往往可以用代理模式來解決补疑。Java開發(fā)中常說動(dòng)態(tài)代理和靜態(tài)代理,而AOP就是動(dòng)態(tài)代理歹撒,因?yàn)榇淼念愂窃谶\(yùn)行時(shí)才生成的莲组。
而一般說的代理模式寫成的代碼是編譯期就已經(jīng)生成的,叫靜態(tài)代理暖夭。
想繼續(xù)了解JAVA知識(shí)的锹杈,記得關(guān)注+關(guān)注+關(guān)注!