對AOP的簡單理解
AOP就是面向切面編程肥惭,這種方式讓我們有更多精力放在核心業(yè)務(wù)邏輯上,下面這個圖可以方便我們理解
APO1.png
左圖的驗證用戶功能就是每個操作中的橫切點而芥,整個系統(tǒng)中所有該功能的點連起來就成為了一個橫切面罕容,我們可以將這個功能做成一個切面類胳岂,當其他地方要使用的時候丸卷,直接將其置入里面就好了
這篇博客址講述AOP的實現(xiàn)方式枕稀,并不直接講述SpringAOP的使用方式,當然SpringAOP的實現(xiàn)也用到了后面的動態(tài)生成代理類原理谜嫉,如果只想看SpringAOP的簡單使用請看這篇SpringAOPdemo
實現(xiàn)AOP的兩種方式(靜態(tài)萎坷、動態(tài)代理)
靜態(tài)代理
我們首先看一下代理模式的結(jié)構(gòu)類圖。Proxy就是我們的代理類沐兰,持有一個被代理對象的引用哆档,和被代理對象共同實現(xiàn)了我們定義的業(yè)務(wù)接口,靜態(tài)代理就是通過代理模式來實現(xiàn)的
代理模式.png
只簡單的說一下實現(xiàn)
public interface IBankOperation(){ //銀行業(yè)務(wù)操作接口
public void drawMoney (int account); //取款
public int transferMoney(int account); //轉(zhuǎn)賬
}
public class BankOperationImp implement IBankOperation(){//具體實現(xiàn)
public void drawMoney(int account){
... //實現(xiàn)查找核心的業(yè)務(wù)邏輯
xxxDao.drawmoney(account); //從持久層查找數(shù)據(jù)
...
}
public int transferMoney(int account){
... //實現(xiàn)轉(zhuǎn)帳的核心業(yè)務(wù)邏輯
xxxDao.transferMoney(account); //持久層數(shù)據(jù)改變
}
}
下面我們需要在IBankOperation接口的所有方法操作前進行驗證操作住闯,變使用代理對象來完成驗證功能瓜浸,并持有一個操作實現(xiàn)類的引用,調(diào)用實現(xiàn)類的方法來代替執(zhí)行
public class BankOperationProxy implement IBankOperation(){ //銀行操作代理類
BankOperationImp operation; //持有一個被代理對象的引用
public void validate(){ //驗證方法
}
public void drawMoney(int account){
validate();
operation.drawMoney(account);
}
public int transferMoney(int account){
validate();
operation.drawMoney(account);
}
}
靜態(tài)代理存在明顯的弊端:
- 一個代理類只能為一個類服務(wù)比原,不同的類需要各自的代理類
- 被代理類進行了功能的擴展插佛,比如實現(xiàn)了另外一個接口,我們的代理類也需要實現(xiàn)對應(yīng)的接口量窘,并作實現(xiàn)
動態(tài)代理(JDKProxy&&CGLibProxy)
package cn.cherish.service;
public interface PersonService { //業(yè)務(wù)類接口
public void save(String name);
public void update(String name, Integer personid);
public String getPersonName(Integer personid);
}
package cn.cherish.service.impl;
import cn.cherish.service.PersonService;
public class PersonServiceBean implements PersonService{ //業(yè)務(wù)實現(xiàn)類
private String user = null;
public String getUser() {
return user;
}
public PersonServiceBean(){}
public PersonServiceBean(String user){
this.user = user;
}
public String getPersonName(Integer personid) {
System.out.println("調(diào)用了etPersonName()方法");
return "xxx";
}
public void save(String name) {
System.out.println("調(diào)用了save()方法");
}
public void update(String name, Integer personid) {
System.out.println("調(diào)用了update()方法");
}
}
JDKProxy
package cn.cherish.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.cherish.service.impl.PersonServiceBean;
public class JDKProxyFactory implements InvocationHandler{
private Object targetObject;
public Object createProxyIntance(Object targetObject){
this.targetObject = targetObject;
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
this.targetObject.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable { //環(huán)繞通知
PersonServiceBean bean = (PersonServiceBean) this.targetObject;
Object result = null;
if(bean.getUser()!=null){
//..... advice()-->前置通知
try {
result = method.invoke(targetObject, args);
// afteradvice() -->后置通知
} catch (RuntimeException e) {
//exceptionadvice()--> 異常通知
}finally{
//finallyadvice(); -->最終通知
}
}
return result;
}
}
簡單的分析一下上面這段代碼:
- 首先我們必須明確雇寇,要通過JDKProxy方式動態(tài)獲取到代理對象,必須實現(xiàn)InvocationHandler接口蚌铜,再通過反射包下的Proxy類動態(tài)獲取代理對象
-
invoke()
方法的三個參數(shù)锨侯,分別是代理對象,被代理對象被調(diào)用的方法冬殃,和被調(diào)用方法的參數(shù)集合 -
Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(), this);
這就是我們通過目標對象生成代理對象的代碼囚痴,三個類分別是被代理對象的類加載器,實現(xiàn)的接口审葬,以及之后代理對象需要調(diào)用的方法深滚,this表示被代理對象方法被調(diào)用時,調(diào)用當前類的invoke()
方法 - 在
invoke()
方法中判斷用戶不為空之后涣觉,method.invoke(targetObject,args)
表示調(diào)用targetObject的save()
方法這里的method是通過反射獲取到的當前被代理對象被調(diào)用的方法 - 最后注意痴荐,雖然
method.invoke(targetObject,args)
返回值為空,但是我們還是需要返回一下的
測試結(jié)果:
package junit.test;
import org.junit.BeforeClass;
import org.junit.Test;
import cn.cherish.aop.CGlibProxyFactory;
import cn.cherish.aop.JDKProxyFactory;
import cn.cherish.service.PersonService;
import cn.cherish.service.impl.PersonServiceBean;
public class AOPTest {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@Test public void proxyTest(){
JDKProxyFactory factory = new JDKProxyFactory();
PersonService service = (PersonService) factory.createProxyIntance(new PersonServiceBean("xxx"));
service.save("888");
}
}
結(jié)果:當我們給定參數(shù)xxx的時候旨枯,才會輸出“調(diào)用了save()方法”蹬昌,因為我們在代理對象中加入了user字段非空判斷
CGLibProxy
JDK的動態(tài)代理機制只能代理實現(xiàn)了接口的類,而不能實現(xiàn)接口的類就不能實現(xiàn)JDK的動態(tài)代理攀隔,cglib是針對類來實現(xiàn)代理的皂贩,他的原理是對指定的目標類生成一個子類,并覆蓋其中方法實現(xiàn)增強昆汹,但因為采用的是繼承明刷,所以不能對final修飾的類進行代理。
package cn.cherish.aop;
import java.lang.reflect.Method;
import cn.cherish.service.impl.PersonServiceBean;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CGlibProxyFactory implements MethodInterceptor{
private Object targetObject;
public Object createProxyIntance(Object targetObject){
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.targetObject.getClass());//非final
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
PersonServiceBean bean = (PersonServiceBean) this.targetObject;
Object result = null;
if(bean.getUser()!=null){
result = methodProxy.invoke(targetObject, args);
}
return result;
}
}