sharding-jdbc自定义分片算法与shrio冲突导致不生效问题

背景

因公司业务扩展,导致数据量持续增长,引发查询返回速度过慢,所以需要进行优化。在经过调研准备使用分库分表技术来解决查询速度慢的问题。

分表技术-Sharding-JDBC

是轻量级的java框架,是增强版的JDBC驱动,该框架简化了分库分表后对数据操作的难度,使用户只需要关注业务即可

image.png

问题描述

因业务特殊性sharding-jdbc提供的分片算法不太好满足我们的分表需求,故使用提供的自定义分片算法进行分片。sharding-jdbc要求实现ComplexKeysShardingAlgorithm这个接口进行重写分片算法,但是在实现之后发现分片算法未生效,并且没有加载自定义实现类导致不可使用。控制台日志如下:

image.png 因为这个类为Null所以在sharding在进行查找分片算法时就找不到对应的方法载入

init方法未执行

ClassBasedShardingAlgorithm.init()方法未执行,导致complexKeysShardingAlgorithm这个对象为空

该方法在spring程序启动开始就会进行加载,但是在启动时发现,并没有执行此方法,但是在启动之后bean容器也已经创建。

查看控制台日志

在查看控制台日志的时候,发现日志打印出如下info日志

14:35:01.505 [main] INFO  o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - [postProcessAfterInitialization,330] - Bean 'test-algorithm' of type [org.apache.shardingsphere.sharding.algorithm.sharding.classbased.ClassBasedShardingAlgorithm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

意思大概是 不符合被所有 BeanPostProcessor 处理的条件(例如:不符合自动代理的条件)

也就是说bean由于某种原因,被提前初始化了,初始化的时候相应拦截器beanpostProcessor还没有注册,所以就没有执行对应处理方法

查到这里就涉及到spring源码系列以及bean的加载周期了。

bean生命周期

spring中Bean的实例化过程图示:

image.png 从流程中可以看见BeanPostProcessor的注册是在applicationcentest生命周期内完成的,故而当bean创建时,如果相应拦截器还没有注册,那么其就不会起作用。所以sharding-jdbc实现的自定义分片算法里面的init()方法不生效可能就是如下原因:

  • bean由于某种原因,被提前初始化了,但是相应的拦截器BeanPostProcessor还没有注册,所以不执行方法

BeanPostProcessor接口作用:

简单来讲,就是在实例化bean、配合bean以及其他初始化方法前后想增加一些自己的逻辑处理,就可以继承这个接口进行

在doGet()方法中debug查看被谁提前加载了

找到 AbstractBeanFactory这个对象下面的doGet()方法并且设置条件断点test-algorithm看看是哪个小哪吒提前加载导致这个bean不生效了

image.png 定位到断点,并且向上查找看看哪个bean提前注册了

image.png 最终找到[1]里面的shiroFilter方法导致shardingBean被提前注册了

image.png test-algorithm执行顺序如下

test-algorithm
    org.apache.shardingsphere.sharding.spring.boot.ShardingRuleSpringBootConfiguration
         org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
             com.sinowel.assistant.assistantsystem.service.impl.UserServiceImpl
                 com.sinowel.assistant.assistantsystem.service.UserService
                     com.sinowel.assistant.assistantsystem.config.ShiroConfig
                     

这样bean的加载就清晰明了,也就是说因为shiroConfig里面的某个配置导致被提前加载。我们在往上找,发现ShiroRealm这个bean,那从debug分析,也就是说,在shiroRealm这个实现类里,注入了UserService这个对象,所以导致实现的shrding-jdbc的BeanPostProcessor被加载.

在shirFilter中有一个ShiroFilterFactoryBean对象,这个对象实际继承了BeanPostProcessor它,所以在初始化的时候就会加载一系列被依赖的Bean容器

image.png

image.png 然后我们在顺着上面找,最终找到了以下方法 image.png 发现继承AuthorizingRealm它的对象里面引入了UserService所以才不好用的。

image.png

解决方案

方案1

EnceladusShiroRealm方法中手动获取bean使用springContext.getBean()方式获取,这样在加载时,就可以了。

方案2

  • 所有shiro 相关的filter 都在ShiroFilterFactoryBean.setFilterChainDefinitionMap 中new 出来,
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.FactoryBean;

public class MyShiroFilterFactoryBean implements FactoryBean<AbstractShiroFilter> {
    private final ShiroFilterFactoryBean shiroFilterFactoryBean;

    public MyShiroFilterFactoryBean(ShiroFilterFactoryBean shiroFilterFactoryBean) {
        this.shiroFilterFactoryBean = shiroFilterFactoryBean;
    }

    public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
        return shiroFilterFactoryBean;
    }

    @Override
    public AbstractShiroFilter getObject() throws Exception {
        return (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
    }

    @Override
    public Class<?> getObjectType() {
        return shiroFilterFactoryBean.getObjectType();
    }

    @Override
    public boolean isSingleton() {
        return shiroFilterFactoryBean.isSingleton();
    }
}
  • ShiroFilterFactoryBean 进行封装,舍弃掉BeanPostProcessor相关方法,封装bean 为普通Spring bean 对ShiroFilterFactoryBean 进行降级

image.png

  • 使用包装bean 进行返回注册Spring 中

再次启用项目发现init()方法执行完成。

image.png 至此问题提解决完毕

总结

经过此次问题定位与查找,可以对spring对bean的管理有一个更深刻的印象。

猜你喜欢

转载自juejin.im/post/7120517655181656078
今日推荐