Spring學(xué)習(xí)筆記(三)-AOP技術(shù)(模擬實現(xiàn)利用java中的Proxy和CGlib)

1.利用JDK自帶的Proxy和CGlib代理類模擬實現(xiàn)AOP功能

1.實現(xiàn)的業(yè)務(wù)是:

1).攔截所有的業(yè)務(wù)方法

2).判斷用戶是否有權(quán)限逛尚,有權(quán)限就允許它執(zhí)行業(yè)務(wù)方法,沒有權(quán)限就不允許它執(zhí)行業(yè)務(wù)方法(是否有權(quán)限根據(jù)user是否為null來判斷)

上面的這個就是AOP中所說的“橫切性關(guān)注點”懊昨。

使用JDK中提供的Proxy(代理類)類實現(xiàn)上面一段話所講的攔截炊林。

編寫代理類,用的是JDK中提供的Proxy破衔,Proxy是面向接口的守问,如果想要創(chuàng)建代理的實例沒有接口匀归,那么不能用Proxy來創(chuàng)建代理。

2.java代碼的實現(xiàn)

1).service接口:PersonService.java

2).serviceImpl:PersonServiceImpl.java

3).編寫代理類耗帕,模擬AOP:JdkProxyFactory.java

package?com.gaoyuan.aop;


import?java.lang.reflect.InvocationHandler;

import?java.lang.reflect.Method;

import?java.lang.reflect.Proxy;


import?com.gaoyuan.service.impl.PersonServiceImpl;


/**

?* 編寫代理類,用的是JDK中提供的Proxy袱贮,Proxy是面向接口的仿便,如果想要創(chuàng)建代理的實例沒有接口,那么不能用Proxy來創(chuàng)建代理攒巍。

?* @author?G_yuan

?*

?*/

public?class?JdkProxyFactory implements?InvocationHandler {

private?Object targetObject;

/**

?* 創(chuàng)建代理對象

?* @param?targetObject

?* @return

?*/

public?Object createProxyIntance(Object targetObject){

this.targetObject?= targetObject;

//創(chuàng)建一個代理類

Object object?= Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),

this.targetObject.getClass().getInterfaces(),this);

return?object;

}

/**

?* 創(chuàng)建的代理對象實現(xiàn)了InvocationHandler嗽仪,所以有個回調(diào)對象,意思就是誰調(diào)用的代理對象柒莉,那么回調(diào)對象就是誰闻坚,

?* 要通過反射對這個回調(diào)對象中的方法進行一定業(yè)務(wù)的處理。

?*/

@Override

public?Object invoke(Object proxy, Method method, Object[] args)

throws?Throwable {

PersonServiceImpl bean?= (PersonServiceImpl) this.targetObject;

Object result?= null;

if(bean.getUser() !=null){

result?= method.invoke(targetObject, args); //此處是通過反射兢孝,動態(tài)的調(diào)用傳入對象(targetObject)的方法窿凤,result的值為,對象方法的值返回是什么跨蟹,result的值就是什么

}

return?result;

}

}

4).測試代碼

如果想要創(chuàng)建代理的類沒有接口的話雳殊,那么用CGlib產(chǎn)生代理對象。(目標(biāo)對象沒有接口)

編碼實現(xiàn):

Service和serviceImpl的代碼上面一樣窗轩,唯一不一樣的就是編寫的代理類

CGlibProxyFactory.java

package?com.gaoyuan.aop;


import?java.lang.reflect.Method;


import?com.gaoyuan.service.impl.PersonServiceImpl;


import?net.sf.cglib.proxy.Enhancer;

import?net.sf.cglib.proxy.MethodInterceptor;

import?net.sf.cglib.proxy.MethodProxy;

/**

?* 如果想要創(chuàng)建代理的類沒有接口的話夯秃,那么用CGlib產(chǎn)生代理對象。(目標(biāo)對象沒有接口)

?* @author?G_yuan

?*

?*/

public?class?CGlibProxyFactory?implements?MethodInterceptor {

private?Object targetObject;

public?Object createProxyIntance(Object targetObject){

this.targetObject?= targetObject;

Enhancer enhancer?= new?Enhancer();//使用Enhancer類來創(chuàng)建代理類痢艺。

//Enhancer類實現(xiàn)代理的方式是:Enhancer繼承了目標(biāo)類(this.targetObject.getClass())仓洼,然后覆蓋了目標(biāo)類中的

//所有非final的方法,然后在覆蓋的方法中添加了自己的一些代碼堤舒。

enhancer.setSuperclass(this.targetObject.getClass()); //設(shè)置目標(biāo)類

enhancer.setCallback(this);

return?enhancer.create();

}

/**

?* Object object:代理對象本身

?* Method method:被攔截的方法,在應(yīng)用中調(diào)用那個方法色建,那就攔截那個方法,具體指看下面的注解

?* Object[] arg2:方法的參數(shù)

???MethodProxy methodProxy:方法代理對象

?*/

public?Object intercept(Object object, Method method, Object[] args,

MethodProxy methodProxy) throws?Throwable {

PersonServiceImpl bean?= (PersonServiceImpl) this.targetObject;

Object result?= null;

if(bean.getUser() !=null){

result?= methodProxy.invoke(targetObject, args); //此處是通過反射植酥,動態(tài)的調(diào)用傳入對象(targetObject)的方法镀岛,result的值為弦牡,對象方法的值返回是什么,result的值就是什么

}

MethodProxy methodProxy1?= methodProxy;//methodProxy net.sf.cglib.proxy.MethodProxy@7e41986c

Method me?= method; //me = public java.lang.String com.gaoyuan.service.impl.PersonServiceImpl.getPersonName(java.lang.Integer)

return?result;

}

}

3.AOP中通知的概念

橫切性關(guān)注點:我們要對那些方法進行攔截漂羊,攔截之后我們要做些什么驾锰,這些思考的步驟都可以定義為橫切性關(guān)注點。

切面:思考完橫切性關(guān)注點之后走越,就要進行編碼進行實現(xiàn)椭豫,那么對應(yīng)上面寫的代理類,JdkProxyFactory或者CGlibProxyFactory類旨指,即可認為是切面赏酥。

連接點:對應(yīng)的就是,在tset類谆构,PersonService service?= (PersonService) factory.createProxyIntance(new?PersonServiceImpl("888"));

//service.save("333");

service.getPersonName(1);

創(chuàng)建的service對象調(diào)用那個方法裸扶,那個方法就是連接點。

切入點:比如上面要實現(xiàn)的業(yè)務(wù)是搬素,對所有的業(yè)務(wù)方法進行攔截呵晨,這就是一個切入點。

通知:

Target(目標(biāo)對象):JdkProxyFactory中Proxy.newProxyInstance創(chuàng)建的對象

織入:serviceImpl本身沒有對方法進行攔截的判斷熬尺,但是通過編寫了切面(代理類)摸屠,然后它具備了這項功能,這個過程的實現(xiàn)就是織入粱哼。

引入:動態(tài)的在代理對象上添加一些字段或者是方法季二。

4.spring中AOP注解開發(fā)

上面是通過沒有使用任何框架來實現(xiàn)的AOP,spring中集成上面的兩種方法實現(xiàn)AOP揭措,如果想要創(chuàng)建代理的類沒有接口時胯舷,spring就會自動使用CGlib來實現(xiàn)AOP,如果有接口時就使用JDK中的Proxy蜂筹。

當(dāng)進行切面開發(fā)時如果用JDK是1.7的版本時需纳,項目中引入的aspectjrt-1.7.4.jar和aspectjweaver-1.7.4.jar也應(yīng)該是1.7版本的。

切面代碼實現(xiàn)

1).PersonService.java

2).PersonServiceImpl.java

package?com.gaoyuan.service.impl;


import?org.springframework.stereotype.Service;


import?com.gaoyuan.service.PersonService;

@Service("personService")//記的寫

public?class?PersonServiceImpl?implements?PersonService ?{

private?String user?= null;

public?String getUser() {

return?user;

}

public?PersonServiceImpl() {

}

public?PersonServiceImpl(String user) {

this.user?= user;

}

@Override

public?String getPersonName(Integer id){

System.out.println("我是getPersonName方法");

return?"xxxx";

}

@Override

public?void?save(String name){

throw?new?RuntimeException();

//System.out.println("我是save方法");

}

@Override

public?void?update(String name?, Integer id?){

System.out.println("我是更新方法");

}

}

3).切面類:MyInterceptor.java


package?com.gaoyuan.service;


import?org.aspectj.lang.ProceedingJoinPoint;

import?org.aspectj.lang.annotation.After;

import?org.aspectj.lang.annotation.AfterReturning;

import?org.aspectj.lang.annotation.AfterThrowing;

import?org.aspectj.lang.annotation.Around;

import?org.aspectj.lang.annotation.Aspect;

import?org.aspectj.lang.annotation.Before;

import?org.aspectj.lang.annotation.Pointcut;

import?org.springframework.stereotype.Component;


@Aspect

@Component

public?class?MyInterceptor?{

/**

* 對"execution (* com.gaoyuan.service..*.*(..))"的解釋

(!void com.gaoyuan.service..*.*(..) 攔截返回值不是void的方法

(* com.gaoyuan.service..*.*(java.lang.String,..) 攔截方法?

???中的參數(shù)第一個是String類型艺挪,后面隨意參數(shù)的方法不翩。

?* execution:表示執(zhí)行

?* 第一個“ * ”:表示返回值類型,*代表所有的返回值類型麻裳,如果特殊需要寫具體的類型時口蝠,寫類型的全名意思就是加包名。

?* com.gaoyuan.service..:表示包名津坑,com.gaoyuan.service包下面的以及它的子包后面的兩個".."意思包含子包妙蔗,可以直接寫包的全名

?* 如果直接寫包的全名的話,那就是只是攔截此包下類的方法疆瑰。

?* 第二個“ * ”:表示:前面包的所有類眉反,也是可以寫全名的昙啄。寫全名代碼只攔截此包下此類的方法 。

?* 第二個*和第三個*之間的“.”是分割這兩個星號的寸五。

?* 第三個“*”:代表方法名 “*”代表所有的方法梳凛。

?* 第三個“*”后的(..):代表參數(shù)。

?*/

@Pointcut("execution (* com.gaoyuan.service..*.*(..))")

private?void?anyMethod(){} //聲明一個切入點

//如果想獲取調(diào)用方法的參數(shù)時梳杏,配置args(name) 括號中的name要和doAccessCheck(String name)中參數(shù)的名字一致韧拒。

@Before("anyMethod() && args(name)")

public?void?doAccessCheck(String name){

System.out.println("前置通知"+name);

}

//如果想獲取調(diào)用方法后的返回值十性,配置returning="result" 括號中的result要和doAfterReturning(String result)中參數(shù)的名字一致。

@AfterReturning(pointcut="anyMethod()",returning="result")

public?void?doAfterReturning(String result){

System.out.println("后置通知"+result);

}

@After("anyMethod()")

public?void?doAfter(){

System.out.println("最終通知");

}

//如果想獲取方法中拋出的異常用throwing="e"

@AfterThrowing(pointcut="anyMethod()",throwing="e")

public?void?doAfterThrowing(Exception e){

System.out.println("例外通知"+e);

}

@Around("anyMethod()")

public?Object doBasicProfiling(ProceedingJoinPoint pjp) throws?Throwable{

//如果定義了環(huán)繞通知后楷掉,必須調(diào)用proceed()方法,如果不調(diào)用的話霞势,后面調(diào)用業(yè)務(wù)層的方法是不會執(zhí)行的。所以支示,

//此處特別適合做權(quán)限判斷

//if(){Object result = pjp.proceed();}

System.out.println("進入方法");

Object result?= pjp.proceed();

System.out.println("退出方法 ");

return?result;

}

}

4.測試類:

5.基于XML配置方式聲明切面

對應(yīng)的java類

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鄙才,一起剝皮案震驚了整個濱河市颂鸿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嘴纺,老刑警劉巖浓冒,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稳懒,死亡現(xiàn)場離奇詭異,居然都是意外死亡场梆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門寞忿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顶岸,“玉大人,你說我怎么就攤上這事霹抛。” “怎么了恃逻?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵藕施,是天一觀的道長裳食。 經(jīng)常有香客問我,道長诲祸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任找田,我火速辦了婚禮着憨,結(jié)果婚禮上甲抖,老公的妹妹穿的比我還像新娘。我一直安慰自己准谚,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秀存,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惫恼。 梳的紋絲不亂的頭發(fā)上澳盐,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音腕窥,去河邊找鬼。 笑死癞松,一個胖子當(dāng)著我的面吹牛入蛆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播枫甲,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼想幻,長吁一口氣:“原來是場噩夢啊……” “哼话浇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起幔崖,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤岖瑰,失蹤者是張志新(化名)和其女友劉穎砂代,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體露戒,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡智什,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年丁屎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片证九。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖呀页,靈堂內(nèi)的尸體忽然破棺而出拥坛,到底是詐尸還是另有隱情,我是刑警寧澤丸氛,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布惨奕,位于F島的核電站梨撞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏卧波。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一螃成、第九天 我趴在偏房一處隱蔽的房頂上張望查坪。 院中可真熱鬧偿曙,春花似錦、人聲如沸望忆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歉备。三九已至,卻和暖如春落午,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背界拦。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工梗劫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梳侨,地道東北人丙躏。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像栅盲,于是被迫代替她去往敵國和親废恋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容