开发环境遇到报错
org.springframework.beans.factory.nosuchbeandefinitionexception: no qualifying bean of type 'xxxxx' available
现将原问题代码简化抽出分析。
部署环境
- springboot版本:1.x
- java:1.8
- 其他:略
问题代码
service层
- 接口:
public interface errorbeanservice { void transactionalmethod(); void callmethod(); }
- 实现类:
@service("errorbeanserviceimpl") public class errorbeanserviceimpl implements errorbeanservice{ private applicationcontext applicationcontext; @autowired public void setapplicationcontext(applicationcontext applicationcontext){ this.applicationcontext = applicationcontext; } @transactional(rollbackfor = exception.class) public void transactionalmethod() { system.out.println("transactionalmethod run ~ "); } public void callmethod() { // transactional注解失效 // transactionalmethod(); // 使用applicationcontext获取到bean 使事物注解生效 applicationcontext.getbean(errorbeanserviceimpl.class).transactionalmethod(); // 报错行 } }
contro层:
@restcontroller @requestmapping("/error") public class errorcontroller { @autowired private errorbeanservice errorbeanservice; @requestmapping(value = "/errorbean", method = requestmethod.get) public void errorbean() { errorbeanservice.callmethod(); } }
查看报错行代码,使用了applicationcontext.getbean的方式获取bean使得内部调用事务注解生效,但在获取bean时报错nosuchbeandefinitionexception异常,报错如下:
排除了包扫描和beanname问题,看上述报错倒数第四行报错:
org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:215)
由于使用了事物注解(aop)从而使用了动态代理,该环境使用的是jdk的动态代理机制,那么代理不会继承而是使用实现接口。
所以这个bean是通过接口注入的,就无法通过接口的实例与bean的实例关联。
ps:
- springboot 1.x aop默认还是使用 jdk 动态代理的
- 如果目标对象实现了接口默认采用jdk动态代理 (可强制改为cglib)
- 如果目标对象没有实现接口默认采用cglib动态代理
解决方案
- 升级springboot版本为2.x,aop默认使用cglib实现。
- properties配置文件增加:spring.aop.proxy-target-class=true ## 强制使用cglib代理
- 使用applicationcontext.getbean的重载方法获取对应的bean
修复代码
为了探求transactional注解是否生效,现增加aop 切面类模拟事物。
@aspect @configuration public class aopconfig { /** * 增强transactionalmethod方法 模拟transational注解 */ @pointcut("execution(* com.example.springbootdemo.error.test.errorbeanserviceimpl.transactionalmethod())") public void executeadvice() { } /** * 环绕增强 */ @around("executeadvice()") public object aroundadvice(proceedingjoinpoint thisjoinpoint) throws throwable { system.out.println("aroundadvice before proceed"); object obj = thisjoinpoint.proceed(); system.out.println("aroundadvice after proceed"); return obj; } }
去除事务注解:
@service("errorbeanserviceimpl") public class errorbeanserviceimpl implements errorbeanservice{ private applicationcontext applicationcontext; @autowired public void setapplicationcontext(applicationcontext applicationcontext){ this.applicationcontext = applicationcontext; } public void transactionalmethod() { system.out.println("transactionalmethod run ~ "); } public void callmethod() { applicationcontext.getbean(errorbeanserviceimpl.class).transactionalmethod(); } }
- 升级springboot版本为2.x 略
- 配置配置文件 略
- 修改callmethod方法使用getbean的重载方法
<t> t getbean(string var1, class<t> var2) throws beansexception;
代码如下:
public void callmethod() { // applicationcontext.getbean(errorbeanserviceimpl.class).transactionalmethod(); applicationcontext.getbean("errorbeanserviceimpl", errorbeanservice.class).transactionalmethod(); }
上述三种方式最终输出结果:
aroundadvice before proceed
transactionalmethod run ~
aroundadvice after proceed
体悟:八股文还是有用的~
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论