您好,登錄后才能下訂單哦!
本篇內容主要講解“Spring支持對靜態方法進行Aop增強嗎”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Spring支持對靜態方法進行Aop增強嗎”吧!
JDK代理代碼:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface Echor { public void echo(); } class EchorImpl implements Echor { @Override public void echo() { System.out.println("echo ~"); } } class MethodInvoker<T> implements InvocationHandler { private T invoker; public MethodInvoker(T invoker) { this.invoker = invoker; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start ~"); Object result = method.invoke(invoker, args); System.out.println("end ~"); return result; } } public class DebugJdkProxy { public static void main(String[] args) { Echor proxy = (Echor) Proxy.newProxyInstance(DebugJdkProxy.class.getClassLoader(), new Class[]{Echor.class}, new MethodInvoker<Echor>(new EchorImpl())); proxy.echo(); } }
JVM實現代理類比較重要的類sun.misc.ProxyGenerator,生成代理類的方法為generateClassFile源碼:
private byte[] generateClassFile() { this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; //重點:代理那些方法?實例方法 Method[] var5 = var4.getMethods(); int var6 = var5.length; for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } Iterator var11 = this.proxyMethods.values().iterator(); List var12; while(var11.hasNext()) { var12 = (List)var11.next(); checkReturnTypes(var12); } Iterator var15; try { this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator(); while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator(); while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(var16.generateMethod()); } } this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); } if (this.methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } else if (this.fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } else { this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); var1 = this.interfaces; var2 = var1.length; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; this.cp.getClass(dotToSlash(var4.getName())); } this.cp.setReadOnly(); ByteArrayOutputStream var13 = new ByteArrayOutputStream(); DataOutputStream var14 = new DataOutputStream(var13); try { var14.writeInt(-889275714); var14.writeShort(0); var14.writeShort(49); this.cp.write(var14); var14.writeShort(this.accessFlags); var14.writeShort(this.cp.getClass(dotToSlash(this.className))); var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); var14.writeShort(this.interfaces.length); Class[] var17 = this.interfaces; int var18 = var17.length; for(int var19 = 0; var19 < var18; ++var19) { Class var22 = var17[var19]; var14.writeShort(this.cp.getClass(dotToSlash(var22.getName()))); } var14.writeShort(this.fields.size()); var15 = this.fields.iterator(); while(var15.hasNext()) { ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next(); var20.write(var14); } var14.writeShort(this.methods.size()); var15 = this.methods.iterator(); while(var15.hasNext()) { ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next(); var21.write(var14); } var14.writeShort(0); return var13.toByteArray(); } catch (IOException var9) { throw new InternalError("unexpected I/O Exception", var9); } } }
上DEBUG截圖:
到此處,已經清楚JDK底層生成代理類時代理哪些方法,其中反射getMethods是可以獲取到Class中所有public方法,包括靜態方法。
由于JDK代理是基于接口的,而接口里面又不允許有靜態方法,所以是無法代理靜態方法的。換個角度:基于接口的Jdk代理與基于繼承Class的代理本質都是基于繼承之后重寫指定方法實現的代理,而static方法是屬于class的,而不是類實例的,無法被重寫所以static方法無法代理。除此之外,JDK代理類是基于接口實現生成的,因此對于子類的final方法是可以代理的。
需要注意:Jdk8中的default方式是實例方法,而靜態方法。
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; interface Echor { public void echo(); public static void hello() { System.out.println("hello world!"); } } abstract class AbsEchor implements Echor { public static void abs() { System.out.println("abs~~"); } public static void hello() { System.out.println("hello world!"); } } class EchorImpl implements Echor { public static void hello2() { System.out.println("hello world!"); } @Override public void echo() { System.out.println("echo ~"); } } class EchorMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("start ~"); Object result = proxy.invokeSuper(obj, args); System.out.println("end ~"); return result; } } class DebugCGlibProxy { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(AbsEchor.class); enhancer.setCallback(new EchorMethodInterceptor()); AbsEchor hello = (AbsEchor) enhancer.create(); hello.abs(); } }
小結一下:基于JDK代理與基于CGLIB代理的代理類生成本質都是基于繼承重寫實現的(實現接口可以認為是一種特殊的繼承);對于static成員方法是無法子類重寫的,static是歸屬于class所屬。
至此:由于Spring使用的是JDK與CGLIB這兩種方式實現AOP,因此結論就是Spring無法支持static方法的代理增強。
對于AOP 我們應該拿OOP來對比學習,它們之間的區別如下:
通俗理解就是:茅臺公司生產出酒,而代理商拿出來銷售并推出各種銷售活動。這時茅臺公司就是真實主題,也就是目標對象。而代理商就是代理。茅臺酒就是目標對象中的方法。各種銷售活動就是給目標對象中的方法的增強補充,比如對方法添加日志等等操作。
代理又分為靜態代理和動態代理兩種:像這樣已知目標對象就是為茅臺公司 就為靜態代理,這時目標對象已確定
先定義一個PersonBiz的接口:
再對這個接口進行實現
這是我們使用靜態代理給這兩個方法加上一個操作時間的功能,我就直接上代碼了:
package com.yc.dao.impl; import java.util.Date; import com.yc.dao.PersonBiz; //代理對象 public class PersonBizProxy implements PersonBiz { private PersonBiz personBiz;// 對真實主題的引用 public PersonBizProxy(PersonBiz personBiz) { this.personBiz = personBiz; } @Override public void add(String name) { // 加入關注點-》增強的功能 showLog();// 前置增強 // 再調用真實主題的方法 this.personBiz.add(name); } @Override public String find() { // 調用真實主題的方法 personBiz.find(); // 加入關注點-》增強的功能 showLog();// 后置增強 return null; } private void showLog() { Date d = new Date(); System.out.println("-----------------"); System.out.println("操作時間" + d); System.out.println("-----------------"); } }
最后就是測試類:
代理的優勢很明顯:當你不需要新增的操作時間的功能時,就將PersonBizProxy pbp=new PersonBizProxy(pb);去掉即可,后面改用pb調用方法,讓代碼很好的實現了可擴展性,也不用在原來已有的代碼上修改。
靜態代理的缺點:只能針對一個接口進行代理
到此,相信大家對“Spring支持對靜態方法進行Aop增強嗎”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。