背景
因公司业务扩展,导致数据量持续增长,引发查询返回速度过慢,所以需要进行优化。在经过调研准备使用分库分表技术来解决查询速度慢的问题。
分表技术-Sharding-JDBC
是轻量级的java
框架,是增强版的JDBC驱动,该框架简化了分库分表后对数据操作的难度,使用户只需要关注业务即可
问题描述
因业务特殊性sharding-jdbc提供的分片算法不太好满足我们的分表需求,故使用提供的自定义分片算法进行分片。sharding-jdbc要求实现ComplexKeysShardingAlgorithm
这个接口进行重写分片算法,但是在实现之后发现分片算法未生效,并且没有加载自定义实现类导致不可使用。控制台日志如下:
因为这个类为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的实例化过程图示:
从流程中可以看见BeanPostProcessor
的注册是在applicationcentest生命周期内完成的,故而当bean
创建时,如果相应拦截器还没有注册,那么其就不会起作用。所以sharding-jdbc实现的自定义分片算法里面的init()
方法不生效可能就是如下原因:
- bean由于某种原因,被提前初始化了,但是相应的拦截器
BeanPostProcessor
还没有注册,所以不执行方法
BeanPostProcessor接口作用:
简单来讲,就是在实例化bean、配合bean以及其他初始化方法前后想增加一些自己的逻辑处理,就可以继承这个接口进行
在doGet()方法中debug查看被谁提前加载了
找到 AbstractBeanFactory这个对象下面的doGet()方法并且设置条件断点test-algorithm
看看是哪个小哪吒提前加载导致这个bean不生效了
定位到断点,并且向上查找看看哪个bean提前注册了
最终找到[1]里面的shiroFilter
方法导致shardingBean被提前注册了
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容器
然后我们在顺着上面找,最终找到了以下方法 发现继承AuthorizingRealm
它的对象里面引入了UserService
所以才不好用的。
解决方案
方案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 进行降级
- 使用包装bean 进行返回注册Spring 中
再次启用项目发现init()
方法执行完成。
至此问题提解决完毕
总结
经过此次问题定位与查找,可以对spring对bean的管理有一个更深刻的印象。