在做AOP 日志记录时 启动项目报如下错误0 L! t- S- q/ f
7 s: ~1 S# a' i( r java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given / y. O& V2 m5 V& v, G
# d" Y# V4 E- x0 y$ U4 b, A大概意思是说 父类没有空的构造函数但没有给出参数 出自百度翻译。; F& K( y2 z+ X
' f# x5 ]1 F2 Z7 m: q
说明一下: 0 a2 F, o0 M6 T( G& I: Y在类没有实现任何接口,并且没有默认构造函数的情况下,通过构造函数注入时,目前的spring是无法实现AOP切面拦截的。 3 t z& [3 }$ W" W; e8 B5 p w3 ~! Z ; o; i3 @% I7 Y0 L6 X2 ^# F5 Z也就是说 生成一个空的构造方法即可。3 B* j' `) ]4 U, f
原因分析 # T- [ f8 c8 R4 P, R8 A
8 p! v! P/ a& L4 _, Y0 P7 g) n
【前提】本文提到的没有实现接口,使用构造函数注入的类是Service层的类,并且注入的是Dao对象。 / M& x% ~0 X- b6 ]1 H我把Service类中加了默认构造函数,测试了一下,果然成功了,但是我就纳闷了,为什么加个默认构造函数就行了呢?如果是真的使用了这个默认构造函数生成类对象,那么起码Dao是根本就没有被注入到Service中的,而我在实际的测试中,读写数据库一切正常,显然意味着Dao对象是已经注入到Service对象里了。 % K! w% E& a; v: r( z- C2 l ! \# @/ j$ [ I! z/ T3 ^按照Spring in Action书中所述: 3 r( m6 s9 s. W9 j/ ]) f. {如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。在创建这个类的时候,Spring将通知织入,并且将对目标对象的调用委托给这个子类。 : S0 B1 X# R, }; K4 k9 j5 B/ C4 G. {4 u# c7 E7 A; r0 T \$ T
这段叙述不足以让大家理解到底Spring是如何借助于CGLIB来实现AOP拦截的,在Debug Spring代码过程中,我惊奇的发现,实际上,Spring确实借助于CGLIB生成了个子类,但这个子类只是一个壳,只是原来目标类对象的一个代表,当我们调用目标类的方法时,从表面上看,是被CGLIB生成的子类接受到了,调的是子类的方法,而实际上当子类对象的方法被调用时,又回调了目标类对象的方法。当然在回调时,可以在目标对象的方法被调用前后加点切面处理。 f9 c3 b1 x, ]
7 K1 q+ a5 }6 @
这样一看,CGLIB生成的目标对象的这个子类,还真是个名副其实的代理(只是代表着目标对象的样子,实际上一有处理就转交目标对象处理了),我们只要想办法搞出这个子类的对象就可以了,由于他是直接回调的目标对象,所以,即使我们所必须的Dao没有被注入到子类对象也是没有关系的。 5 ?" t) o- e, D, B5 @0 k1 [4 Q$ V
. q/ ~7 c! ?9 Y
然而,最大的不幸是在下面。 ( `' f& C" P" e! k; Z# j
在AOP切进来之前,实际上目标类的对象已经被注满了东西,也被初始化完毕了,然后,才将AOP切进来。 : F8 w1 D- O7 @7 u7 G" R. L
在AOP切进来时,对于实现了接口的类,直接用了JDK的动态代理,把目标对象扔给JDK的Proxy,拿到代理对象就完事大吉了。 * Z( m3 d! U v4 m4 I
然而对于没有实现接口的类,就麻烦了,当然肯定的借助于CGLIB来实现代理。 # c' ^: A3 I1 i0 c不幸就在这里,当Spring在制造完了目标对象后,并没有将在生产目标对象进所用的注入构造函数的参数对象传给AOP处理这部分,也就是说当到了AOP这块处理时,目标对象是传过来了,但当时生产它时候所用的构造函数的参数对象并没有一起传过了。 O! L* o6 ~/ q4 Q5 W8 B
作为使用构造函数注入的目的之一:保证注入属性的不可变性。自然,我们也不会给目标类再安上构造函数注入属性的set/get方法。 1 f' A5 O2 H3 O+ T% D9 O9 M5 Z$ Z& ]
于是乎,当到了DefaultAopProxyFactory类下面的处理时,Spring根本无法将目标对象的构造函数参数对象传给Cglib2AopProxy对象。Cglib2AopProxy类的setConstructorArguments方法也只能是眼巴巴的看着异常的发生。因为到了CGLIB在制造代理对象时,ReflectUtils类的getConstructor方法根本就找不到默认的构造函数,于时异常最终发生了。 8 j9 i$ X& y6 E : z6 c% r j2 n4 h( Y w% yDefaultAopProxyFactory类中的内部CglibProxyFactory 2 }( m2 ]) u5 q$ g6 V/ g
private static class CglibProxyFactory { " z. Y, u( L+ {1 z# F
3 s5 s& _; q* x+ B% x( ?! `$ |8 Z& S; L
public static AopProxy createCglibProxy(AdvisedSupport advisedSupport) {% `4 d4 Y" H/ [8 K
return new Cglib2AopProxy(advisedSupport);* V4 ?5 n1 C2 W% e1 F; e
} 1 ^9 ?) b, W V" O% b
}
复制代码
ReflectUtils类的getConstructor方法 ) X$ F* G, o2 [% t: c
public static Constructor getConstructor(Class type, Class[] parameterTypes) {. d+ d! l( c; i( ?2 g