spring中注入bean的实现类导致的异常报错-AOP相关

关于spring中aop的实现已经是一个老生常谈的话题了,网上这块的文章也很多。近期就遇到了这样的一个问题,觉得对于spring中aop原理的简单说明,这是一个十分恰当的且实际应用的例子,因此记录下来。

起因

原本运行正常的代码,在加入aop后无法正常启动,log提示create bean error。

排查

在尝试把aop移除后,发现代码可以正常运行,因此可以把问题定位到与aop相关。其后,注意到报错的类中,注入的相关类是一个接口的实现类,而非使用多态的方式注入接口。在联系spring中aop的原理思考后,可以得知是由于aop的原因。

结论

众所周知,在spring中aop的实现是使用jdk的原生实现,以及cglib。
其中jdk的实现是有其局限性的,既只有实现接口的类才能使用jdk的方式来作代理。而cglib则补足了jdk的不足,他允许创建一个无接口实现的类的代理类。
在默认的情况下,spring总是优先使用jdk的方式作aop代理,当发现这个类没有实现任何一个接口的时候,才会使用cglib。
在此前提下,我们可以知道,我们前面提到的代码中,最终的代理类是使用jdk的方式创建的。
假设我们有一个接口 UserService,还有一个实现类UserServiceImpl。在正常的情况下,无论我们写出:

@Autowired
private UserService userService;

还是写出:

@Autowired
private UserServiceImpl userService;

这两种编码方式,在程序上都是可运行的。
但是由于使用了aop,因此这时spring检测到UserServiceImpl实现了接口UserService,因此他会使用jdk来创建类似以下的一个代理类:

public class UserServiceProx implement UserService{
	private UserServiceImpl userServiceImpl;
}

注意看上面的代码,基于jdk的代理方式创建的代理类,实际上是实现了UserService的一个代理类,但是并没有与我们真正要注入的实现类UserServiceImpl产生真正的层级上的关系。他仅仅是内部引用了这个实现类,作为实现他aop编程的一部分,但是在类的层级结构上,并没有继承我们的实现类。
基于此,我们没有办法用一个实现类来接手一个实现了接口且被aop切入的的spring bean。要想解决这个问题,我们可以修改spring aop的默认方式,使用cglib优先。但是更建议的方法是,如果你的类实现了接口,那么应该始终使用多态的方式来接收他。当一个接口有多个实现类的时候,可以使用名称来进行区分。

你可能感兴趣的:(spring)