Spring Aop是否对静态方法进行代理?不着急看结论,看完实现也就明白了细节。
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实现代理类比较重要的类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方式是实例方法,而静态方法。
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();
}
}
CGlib是基于继承重写实现的代理,因此要求Class必须是非final class 与此同时被代理的方法必须是非final方法,因此final方法无法被子类重写,因此就无法代理。
小结
基于JDK代理与基于CGLIB代理的代理类生成本质都是基于继承重写实现的(实现接口可以认为是一种特殊的继承);对于static成员方法是无法子类重写的,static是归属于class所属。
至此:由于Spring使用的是JDK与CGLIB这两种方式实现AOP,因此结论就是Spring无法支持static方法的代理增强。