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類