摘要:關于的這種,我感覺應該是屬于編譯器織入,因為是通過子類生成字節碼然后進行調用。
關于的Aop:
用處:可以用在連接數據庫的方面,比如每一次在連接數據庫的時候都需要手動新建一個連接,然后執行數據庫操作,最后再來關閉數據庫的連接來避免資源的消耗。這時候有人就在想那么可不可以讓系統在我們每一次執行數據庫操作的時候都自動的新建一個連接然后當我們執行完數據庫的連接之后再自動的關閉連接呢。
這里就需要一個數據庫
Aop的原理例子(會使用到cglib動態代理):
例子:
首先新建三個注解
//定義在類上面標明該類是一個切點 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAspect { } // 前置通知注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Before { String value(); } //后置通知注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface After { String value(); }
新建一個歌曲類
public class Music { public void sing(String str){ System.out.println(str+"唱歌"); } }
當然,有人唱個歌就得有人做開始前的準備,例如唱歌前的準備,于是新建一個類表示唱歌前的準備:
@MyAspect // 表明這是一個切點類 public class Player { @Before("reflec.aop.cglibtest.Music.sing()") // 前置通知,當調用sing方法被調用的時候該方法會被在它之前調用 public void beforeSing() { System.out.println("開始唱歌前的準備"); } @After("reflec.aop.cglibtest.Music.sing()") // 同理,在調用sing方法之后再來調用該方法 public void afterSing() { System.out.println("唱完之后開始評分"); } }
那么當調用sing方法的時候我們怎樣調用這兩個方法呢,即怎樣織入這兩個方法
這里就得考慮Cglib動態代理了,cglib依賴asm包,在目標類的基礎上生成一個子類,然后通過子類來實現在目標方法調用的時候實現前置或者后置通知。。關于Cglib的這種,我感覺應該是屬于編譯器織入,因為是通過子類生成字節碼然后進行調用。
建立代理類:
這個類的作用只要是通過動態代理來實現那兩個方法的執行
public class CGLIBProxy implements MethodInterceptor { private Object target; private ProxyUtil proxyUtil ; public CGLIBProxy(Object target) throws ClassNotFoundException { this.target = target; proxyUtil =new ProxyUtil(); } publicT getProxy(){ return (T) new Enhancer().create(this.target.getClass(),this); } public T getProxy(Class> clazz){ return (T) new Enhancer().create(this.target.getClass(),this); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { ProxyEntity proxyEntity =new ProxyEntity(proxy,this.target.getClass(),obj,method,args); return proxyUtil.generateEntity(proxyEntity); }
建立反射類:(即主要是通)
public class Reflect { Mapmap ; //存入的是方法名以及其注解 Map clazzMap; public Reflect() throws ClassNotFoundException { map=new HashMap<>(); clazzMap =new HashMap<>(); getAnnotationClass(); } public Map getMap() { // 這里返回的是已經全部存好的map方面ProxyUtil使用 return map; } @Test public void getAnnotationClass() throws ClassNotFoundException { String clazzName="reflec.aop.cglibtest.Player"; Class> clazz =Class.forName(clazzName,false,Thread.currentThread().getContextClassLoader()); // 這里為了省事直接動態加載了該類 if (clazz.isAnnotationPresent(MyAspect.class)) { //假設是注解類 Method[] methods =clazz.getDeclaredMethods(); //遍歷方法 for (Method method :methods) { if (method.isAnnotationPresent(Before.class)) { // 獲取注解 Before before =method.getAnnotation(Before.class); String beforeValue=before.value(); // 獲取注解的值以及當前類的名字方面調用方法 map.put(method.getName()+ "-"+clazzName+"-"+"before",beforeValue.substring(0,beforeValue.length()-2)); // 存入的是方法名和注解名以及執行的順序,這里為了省事直接就在后面寫了 if (method.isAnnotationPresent(After.class)) { After after =method.getAnnotation(After.class); / String afterValue=after.value(); map.put(method.getName()+ "-"+clazzName+"-"+"after",afterValue.substring(0,afterValue.length()-2)); } } } }
建立處理這個Cglib里面的MethodInterceptor接口中的intercept方法的具體類
public class ProxyUtil { Reflect reflect; public ProxyUtil() throws ClassNotFoundException { reflect = new Reflect(); } public void getMethod(String name) { Mapmap = new HashMap<>(); } //該方法負責代理 public Object generateEntity(ProxyEntity proxyEntity) throws Throwable { String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("(")); Map methodMap = reflect.getMap(); for (Map.Entry map : methodMap.entrySet()) { if (map.getValue().equals(proxyMethodValue)) { String[] str = mapKeyDivision(map.getKey()); if (str[2].equals("before")) { Class> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // 加載該類 Method method = clazz.getDeclaredMethod(str[0]); method.invoke(clazz.newInstance(), null); // 反射調用方法 } } } return doAfter(proxyEntity,methodMap); // 處理后置通知 } private Object doAfter(ProxyEntity proxyEntity,Map map) throws Throwable { Object object = proxyEntity.getMethodProxy().invokeSuper(proxyEntity.getObject(), proxyEntity.getArgs()); // 調用方法 String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("(")); for(Map.Entry aMap:map.entrySet()){ if (aMap.getValue().equals(proxyMethodValue)){ String[] str =mapKeyDivision(aMap.getKey()); if(str[2].equals("after")){ Class> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // 加載該類 Method method = clazz.getDeclaredMethod(str[0]); method.invoke(clazz.newInstance(), null); // 這一步需要原始的類 } } } return object; } //分解map里面的鍵,因為里面存入了方法和類名以及執行順序 private String[] mapKeyDivision(String value) { String[] str = new String[10]; str[0] = value.substring(0, value.indexOf("-")); //注解下面的方法 str[1] = value.substring(value.indexOf("-") + 1, value.lastIndexOf("-")); //注解所在的類 str[2]=value.substring(value.lastIndexOf("-")+1,value.length()); //是before還是after return str; }
最后是一個bean
public class ProxyEntity { private final MethodProxy methodProxy; private final Class> clazz; private final Object object; private final Method method; private final Object[] args; public Object getObject() { return object; } public Object[] getArgs() { return args; } public Class> getClazz() { return clazz; } public Method getMethod() { return method; } public ProxyEntity(MethodProxy methodProxy, Class> clazz, Object object, Method method, Object[] args) { this.methodProxy = methodProxy; this.clazz = clazz; this.object = object; this.method = method; this.args = args; } public MethodProxy getMethodProxy() { return methodProxy; } }
最后進行測試:
public class CglibTest { public static void main(String args[]) throws ClassNotFoundException { Music music = new Music(); CGLIBProxy cglibProxy = new CGLIBProxy(music); ((Music)cglibProxy.getProxy()).sing("測試的人 "); } } 測試結果: 開始唱歌前的準備 唱歌測試的人 唱完之后開始評分
在這個測試中并沒有調用Player類里面的方法卻在運行的時候自動的運行了,這個例子執行簡單的模仿了下Spring的AOP,其實還有好多地方都沒有顧及到
這個類的思路就是先通過反射獲取到切點類,然后將用注解標注的方法名以及注解里面的值存入一個map,最后在建立一個類用來處理map
Github地址:https://github.com/Somersames...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69896.html
摘要:使用與的靜態代理不同,使用的動態代理,所謂的動態代理就是說框架不會去修改字節碼,而是在內存中臨時為方法生成一個對象,這個對象包含了目標對象的全部方法,并且在特定的切點做了增強處理,并回調原對象的方法。 AOP(Aspect Orient Programming),我們一般稱為面向方面(切面)編程,作為面向對象的一種補充,用于處理系統中分布于各個模塊的橫切關注點,比如事務管理、日志、緩存...
摘要:,,面向切面編程。,切點,切面匹配連接點的點,一般與切點表達式相關,就是切面如何切點。例子中,注解就是切點表達式,匹配對應的連接點,通知,指在切面的某個特定的連接點上執行的動作。,織入,將作用在的過程。因為源碼都是英文寫的。 之前《零基礎帶你看Spring源碼——IOC控制反轉》詳細講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態代理嗎?看這篇就夠了》介紹了下...
摘要:在上文中,我實現了一個很簡單的和容器。比如,我們所熟悉的就是在這里將切面邏輯織入相關中的。初始化的工作算是結束了,此時處于就緒狀態,等待外部程序的調用。其中動態代理只能代理實現了接口的對象,而動態代理則無此限制。 1. 背景 本文承接上文,來繼續說說 IOC 和 AOP 的仿寫。在上文中,我實現了一個很簡單的 IOC 和 AOP 容器。上文實現的 IOC 和 AOP 功能很單一,且 I...
摘要:又是什么其實就是一種實現動態代理的技術,利用了開源包,先將代理對象類的文件加載進來,之后通過修改其字節碼并且生成子類。 在實際研發中,Spring是我們經常會使用的框架,畢竟它們太火了,也因此Spring相關的知識點也是面試必問點,今天我們就大話Aop。特地在周末推文,因為該篇文章閱讀起來還是比較輕松詼諧的,當然了,更主要的是周末的我也在充電學習,希望有追求的朋友們也盡量不要放過周末時...
摘要:我自己總結的學習的系統知識點以及面試問題,已經開源,目前已經。目前最新的版本中模塊的組件已經被廢棄掉,同時增加了用于異步響應式處理的組件。每一次請求都會產生一個新的,該僅在當前內有效。顯而易見,這種模式存在很多問題。 我自己總結的Java學習的系統知識點以及面試問題,已經開源,目前已經 41k+ Star。會一直完善下去,歡迎建議和指導,同時也歡迎Star: https://githu...
閱讀 2795·2021-09-01 10:30
閱讀 1687·2019-08-30 15:52
閱讀 976·2019-08-29 18:40
閱讀 1131·2019-08-28 18:30
閱讀 2400·2019-08-23 17:19
閱讀 1331·2019-08-23 16:25
閱讀 2705·2019-08-23 16:18
閱讀 2986·2019-08-23 13:53