一.AOP的概念
在軟件業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
二.主要用途
將日志記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中划分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改變這些行為的時候不影響業務邏輯的代碼。
三.代理
AOP的實現關鍵在於AOP框架自動創建的AOP代理。AOP代理主要分為兩大類:
1.靜態代理:使用AOP框架提供的命令進行編譯,從而在編譯階段就可以生成AOP代理類,因此也稱為編譯時增強;靜態代理一Aspectj為代表。
2.動態代理:在運行時借助於JDK動態代理,CGLIB等在內存中臨時生成AOP動態代理類,因此也被稱為運行時增強,Spring AOP用的就是動態代理。
那么其實在Spring框架中用到就是JDK動態代理或者是CGLIB代理。
四.JDK動態代理
JDK動態代理用到了一個類Proxy,下面舉個例子來說明一下,Proxy這個類是如何實現JDK的動態代理的。
有一個需求就是,當用戶名為空的時候,不能調用業務方法,當用戶名不為空是可以調用業務方法。
1.新建一個業務接口,並書寫業務方法。
package cn.service; public interface DoService { //添加 public void add(); //查詢 public void selectInfo(); //更新 public void update(); }
2.新建一個業務接口實現類
package cn.service.impl; import cn.service.DoService; public class DoServiceImpl implements DoService{ //用戶名 private String userName; @Override public void add() { System.out.println("添加的方法"); } @Override public void selectInfo() { System.out.println("查詢的方法"); } @Override public void update() { System.out.println("更新的方法"); } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
一般如果我們需要進行這樣一個業務需求我們會相到 一個辦法就是在所有的方法中進行一個判斷就是判讀用戶名是否為空,顯然這樣很麻煩,所有我們用到了JDK動態代理,主函數 -->代理-->目標對象的方法。這樣,以后不管是修改判斷條件,還是查找等,可以直接在代理類中進行處理。
3.創建代理對象工廠類
package cn.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.service.impl.DoServiceImpl; /** * 代理工廠類 * @author hyj * */ public class JDKProxyFactory implements InvocationHandler{ //要返回的代理對象 private Object obj; /** * 通過代理工廠的方式來創建代理類 * @param obj 要代理的類的對象 * @return */ public Object createProxyIntance(Object obj){ this.obj=obj; //第一個參數是目標對象的類加載器 //第二個參數是目標對象實現的接口 //第三個參數傳入一個InvocationHandler實例,該參數和回調有關系。 return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object proxyObject=null; DoServiceImpl ds=(DoServiceImpl)obj; if(ds.getUserName()!=null){ proxyObject= method.invoke(ds, args); }else{ System.out.println("用戶名為空,已攔截"); } return proxyObject; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } }
4.測試類
package cn.test; import cn.aop.JDKProxyFactory; import cn.service.DoService; import cn.service.impl.DoServiceImpl; public class TestHappy { public static void main(String[] args) { JDKProxyFactory jpf=new JDKProxyFactory(); DoService ds=(DoService)jpf.createProxyIntance(new DoServiceImpl("fsf")); ds.selectInfo(); } }
步驟:
- 首先調用代理工廠的createProxyIntance(Object obj)創建DoServiceImpl類的代理類.
- 在該方法內,調用Proxy.newProxyInstance()方法創建代理對象。第一個參數是目標對象的類加載器,第二個參數是目標對象實現的接口,第三個參數傳入一個InvocationHandler實例,該參數和回調有關系。
- 每當調用目標對象的方法的時候,就會回調該InvocationHandler實例的方法,也就是public Object invoke()方法,我們就可以把限制的條件放在這里,條件符合的時候,就可以調用method.invoke()方法真正的調用目標對象的方法,否則,則可以在這里過濾掉不符合條件的調用。
五.CGLIB代理
在這一步中,我們使用一個Enhancer類來創建代理對象,不再使用Proxy。使用Enhancer類,需要為其實例指定一個父類,也就是我們 的目標對象。這樣,我們新創建出來的對象就是目標對象的子類,有目標對象的一樣。除此之外,還要指定一個回調函數,這個函數就和Proxy的 invoke()類似。
總體來說,使用CGlib的方法和使用Proxy的方法差不多,只是Proxy創建出來的代理對象和目標對象都實現了同一個接口。而CGlib的方法則是直接繼承了目標對象。
1.引入jar包
2.創建CGLIB實現代理的類
package cn.aop; import java.lang.reflect.Method; import cn.service.impl.DoServiceImpl; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * CGLIB實現代理 * @author hyj * */ public class CGLIBProxyFactory implements MethodInterceptor { //要放回的代理對象 private Object obj; public Object createProxy(Object obj){ //把傳進來的代理對象賦值給obj this.obj=obj; Enhancer enhancer = new Enhancer(); //需要為其實例指定一個父類,也就是我們 的目標對象,那么我們新創建出來的對象就是目標對象的子類,有目標對象的一樣 enhancer.setSuperclass(this.obj.getClass()); //除此之外,還要指定一個回調函數,這個函數就和Proxy的 invoke()類似 enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object proxyObject=null; DoServiceImpl ds=(DoServiceImpl)obj; if(ds.getUserName()!=null){ proxyObject= methodProxy.invoke(ds, args); }else{ System.out.println("用戶名為空,已攔截"); } return proxyObject; } }
測試類
@Test public void CGLIBProxyTest(){ CGLIBProxyFactory gb=new CGLIBProxyFactory(); DoServiceImpl ds= (DoServiceImpl)gb.createProxy(new DoServiceImpl("sf")); ds.add(); }