异常信息:
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named ‘userServiceImpl’ is expected to be of type ‘com.wjw.springAop.impl.UserServiceImpl’ but was actually of type ‘com.sun.proxy.$Proxy19’
-
我们阅读一下异常信息:“org.springframework.beans.factory.BeanNotOfRequiredTypeException:名为“userServiceImpl”的Bean应为“com.ww.springAop.impl.userServiceImpl“类型,但实际为“com.sun.proxy.Proxy19”类型”,它说我们应该为这个类型,诶?怎么回事呢?我们不是给的userServiceImpl的类型吗?
-
好,我们在spring中编写aop是不是会在编写切面的类上写上这么一个注解
@Aspect
,来标注这是一个切面,对吧,OK,现在我们把这个注解去掉:
运行结果:
我切面中是写了通知的,现在通知没有了,但是程序不报异常,所以我们可以得出,问题出在动态代理上。
贴上我的demo代码
- UserService接口
/**
* 编写接口
*/
public interface UserService {
//查询用户功能
void query();
}
- UserServiceImpl实现类
/**
* 接口实现类,也就是我们的目标对象
*/
@Service
public class UserServiceImpl implements UserService {
@Override
public void query() {
System.out.println("查询用户功能执行。。。。");
}
}
- 一个日志增强的切面,这里使用了log4j 以及 slf4j 打印日志
@Slf4j
@Component
@Aspect //标注这个雷士一个切面
public class LogAspect {
@Before("execution(* com.wjw.springAop..*(..))")
public void beforeLog(){
log.info("方法前置日志log打印");
}
@AfterReturning("execution(* com.wjw.springAop..*(..))")
public void afterReturning(){
log.info("方法返回之前AfterReturning日志打印");
}
/**
* 环绕通知有点特殊,类似于我们传统的动态代理
* 需要传入一个切入点对象,由切入点对象执行目标方法,如果有返回值,则返回
* @param joinPoint
* @throws Throwable
*/
@Around("execution(* com.wjw.springAop..*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("前环绕Around");
Object proceed = joinPoint.proceed();//目标方法执行
log.info("后环绕Around");
}
@After("execution(* com.wjw.springAop..*(..))")
public void afterLog(){
log.info("方法执行完最终通知日志log打印。");
}
}
- spring配置文件
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.wjw.springAop"></context:component-scan>
<!-- 开启aop自动代理 -->
<aop:aspectj-autoproxy/>
- 测试类test
@Test
public void test() throws Exception {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring-aop.xml");
UserService userService = applicationContext.getBean("userServiceImpl",UserServiceImpl.class);
userService.query();
}
解释为什么会出现异常
- 在这里我附上两篇博客,是别人的,可以看看,也可以看我下面的解释,我整合了两位的。
- https://blog.csdn.net/qq_46129756/article/details/121944517
- (2条消息) 解决: org.springframework.beans.factory.BeanNotOfRequiredTypeException办法_BianChengNinHao的博客-CSDN博客
出现错误代码的原因主要是因为这个:
UserServiceImpl userServiceImpl = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
为什么?我们回顾一哈,spring可以动态的选择是由jdk代理还是CGLIB代理,主要就是观察该类有没有实现接口,如果实现了接口会选择jdk的动态代理,所以这里使用的是jdk的动态代理,而我之前在写动态代理的时候写过,传入Proxy.newProxyInstance()方法中有一个是传的接口,jdk提供的需要被代理类实现接口,cglib不用,所以我们之前使用cglib时没有报错,在使用jdk时报错了。
所以这里我们应该传入的是
UserService.class
,而不是其实现类的.class,修改后:UserService userService = applicationContext.getBean("userServiceImpl",UserService.class);
运行结果如下:
完整的解决方案
- 解释完原因后,其实不止上面一种解决方案,下面我提出以下几种解决方案:
- 就是上面讲过的,遵循jdk动态代理规范,将传入的类对象
UserServiceImpl.class
改为接口对象UserService.class
UserService userService = applicationContext.getBean("userServiceImpl",UserService.class);
或者,可以再getBean的方式上做手脚,根据方法的重载,只传入beanName时,返回的是Object对象,我们将这个对象向下转型成为UserService接口对象,也可以达到同样的效果
UserService userService =(UserService) applicationContext.getBean("userServiceImpl");
- 不修改代码的基础上,修改配置文件为强制使用cglib动态代理,这样spring中就不会使用jdk动态代理了
<aop:config proxy-target-class="true"/> <aop:aspectj-autoproxy/>
可以使用简化配置,一步到胃!
<aop:aspectj-autoproxy proxy-target-class="true"/>
- spring的事务中也可以开启强制cglib动态代理,当然你必须要在此之前配置好事务相关,才能使用该标签
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
以上三种任选一即可!