spring开启事务后报but was actually of type 'com.sun.proxy.$Proxy40异常

这个问题是在我的个人网站项目上发生的,一直在寻找原因,排查了很多可能,都没有找到问题的所在,并困扰了我半年之多。后来由于无法解决,该问题便被搁置。最近由于网站将要做完,准备上线,便再次研究该问题,困扰半年的问题终于被解决。

以下(只贴出重点代码和配置):

框架:spring-4.3+springMVC-4.3+MyBatis+shrio+maven

JDK:1.8

服务器:tomcat8

数据库:MySql5.7

TestController:

@Controller
public class TestController {

    @Autowired
    private TestService testService;

    @RequestMapping("/tx/test1")
    @ResponseBody
    public String test(){
        return testService.test1();
    }
}

TestService:

public class TestService {

    @Autowired
    private TestDao testDao;

    @Transactional(rollbackFor = Exception.class)
    public String test1(){
        return "hello tx!";
    }
}

spring-context.xml

<aop:aspectj-autoproxy proxy-target-class="true"/>

    <context:component-scan base-package="com.xiaohe66.web.**.service"/>
    <context:component-scan base-package="com.xiaohe66.web.**.controller"/>
    <context:component-scan base-package="com.xiaohe66.web.aop"/>
    <context:component-scan base-package="com.xiaohe66.web.spring"/>

    <!-- 开启注解 -->
    <context:annotation-config/>
    <!-- 导入配置 -->
    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="springUtils" class="com.xiaohe66.web.common.util.SpringUtils"/>

    <import resource="spring-mybatis.xml"/>
    <import resource="spring-shrio.xml"/>

spring-mabatis.xml

<!-- 配置数据源 -->
    <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${datasource.driverClass}"/>
        <property name="jdbcUrl" value="${datasource.url}"/>
        <property name="user" value="${datasource.user}"/>
        <property name="password" value="${datasource.password}"/>
    </bean>

    <!-- 开启注解事务 -->
    <!--<tx:annotation-driven proxy-target-class="true" mode="aspectj"/>-->
    <tx:annotation-driven proxy-target-class="true"/>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>


    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBaties全局配置文件:mybaconfnfig.xml -->
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
        <!--
        给包中的类注册别名,注册后可以直接使用类名
        属性可以配置多个,可以用,;\t\n进行分割。但是不支持Ant风格的路径。如:com.xiaohe66.web.**.po
         -->
        <property name="typeAliasesPackage" value="com.xiaohe66.web.comm.po
                                                    com.xiaohe66.web.org.po
                                                    com.xiaohe66.web.security.po
                                                    com.xiaohe66.web.sys.po
                                                    com.xiaohe66.web.text.po
                                                    com.xiaohe66.web.home.po" />
        <!-- 自动扫描mapper目录 -->
        <property name="mapperLocations" value="classpath:com/xiaohe66/web/mapper/**/*.xml"/>
    </bean>

    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory" />
    </bean>

    <!--自动扫描dao接口,并注入sqlsession-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.xiaohe66.web.**.dao"/>
        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
    </bean>

spring-shrio.xml

<!--  自定义Realm -->
    <bean id="realmService" class="com.xiaohe66.web.security.RealmService"/>

    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="realmService"/>
    </bean>

    <!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- 开启shiro注解 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

错误信息(重点部分):

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testController': 
Unsatisfied dependency expressed through field 'testService'; 
nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: 
Bean named 'testService' is expected to be of type 'com.xiaohe66.web.security.service.TestService' 
but was actually of type 'com.sun.proxy.$Proxy40'

---------------------------分割线----------------------------------------------------------

根据错误信息,我查阅了各方资料,知道了这个问题是由于动态代理使用了标准jdk的基于接口的代理方式

而我的controller和service是没有实现任何接口的,因此我是需要使用基于类的代理方式的。

检查了自己的配置:

1.开启了注解

<context:annotation-config/>

2.开启了aop,加上了proxy-target-class="true"

<aop:aspectj-autoproxy proxy-target-class="true"/>

3.开启了事务,加上了proxy-target-class="true"

<tx:annotation-driven proxy-target-class="true"/>

4.加入了cglib包(图片是maven打包后的war包)



结果发现,该有的配置都有,该加的包有加,百度谷歌都查吐了。愣是没有解决,而万恶的错误,还是一直存在。

--------------------------------------分割线--------------------------------------------

快要绝望的我,开始觉得会不会是spring的导入“<import resource="spring-mybatis.xml"/>”这种方式的问题。

于是我就将spirng-mybatis.xml里面的配置信息直接放到spring-context.xml中,并注释掉了导入的配置,如下

spring-context.xml:

<!--<import resource="spring-mybatis.xml"/>-->
    <!--<import resource="spring-shrio.xml"/>--> 

然后启动


Wath????启动了???????????没报错???????

访问一下



可以访问了,项目启动成功了。我开心的要跳起来了。但事情并没有想象中的那么简单。

--------------------------分割线--------------------------------------

项目启动成功后,测试了一下,事务成功的被应用了,抛出异常后,事务成功回滚。具体代码和过程就不说了。

然后,我再将spring-shrio.xml中的配置文件,也放到spring-context.xml中。启动

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testController': 
Unsatisfied dependency expressed through field 'testService'; 
nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: 
Bean named 'testService' is expected to be of type 'com.xiaohe66.web.security.service.TestService' 
but was actually of type 'com.sun.proxy.$Proxy40'

是的,我又看到了那段该死的错误。wath?什么情况?难道不是导入配置这种写法的问题?

然后我将spring-mybatis.xml中的配置还原,并删除掉spring-context.xml中的配置。然后注释掉spring-shrio.xml的相关配置。

启动。

结果是成功启动了。wath?是shrio的配置导致的问题?开始上网查找:shiro导致无法开启事务。

然后在知乎上看到这篇文章https://zhuanlan.zhihu.com/p/29161098,其中重点的一句话为:

bean被多次代理的时候,jdk代理是基于接口的,所以最后这个bean的类型变成了代理接口proxy的类型

然后,文章中也说明了问题的原因:




按文章所说的,我删除掉shrio中的该bean的配置后,项目果然能够成功启动。试了一下,事务管理有效。至此,困扰我半年多的问题终于被成功解决。

---------------------------------------------------------------------------------------

总结:

问题原因:bean的多次代理导致bean的类型变成了代理接口proxy的类型

解决:删除掉多余的代理,只保留一个

感想:很多事情,也许并不是表面的那样,要看清更深层次的原因。


参考:https://zhuanlan.zhihu.com/p/29161098


  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值