詳談Spring是否支持對(duì)靜態(tài)方法進(jìn)行Aop增強(qiáng)
Spring Aop是否對(duì)靜態(tài)方法進(jìn)行代理?不著急看結(jié)論,看完實(shí)現(xiàn)也就明白了細(xì)節(jié)。
1、JDK代理
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實(shí)現(xiàn)代理類比較重要的類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];
//重點(diǎn):代理那些方法?實(shí)例方法
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截圖:

到此處,已經(jīng)清楚JDK底層生成代理類時(shí)代理哪些方法,其中反射getMethods是可以獲取到Class中所有public方法,包括靜態(tài)方法。
由于JDK代理是基于接口的,而接口里面又不允許有靜態(tài)方法,所以是無(wú)法代理靜態(tài)方法的。換個(gè)角度:基于接口的Jdk代理與基于繼承Class的代理本質(zhì)都是基于繼承之后重寫指定方法實(shí)現(xiàn)的代理,而static方法是屬于class的,而不是類實(shí)例的,無(wú)法被重寫所以static方法無(wú)法代理。除此之外,JDK代理類是基于接口實(shí)現(xiàn)生成的,因此對(duì)于子類的final方法是可以代理的。
需要注意:Jdk8中的default方式是實(shí)例方法,而靜態(tài)方法。
2、CGLIB代理
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();
}
}
小結(jié)一下:基于JDK代理與基于CGLIB代理的代理類生成本質(zhì)都是基于繼承重寫實(shí)現(xiàn)的(實(shí)現(xiàn)接口可以認(rèn)為是一種特殊的繼承);對(duì)于static成員方法是無(wú)法子類重寫的,static是歸屬于class所屬。
至此:由于Spring使用的是JDK與CGLIB這兩種方式實(shí)現(xiàn)AOP,因此結(jié)論就是Spring無(wú)法支持static方法的代理增強(qiáng)。
Spring AOP靜態(tài)代理
對(duì)于AOP 我們應(yīng)該拿OOP來(lái)對(duì)比學(xué)習(xí),它們之間的區(qū)別如下:

AOP中不得不提的就是代理

通俗理解就是:茅臺(tái)公司生產(chǎn)出酒,而代理商拿出來(lái)銷售并推出各種銷售活動(dòng)。這時(shí)茅臺(tái)公司就是真實(shí)主題,也就是目標(biāo)對(duì)象。而代理商就是代理。茅臺(tái)酒就是目標(biāo)對(duì)象中的方法。各種銷售活動(dòng)就是給目標(biāo)對(duì)象中的方法的增強(qiáng)補(bǔ)充,比如對(duì)方法添加日志等等操作。
代理又分為靜態(tài)代理和動(dòng)態(tài)代理兩種:像這樣已知目標(biāo)對(duì)象就是為茅臺(tái)公司 就為靜態(tài)代理,這時(shí)目標(biāo)對(duì)象已確定
下面為一個(gè)靜態(tài)代理的例子
先定義一個(gè)PersonBiz的接口:

再對(duì)這個(gè)接口進(jìn)行實(shí)現(xiàn)

這是我們使用靜態(tài)代理給這兩個(gè)方法加上一個(gè)操作時(shí)間的功能,我就直接上代碼了:
package com.yc.dao.impl;
import java.util.Date;
import com.yc.dao.PersonBiz;
//代理對(duì)象
public class PersonBizProxy implements PersonBiz {
private PersonBiz personBiz;// 對(duì)真實(shí)主題的引用
public PersonBizProxy(PersonBiz personBiz) {
this.personBiz = personBiz;
}
@Override
public void add(String name) {
// 加入關(guān)注點(diǎn)-》增強(qiáng)的功能
showLog();// 前置增強(qiáng)
// 再調(diào)用真實(shí)主題的方法
this.personBiz.add(name);
}
@Override
public String find() {
// 調(diào)用真實(shí)主題的方法
personBiz.find();
// 加入關(guān)注點(diǎn)-》增強(qiáng)的功能
showLog();// 后置增強(qiáng)
return null;
}
private void showLog() {
Date d = new Date();
System.out.println("-----------------");
System.out.println("操作時(shí)間" + d);
System.out.println("-----------------");
}
}
最后就是測(cè)試類:

代理的優(yōu)勢(shì)很明顯:當(dāng)你不需要新增的操作時(shí)間的功能時(shí),就將PersonBizProxy pbp=new PersonBizProxy(pb);去掉即可,后面改用pb調(diào)用方法,讓代碼很好的實(shí)現(xiàn)了可擴(kuò)展性,也不用在原來(lái)已有的代碼上修改。
靜態(tài)代理的缺點(diǎn):只能針對(duì)一個(gè)接口進(jìn)行代理
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺談@RequestBody和@RequestParam可以同時(shí)使用
這篇文章主要介紹了@RequestBody和@RequestParam可以同時(shí)使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java Builder模式構(gòu)建MAP/LIST的實(shí)例講解
下面小編就為大家?guī)?lái)一篇Java Builder模式構(gòu)建MAP/LIST的實(shí)例講解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
java selenium教程之selenium詳細(xì)介紹
本文主要介紹Java selenium,這里整理了selenium的一些基本資料,此軟件主要用于Web UI自動(dòng)測(cè)試框架,有興趣的同學(xué)可以看一下2016-08-08
Springboot使用maven打包指定mainClass問(wèn)題
這篇文章主要介紹了Springboot使用maven打包指定mainClass問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
聊聊DecimalFormat的用法及各符號(hào)的意義
這篇文章主要介紹了DecimalFormat的用法及各符號(hào)的意義,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Java Springboot 重要知識(shí)點(diǎn)整理匯總
Spring Boot作為微服務(wù)中最好的Java框架,本文主要為大家整理匯總了七個(gè)Spring Boot的重要知識(shí)點(diǎn),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-11-11
Spring實(shí)戰(zhàn)之Bean的作用域request用法分析
這篇文章主要介紹了Spring實(shí)戰(zhàn)之Bean的作用域request用法,結(jié)合實(shí)例形式分析了spring中Bean的request作用域相關(guān)使用技巧與操作注意事項(xiàng),需要的朋友可以參考下2019-11-11
對(duì)數(shù)據(jù)進(jìn)行分頁(yè)顯示到table中的實(shí)現(xiàn)方法
這篇文章主要介紹了對(duì)數(shù)據(jù)進(jìn)行分頁(yè)顯示到table中的實(shí)現(xiàn)方法的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-05-05

