上篇文章讲完了jdk的动态代理,基本上对jdk动态代理的原理有了基本认识,比如按class结构填充class文件,通过反射调用方法等。但是jdk有一些问题,比如必须要实现接口,对类的直接代理无法完成,cglib就解决了此类问题,允许直接对类进行代理。
废话不多说先上代码,对类的代理。
被代理类
public class HelloImplClass implements Hello{
public void sayHello(String name) {
System.out.println("hello "+name);
}
}
增强逻辑,参数的意义后面再说
public class CglibClassProxyIntercepter implements MethodInterceptor {
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行前...");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("执行后...");
return object;
}
}
测试类
public class MainTestClass {
public static void main(String[] args) throws Exception {
//代理类class文件存入本地磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
Enhancer enhancer = new Enhancer();
enhancer.setCallback(new CglibClassProxyIntercepter());
enhancer.setSuperclass(HelloImplClass.class);
HelloImplClass proxy= (HelloImplClass) enhancer.create();
proxy.sayHello("张三");
}
}
以上就是cglib的基本用法,可以看到我们没有接口,直接通过setSuperClass设置了实现类,就可以生成代理了proxy.
但是代理类是如何生成的呢,又是如何调用的呢,我们看看源码
我们的入口定在enhancer.create()方法,鉴于咱们队jdk动态代理源码分析的比较细,这里就简单的看看
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}
Enhancer的cerate()方法并没有做些什么,委托给createHelper方法,代码如下
private Object createHelper() {
//验证
preValidate();
//生成key
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
//通过key去构造
Object result = super.create(key);
return result;
}
发现这里面也有key,cglib也和jdk的动态代理一样,运用了缓存功能。并且这个key很特殊,相当于一个联合key,由多个字段组成,比如类的名字,filter等,而且这里的KEY_FACTORY也是有cglib代理生成的,在本地只有接口。我们继续看super.create,其在父类AbstractClassGenerator中
protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
//这里和jdk动态代理类似,也用到了缓存,classloader为key
Map cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {
//没有数据则构建新缓存
Map newCache = new WeakHashMap(cache);
//重点:这里生成了class并存入缓存
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
//从缓存获取数据
Object obj = data.get(this, getUseCache());
//根据返回类型的不同去实例化对象
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}
其中的cache是这样的,也是弱引用类型,不过与jdk动态代理不同的是其二级缓存是ClassLoadData类型
private static volatile Map CACHE = new WeakHashMap();
我们继续跟踪new ClassLoaderData(loader)看下是如何生成class对象,代码很简单只是做了class生成然后封装,最后放到generatedClasses中,其中的generatedClasses是二级缓存
public ClassLoaderData(ClassLoader classLoader) {
if (classLoader == null) {
throw new IllegalArgumentException("classLoader == null is not yet supported");
}
this.classLoader = new WeakReference(classLoader);
Function load =
new Function() {
public Object apply(AbstractClassGenerator gen) {
//生成class对象
Class klass = gen.generate(ClassLoaderData.this);
return gen.wrapCachedClass(klass);
}
};
generatedClasses = new LoadingCache(GET_KEY, load);
}
继续看gen.generate()
protected Class generate(ClassLoaderData data) {
Class gen;
Object save = CURRENT.get();
CURRENT.set(this);
try {
ClassLoader classLoader = data.getClassLoader();
if (classLoader == null) {
throw new IllegalStateException("ClassLoader is null while trying to define class " +
getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +
"Please file an issue at cglib's issue tracker.");
}
synchronized (classLoader) {
//类的名字
String name = generateClassName(data.getUniqueNamePredicate());
data.reserveName(name);
this.setClassName(name);
}
if (attemptLoad) {
try {
gen = classLoader.loadClass(getClassName());
return gen;
} catch (ClassNotFoundException e) {
// ignore
}
}
//根据策略生成不同的字节码
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
//利用反射生成class对象
synchronized (classLoader) { // just in case
if (protectionDomain == null) {
gen = ReflectUtils.defineClass(className, b, classLoader);
} else {
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
}
}
return gen;
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
} finally {
CURRENT.set(save);
}
}
上面只要做了三件事
1、生成类的名字规则是:被代理class name + "$$" + class generator name + "ByCGLIB" + "$$" + key的hashcode
2、根据策略生成字节码
3、利用反射根据字节码生成class对象
反射生成对象不用说,这里看下策略生成字节码,是在DefaultGeneratorStrategy默认生成策略类中
public byte[] generate(ClassGenerator cg) throws Exception {
DebuggingClassWriter cw = getClassVisitor();
//调用ClassGenerator的generateClass去生成
transform(cg).generateClass(cw);
//转为字节数组
return transform(cw.toByteArray());
}
上面代码根据不同的ClassGenerator去生成不同的字节码,部分ClassGenerator如下,都是用ASM框架去生成字节码的
对ASM框架如何生成字节码我们不做深究,这里就不在细说。到这里代理是如何生成的答题有了一个认识
cglib 是通过ASM框架生成字节码,采用反射去构造代理对象,同时为了提高效率也用了缓存结构,缓存结构也是弱类型。
接下来分析下cglib生成的class对象,一共三个
//HelloImplClass的代理对象
HelloImplClass$$EnhancerByCGLIB$$a85b2ff3.class
//HelloImplClass对应的FastClass对象
HelloImplClass$$FastClassByCGLIB$$67ea48ad.class
//HelloImplClass的代理对象对应的FastClass对象
HelloImplClass$$EnhancerByCGLIB$$a85b2ff3$$FastClassByCGLIB$$f8e31dfe.class
首先看下HelloImplClass$$EnhancerByCGLIB$$a85b2ff3.class代理对象
public class HelloImplClass$$EnhancerByCGLIB$$a85b2ff3 extends HelloImplClass implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
//方法拦截,我们定义的
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
//被代理对象的方法
private static final Method CGLIB$sayHello$0$Method;
//代理的方法
private static final MethodProxy CGLIB$sayHello$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
//var0为代理对象
Class var0 = Class.forName("com.example.lrn_manager.proxy.cglib_proxy.HelloImplClass$$EnhancerByCGLIB$$a85b2ff3");
//var1为被代理对象
Class var1;
//被代理方法
CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[]{"sayHello", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.example.lrn_manager.proxy.cglib_proxy.HelloImplClass")).getDeclaredMethods())[0];
//代理方法
CGLIB$sayHello$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "sayHello", "CGLIB$sayHello$0");
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
}
//这个方法在fastClass中调用,此调用是执行真正的逻辑
final void CGLIB$sayHello$0(String var1) {
super.sayHello(var1);
}
//代理对象调用sayHello
public final void sayHello(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
//如果方法拦截不为空则调用intercept方法
if (var10000 != null) {
var10000.intercept(this, CGLIB$sayHello$0$Method, new Object[]{var1}, CGLIB$sayHello$0$Proxy);
} else {
super.sayHello(var1);
}
}
重点已经标注
1、代理类继承了被代理类,这里和jdk动态代理不一样,动态代理是集成Proxy类实现了我们的接口,而cglib是集成了我们的类重写了父类中的方法
2、MethodProxy.create创建了MethodProxy
3、在重写的方法中sayHello中加入了方法拦截逻辑,我们定义的CglibClassProxyIntercepter就是在此处去执行intercept方法的
intercept的四个参数的意思也明确了,填上开始的坑
intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy)方法四个参数的含义
sub:可以看出传入的是this,this指的是代理对象
method:是被代理对象方法对象CGLIB$sayHello$0$Method
objects:指的是方法入参
methodProxy:是方法的代理对象CGLIB$sayHello$0$Proxy
如我们看到在我们开始的例子,调用proxy.sayHello("张三")时,是调用的代理对象的sayHello方法,在代理对象的sayHello调用了CglibClassProxyIntercepter的intercept方法。
我们看下CglibClassProxyIntercepter的逻辑,执行增强
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行前...");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("执行后...");
return object;
}
这里用到methodProxy.invokeSuper方法,还记得在代理类中我们调用了他的create方法,现在说说MethodProxy类
先从create方法说起
public class MethodProxy {
private Signature sig1;
private Signature sig2;
private CreateInfo createInfo;
private final Object initLock = new Object();
private volatile FastClassInfo fastClassInfo;
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
create的参数在代理类中已经给出,如下,var1即c1是被代理对象,var0即c2使代理对象
MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "sayHello", "CGLIB$sayHello$0")
create按名字生成了两个签名,并且按两个class对象生成了一个createinfo,createInfo代码如下
private static class CreateInfo
{
Class c1;//被代理对象
Class c2;//代理对象
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad;
public CreateInfo(Class c1, Class c2)
{
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if (fromEnhancer != null) {
namingPolicy = fromEnhancer.getNamingPolicy();
strategy = fromEnhancer.getStrategy();
attemptLoad = fromEnhancer.getAttemptLoad();
}
}
}
其实貌似什么也没有做,只是实例化了一些东西,做了些准备
接下来看methodProxy.invokeSuper方法
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
这里主要进行了初始化,调用了FastClassInfo的变量的invoke方法,我们只知道这么多,然后一步一步看
FastClassInfo长这样,两个FastClass变量,两个int类型的变量
private static class FastClassInfo
{
FastClass f1;//被代理对象FastClass
FastClass f2;//代理对象FastClass
int i1;
int i2;
}
init方法
private void init()
{
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
}
}
}
helper方法用ASM框架去生成了两个FastClass类,我们目前只讲了一个剩下的两个就是在这里生成的
//HelloImplClass对应的FastClass对象
HelloImplClass$$FastClassByCGLIB$$67ea48ad.class
//HelloImplClass的代理对象对应的FastClass对象
HelloImplClass$$EnhancerByCGLIB$$a85b2ff3$$FastClassByCGLIB$$f8e31dfe.class
并且调用了FastClass的getIndex方法,我们看其中的HelloImplClass$$EnhancerByCGLIB$$a85b2ff3$$FastClassByCGLIB$$f8e31dfe.class
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -2055565910:
if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 13;
}
break;
case -1882565338:
if (var10000.equals("CGLIB$equals$1(Ljava/lang/Object;)Z")) {
return 15;
}
break;
case -1721191351:
if (var10000.equals("CGLIB$sayHello$0(Ljava/lang/String;)V")) {
return 16;
}
break;
case -1457535688:
if (var10000.equals("CGLIB$STATICHOOK1()V")) {
return 20;
}
break;
case -1411842725:
if (var10000.equals("CGLIB$hashCode$3()I")) {
return 18;
}
break;
case -894172689:
if (var10000.equals("newInstance(Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
return 4;
}
break;
case -623122092:
if (var10000.equals("CGLIB$findMethodProxy(Lnet/sf/cglib/core/Signature;)Lnet/sf/cglib/proxy/MethodProxy;")) {
return 14;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return 3;
}
break;
case -419626537:
if (var10000.equals("setCallbacks([Lnet/sf/cglib/proxy/Callback;)V")) {
return 7;
}
break;
case 560567118:
if (var10000.equals("setCallback(ILnet/sf/cglib/proxy/Callback;)V")) {
return 8;
}
break;
case 771401912:
if (var10000.equals("sayHello(Ljava/lang/String;)V")) {
return 9;
}
break;
case 811063227:
if (var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
return 5;
}
break;
case 973717575:
if (var10000.equals("getCallbacks()[Lnet/sf/cglib/proxy/Callback;")) {
return 11;
}
break;
case 1221173700:
if (var10000.equals("newInstance([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
return 6;
}
break;
case 1230699260:
if (var10000.equals("getCallback(I)Lnet/sf/cglib/proxy/Callback;")) {
return 10;
}
break;
case 1306468936:
if (var10000.equals("CGLIB$toString$2()Ljava/lang/String;")) {
return 17;
}
break;
case 1584330438:
if (var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 12;
}
break;
case 1800494055:
if (var10000.equals("CGLIB$clone$4()Ljava/lang/Object;")) {
return 19;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return 0;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return 1;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 2;
}
}
return -1;
}
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
a85b2ff3 var10000 = (a85b2ff3)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
return new Boolean(var10000.equals(var3[0]));
case 1:
return var10000.toString();
case 2:
return new Integer(var10000.hashCode());
case 3:
return var10000.clone();
case 4:
return var10000.newInstance((Callback)var3[0]);
case 5:
return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 6:
return var10000.newInstance((Callback[])var3[0]);
case 7:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 8:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 9:
var10000.sayHello((String)var3[0]);
return null;
case 10:
return var10000.getCallback(((Number)var3[0]).intValue());
case 11:
return var10000.getCallbacks();
case 12:
a85b2ff3.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 13:
a85b2ff3.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 14:
return a85b2ff3.CGLIB$findMethodProxy((Signature)var3[0]);
case 15:
return new Boolean(var10000.CGLIB$equals$1(var3[0]));
case 16:
var10000.CGLIB$sayHello$0((String)var3[0]);
return null;
case 17:
return var10000.CGLIB$toString$2();
case 18:
return new Integer(var10000.CGLIB$hashCode$3());
case 19:
return var10000.CGLIB$clone$4();
case 20:
a85b2ff3.CGLIB$STATICHOOK1();
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
getIndex的作用是根据签名去创建索引,invoke方法是根据索引快速执行方法。
FastClass的作用也就明确了,生成索引,并且快速定位调用方法
init的方法作用就是生成索引
fci.f2.invoke(fci.i2, obj, args) 是调用代理类的FastClass 的invoke 快速索引是i2从上面可以看出i2是16,在执行invoke快速case到16去调用代理类的CGLIB$sayHello$0方法,此方法调用了super.sayHello被代理类的真实逻辑。
至此所有的都分析完了,我们再回顾一下。
1)如何生成代理类:底层是通过ASM框架生成字节码,用反射生成代理类
2)代理类是如何调用的:通过FastClass的getindex生成索引,invoke方法定位索引来实现调用方法的
3)执行顺序:代理类sayHello->CglibClassProxyIntercepter类intercept->methodProxy类invokeSuper->代理类FastClass类的invoke->代理类的CGLIB$sayHello$0方法->被代理类的sayHello方法
cglib也可以对接口进行代理,对接口的代理
接口
public interface Hello {
void sayHello(String name);
}
实现类
public class HelloImpl implements Hello{
public void sayHello(String name) {
System.out.println("hello "+name);
}
}
增强逻辑
public class CglibProxyIntercepter implements MethodInterceptor {
Object hello;
CglibProxyIntercepter(Object hello){
this.hello=hello;
}
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行前...");
Object object = methodProxy.invoke(hello, objects);
System.out.println("执行后...");
return object;
}
}
测试类MainTest
public class MainTest {
public static void main(String[] args) throws Exception {
//代理类class文件存入本地磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
Enhancer enhancer = new Enhancer();
Hello hello=new HelloImpl();
enhancer.setCallback(new CglibProxyIntercepter(hello));
enhancer.setInterfaces(new Class[]{Hello.class});
Hello proxy= (Hello) enhancer.create();
proxy.sayHello("张三");
}
}
对接口的代理用的方式是setInterface+methodProxy.invoke,而对类的代理是setSuperClass+methodProxy.invokeSuper
对接口的代理的增强逻辑和jdk动态代理非常类似,都是持有目标类。
看下代理类的反编译代码
public class Hello$$EnhancerByCGLIB$$4e6b0c17 implements Hello, Factory {
其余和对类的代理都一样。。。。
}
和对类的代理不同这里是实现了接口,而对类的代理是集成了目标类。
再看下FastClass反编译代码,发现getIndex的swith case 项少了很多,只有需要实现的方法存在,其余的都没了
public class Hello$$FastClassByCGLIB$$c892f6cb extends FastClass {
public Hello$$FastClassByCGLIB$$c892f6cb(Class var1) {
super(var1);
}
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case 771401912:
if (var10000.equals("sayHello(Ljava/lang/String;)V")) {
return 0;
}
}
return -1;
}
public int getIndex(String var1, Class[] var2) {
switch(var1.hashCode()) {
case -2012993625:
if (var1.equals("sayHello")) {
switch(var2.length) {
case 1:
if (var2[0].getName().equals("java.lang.String")) {
return 0;
}
}
}
}
return -1;
}
public int getIndex(Class[] var1) {
int var10001 = var1.length;
return -1;
}
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
Hello var10000 = (Hello)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.sayHello((String)var3[0]);
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
1)执行顺序:代理类sayHello->CglibClassProxyIntercepter类intercept->methodProxy类invoke->被代理类的FastClass类的invoke->被代理类的sayHello方法
两种方式有本质上的区别,多注意下,而且两种模式不能混用,在类代理中用invoke方法会出现OOM,主要原因时出现了递归调用