在做AOP 日志记录时 启动项目报如下错误8 ?0 k. ~# X, j7 H' p
0 W" H7 r, l, Z% h' u java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given ' K2 @9 J7 O4 c5 q 7 y- \# i0 n9 W3 C _8 ?大概意思是说 父类没有空的构造函数但没有给出参数 出自百度翻译。; j. D+ P3 d9 X( F
0 {' p5 ~6 u, A2 ?0 T/ J! F说明一下:" S& j( ~; H" d) t, \+ @
在类没有实现任何接口,并且没有默认构造函数的情况下,通过构造函数注入时,目前的spring是无法实现AOP切面拦截的。 . s' _' D3 ~7 G) D' b' a, u1 S" u! c# ]+ K0 g5 r
也就是说 生成一个空的构造方法即可。% n" k; L4 ~* k* M0 R8 \
原因分析 ' e5 D' f2 O% t9 F6 \, e
2 g+ s. e+ @2 D0 t: Z' m7 @
【前提】本文提到的没有实现接口,使用构造函数注入的类是Service层的类,并且注入的是Dao对象。 ! [2 k a$ ?) Z/ U我把Service类中加了默认构造函数,测试了一下,果然成功了,但是我就纳闷了,为什么加个默认构造函数就行了呢?如果是真的使用了这个默认构造函数生成类对象,那么起码Dao是根本就没有被注入到Service中的,而我在实际的测试中,读写数据库一切正常,显然意味着Dao对象是已经注入到Service对象里了。 9 [: d+ {3 j7 _* Z* C4 s: Y. d
5 K. e& W# t& |$ i7 ^5 V8 a" k
按照Spring in Action书中所述: 7 ?/ Y- S2 i# o如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。在创建这个类的时候,Spring将通知织入,并且将对目标对象的调用委托给这个子类。 7 p) l7 z9 J0 n/ `% k 7 O1 Y9 n! I$ O; y! I) D5 e' p这段叙述不足以让大家理解到底Spring是如何借助于CGLIB来实现AOP拦截的,在Debug Spring代码过程中,我惊奇的发现,实际上,Spring确实借助于CGLIB生成了个子类,但这个子类只是一个壳,只是原来目标类对象的一个代表,当我们调用目标类的方法时,从表面上看,是被CGLIB生成的子类接受到了,调的是子类的方法,而实际上当子类对象的方法被调用时,又回调了目标类对象的方法。当然在回调时,可以在目标对象的方法被调用前后加点切面处理。 7 I2 j/ d5 t; u8 ?
+ x) l; D, q# G$ ^$ K" O* X2 r- P3 S
这样一看,CGLIB生成的目标对象的这个子类,还真是个名副其实的代理(只是代表着目标对象的样子,实际上一有处理就转交目标对象处理了),我们只要想办法搞出这个子类的对象就可以了,由于他是直接回调的目标对象,所以,即使我们所必须的Dao没有被注入到子类对象也是没有关系的。 # g- S5 z- `: {2 j
5 f1 r3 w0 Z1 B* f; @
然而,最大的不幸是在下面。 , m7 ]5 t. e( b+ w, x9 L. |% x
在AOP切进来之前,实际上目标类的对象已经被注满了东西,也被初始化完毕了,然后,才将AOP切进来。 9 W. Y6 M: O6 |5 ?2 k6 s3 j在AOP切进来时,对于实现了接口的类,直接用了JDK的动态代理,把目标对象扔给JDK的Proxy,拿到代理对象就完事大吉了。 ! p1 D0 C; L. u* ~( B _1 r4 P4 H" }
然而对于没有实现接口的类,就麻烦了,当然肯定的借助于CGLIB来实现代理。 ' g# y+ s4 z$ D- J8 A
不幸就在这里,当Spring在制造完了目标对象后,并没有将在生产目标对象进所用的注入构造函数的参数对象传给AOP处理这部分,也就是说当到了AOP这块处理时,目标对象是传过来了,但当时生产它时候所用的构造函数的参数对象并没有一起传过了。 6 d( H6 ^! K9 J+ c7 f& g6 G6 L作为使用构造函数注入的目的之一:保证注入属性的不可变性。自然,我们也不会给目标类再安上构造函数注入属性的set/get方法。 1 b( F6 V7 f9 [# l& ?/ F X
! G- O, L& \# e! g
于是乎,当到了DefaultAopProxyFactory类下面的处理时,Spring根本无法将目标对象的构造函数参数对象传给Cglib2AopProxy对象。Cglib2AopProxy类的setConstructorArguments方法也只能是眼巴巴的看着异常的发生。因为到了CGLIB在制造代理对象时,ReflectUtils类的getConstructor方法根本就找不到默认的构造函数,于时异常最终发生了。 6 A8 [2 T/ x# w) L; h . E; z: Q% Z Y x& _0 V. jDefaultAopProxyFactory类中的内部CglibProxyFactory . {, Y+ C1 q+ k
private static class CglibProxyFactory {9 {7 h! p* c: L
' h# Y! r3 ^) ]
public static AopProxy createCglibProxy(AdvisedSupport advisedSupport) { 4 a% |# u( {: m+ h& H5 @& ?8 e% E