對(duì)AOP的簡(jiǎn)單理解
AOP就是面向切面編程,這種方式讓我們有更多精力放在核心業(yè)務(wù)邏輯上,下面這個(gè)圖可以方便我們理解

APO1.png
左圖的驗(yàn)證用戶功能就是每個(gè)操作中的橫切點(diǎn),整個(gè)系統(tǒng)中所有該功能的點(diǎn)連起來(lái)就成為了一個(gè)橫切面,我們可以將這個(gè)功能做成一個(gè)切面類(lèi),當(dāng)其他地方要使用的時(shí)候,直接將其置入里面就好了
這篇博客址講述AOP的實(shí)現(xiàn)方式,并不直接講述SpringAOP的使用方式,當(dāng)然SpringAOP的實(shí)現(xiàn)也用到了后面的動(dòng)態(tài)生成代理類(lèi)原理,如果只想看SpringAOP的簡(jiǎn)單使用請(qǐng)看這篇SpringAOPdemo
實(shí)現(xiàn)AOP的兩種方式(靜態(tài)、動(dòng)態(tài)代理)
靜態(tài)代理
我們首先看一下代理模式的結(jié)構(gòu)類(lèi)圖。Proxy就是我們的代理類(lèi),持有一個(gè)被代理對(duì)象的引用,和被代理對(duì)象共同實(shí)現(xiàn)了我們定義的業(yè)務(wù)接口,靜態(tài)代理就是通過(guò)代理模式來(lái)實(shí)現(xiàn)的

代理模式.png
只簡(jiǎn)單的說(shuō)一下實(shí)現(xiàn)
public interface IBankOperation(){ //銀行業(yè)務(wù)操作接口
public void drawMoney (int account); //取款
public int transferMoney(int account); //轉(zhuǎn)賬
}
public class BankOperationImp implement IBankOperation(){//具體實(shí)現(xiàn)
public void drawMoney(int account){
... //實(shí)現(xiàn)查找核心的業(yè)務(wù)邏輯
xxxDao.drawmoney(account); //從持久層查找數(shù)據(jù)
...
}
public int transferMoney(int account){
... //實(shí)現(xiàn)轉(zhuǎn)帳的核心業(yè)務(wù)邏輯
xxxDao.transferMoney(account); //持久層數(shù)據(jù)改變
}
}
下面我們需要在IBankOperation接口的所有方法操作前進(jìn)行驗(yàn)證操作,變使用代理對(duì)象來(lái)完成驗(yàn)證功能,并持有一個(gè)操作實(shí)現(xiàn)類(lèi)的引用,調(diào)用實(shí)現(xiàn)類(lèi)的方法來(lái)代替執(zhí)行
public class BankOperationProxy implement IBankOperation(){ //銀行操作代理類(lèi)
BankOperationImp operation; //持有一個(gè)被代理對(duì)象的引用
public void validate(){ //驗(yàn)證方法
}
public void drawMoney(int account){
validate();
operation.drawMoney(account);
}
public int transferMoney(int account){
validate();
operation.drawMoney(account);
}
}
靜態(tài)代理存在明顯的弊端:
- 一個(gè)代理類(lèi)只能為一個(gè)類(lèi)服務(wù),不同的類(lèi)需要各自的代理類(lèi)
- 被代理類(lèi)進(jìn)行了功能的擴(kuò)展,比如實(shí)現(xiàn)了另外一個(gè)接口,我們的代理類(lèi)也需要實(shí)現(xiàn)對(duì)應(yīng)的接口,并作實(shí)現(xiàn)
動(dòng)態(tài)代理(JDKProxy&&CGLibProxy)
package cn.cherish.service;
public interface PersonService { //業(yè)務(wù)類(lèi)接口
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ù)實(shí)現(xiàn)類(lèi)
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;
}
}
簡(jiǎn)單的分析一下上面這段代碼:
- 首先我們必須明確,要通過(guò)JDKProxy方式動(dòng)態(tài)獲取到代理對(duì)象,必須實(shí)現(xiàn)InvocationHandler接口,再通過(guò)反射包下的Proxy類(lèi)動(dòng)態(tài)獲取代理對(duì)象
-
invoke()方法的三個(gè)參數(shù),分別是代理對(duì)象,被代理對(duì)象被調(diào)用的方法,和被調(diào)用方法的參數(shù)集合 -
Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(), this);這就是我們通過(guò)目標(biāo)對(duì)象生成代理對(duì)象的代碼,三個(gè)類(lèi)分別是被代理對(duì)象的類(lèi)加載器,實(shí)現(xiàn)的接口,以及之后代理對(duì)象需要調(diào)用的方法,this表示被代理對(duì)象方法被調(diào)用時(shí),調(diào)用當(dāng)前類(lèi)的invoke()方法 - 在
invoke()方法中判斷用戶不為空之后,method.invoke(targetObject,args)表示調(diào)用targetObject的save()方法這里的method是通過(guò)反射獲取到的當(dāng)前被代理對(duì)象被調(diào)用的方法 - 最后注意,雖然
method.invoke(targetObject,args)返回值為空,但是我們還是需要返回一下的
測(cè)試結(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é)果:當(dāng)我們給定參數(shù)xxx的時(shí)候,才會(huì)輸出“調(diào)用了save()方法”,因?yàn)槲覀冊(cè)诖韺?duì)象中加入了user字段非空判斷
CGLibProxy
JDK的動(dòng)態(tài)代理機(jī)制只能代理實(shí)現(xiàn)了接口的類(lèi),而不能實(shí)現(xiàn)接口的類(lèi)就不能實(shí)現(xiàn)JDK的動(dòng)態(tài)代理,cglib是針對(duì)類(lèi)來(lái)實(shí)現(xiàn)代理的,他的原理是對(duì)指定的目標(biāo)類(lèi)生成一個(gè)子類(lèi),并覆蓋其中方法實(shí)現(xiàn)增強(qiáng),但因?yàn)椴捎玫氖抢^承,所以不能對(duì)final修飾的類(lèi)進(jìn)行代理。
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;
}
}