Spring AOP中jdk动态代理异常:org.springframework.beans.factory.BeanNotOfRequiredTypeException

异常信息:

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();
    }

解释为什么会出现异常

出现错误代码的原因主要是因为这个:

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);

运行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKBNEBCZ-1690818054447)(assets/image-20230731225943201.png)]

完整的解决方案

  • 解释完原因后,其实不止上面一种解决方案,下面我提出以下几种解决方案:
  1. 就是上面讲过的,遵循jdk动态代理规范,将传入的类对象UserServiceImpl.class改为接口对象UserService.class
UserService userService = applicationContext.getBean("userServiceImpl",UserService.class);

或者,可以再getBean的方式上做手脚,根据方法的重载,只传入beanName时,返回的是Object对象,我们将这个对象向下转型成为UserService接口对象,也可以达到同样的效果

UserService userService =(UserService) applicationContext.getBean("userServiceImpl");
  1. 不修改代码的基础上,修改配置文件为强制使用cglib动态代理,这样spring中就不会使用jdk动态代理了
<aop:config proxy-target-class="true"/>
<aop:aspectj-autoproxy/>

可以使用简化配置,一步到胃!

<aop:aspectj-autoproxy proxy-target-class="true"/>
  1. spring的事务中也可以开启强制cglib动态代理,当然你必须要在此之前配置好事务相关,才能使用该标签
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> 

以上三种任选一即可!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值