记一次SSM项目中配置Mybatis插件属性自动注入失败问题

 问题场景: 有个需求,简单来说就是需要对用户输入的明文信息进行加密后存储密文,而在页面展示的时候要进行明文展示。这就想到了Mybatis提供的插件功能,插件分别可以对 StatementHandler、 ParameterHandler、ResultSetHandler、Executor四种操作进行拦截。结合我们本次的需求,只需要用到ParameterHandler和ResultSetHandler两种。ParameterHandler是对SQL查询参数进行拦截处理,而ResultSetHandler是对查询结果进行拦截处理。

1、插件代码

下面给出两个插件的代码:代码进行了精简化,只保留了需要用到的Spring IOC中需要注入的service对象

@Component
@Intercepts(@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class))
public class EncrypInterceptor implements Interceptor{

    @Autowired
    private YourService yourService;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        //调用yourService提供的方法进行业务处理
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
@Component
@Intercepts(@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = Statement.class))
public class DecryInterceptor implements Interceptor{

    @Autowired
    private YourService yourService;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
       
        //调用yourService提供的方法进行业务处理

        return proceed;
    }



    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

插件定义好后,在Mybatis的配置文件mybatis-config.xml中配置好插件

<plugins>
      <plugin interceptor="com.xxx.xxx.interceptor.DecryInterceptor"></plugin>-->
      <plugin interceptor="com.xxx.xxx.interceptor.EncrypInterceptor"></plugin>-->
</plugins>

但是项目启动后,运行发现插件中注入的Service是空的,导致了NPE异常了。

2、原因分析

        Mybatis的插件虽然被声明成了@Component通过自动扫描看上去好像可以被spring容器管理,但是你可以发现你通过Spring IOC容器获取的这个bean和Mybatis的拦截器链持有的对象不是同一个。那就说明我们容器中存在了2种不同的插件对象了。一种里面的service属性为非空,而另一种里面的service属性为空。通过查看分析Spring和SqlSessionFactoryBean的源码查看插件对象的加载过程发现,当Spring IOC容器启动时会通过自动扫描把这两个插件自动载入到IOC容器中,而它们内部的Service属性也能通过IOC容器进行自动的装配, 这是里面的service属性为非空的那一类插件。而当Spring IOC加载我们配置的SqlSessionFactoryBean对象时,在它内部会加载解析mybatis-config.xml配置文件,然后生成Configuration对象,再通过Configuration对象生SqlSessionFactory对象。而在生成Configuration对象过程中,内部的插件是通过Mybatis自己去创建的,而不是从Spring IOC 容器中去获取,从而创建的插件中Service属性就为空。而我们调用的时候,真正生效的是Mybatis内部创建的创建对象而不是Spring IOC容器中的。而Mybatis内部创建的对象中service是空的,这就导致最后调用的时候发生了NPE了。

3、解决

        既然问题发生的原因找到了,那就有解决办法了。办法就是让Mybatis内部不要创建插件对象,所需的插件对象由我们从外部传给Mybatis,而传给Mybatis的插件是由Spring IOC容器管理的内部service为非空的。

       改动一:在Mybatis配置文件mybatis-config.xml注释掉插件的声明,这样在Mybatis内部就不会创建插件对象

<!--    <plugins>-->
<!--        <plugin interceptor="com.yun.hom.interceptor.DecryInterceptor"></plugin>-->
<!--        <plugin interceptor="com.yun.hom.interceptor.EncrypInterceptor"></plugin>-->
<!--    </plugins>-->

      改动二:将由Spring IOC 创建管理的插件对象配置到SqlSessionFactoryBean对象的定义中

<!-- 3.配置SqlSessionFactory对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   <!-- 注入数据库连接池 -->
   <property name="dataSource" ref="dataSource"/>
   <!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
   <property name="configLocation" value="classpath:mybatis-config.xml"/>
   <property name="mapperLocations" value="classpath*:/mapper/*.xml"/>
   <!-- 配置创建的插件 -->
   <property name="plugins" >
       <array>
            <ref bean="decryInterceptor" />
            <ref bean="encrypInterceptor"/>
       </array>
    </property>
</bean>

4、总结
        好了这个问题就解决了,主要是Mybatis内部插件和spring IOC 管理的插件的生命周期不一致(这两者不是同一个对象)的原因造成。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值