前言
最近我们这边项目做了技术一些升级,之前使用的clickhouse查询数据,现在改成了doris。
目前项目改造完顺利上线了,当时为了考虑一个过渡阶段,访问clickhouse数据库的老代码在项目中没有删除。
但领导考虑到成本问题,本周要把clickhouse服务停了,回收服务器节点。
现在项目面临的问题是:直接启动SpringBoot项目会报无法连接clickhouse数据库的异常。
接下来的这篇文章,就是探讨一下,如何解决这个问题,SpringBoot项目如何屏蔽指定DataSource?
1. 屏蔽ClickHouse数据源配置
既然现在无法连接ClickHouse数据库,第一个想到的是屏蔽ClickHouse数据源配置。
例如,这样的配置:
spring.datasource.clickhouse.url=jdbc:clickhouse://clickhouse.susan.net:9030/test_db?characterEncoding=utf-8
spring.datasource.clickhouse.username=test
spring.datasource.clickhouse.password=123456直接把application.properties配置文件中访问clickhouse数据库的配置,屏蔽了即可。
重新启动之后,发现服务能够正常启动。
太顺利了。
不过后来发现高兴太早了。
在服务的日志中查到一个NullPointerException异常。
DataSourceHealthIndicator类在spring-boot-actuatorjar包下面,主要是为了做健康检查的。
检查发现了数据源为null时,会打印这些异常信息。
2. 关闭健康检查
我看了一下DataSourceHealthIndicator类的源代码,一直往上追溯到了AbstractHealthIndicator类的health方法,这个方法中调用的健康检查方法:

其中doHealthCheck方法是真正做健康检查的方法。这里使用try/catch,捕获了doHealthCheck方法的所有异常。
因此,可以看出即使健康检查失败了,也不影响服务的正常启动。
上面打印的异常日志,其实只是健康检查的日志,对正常业务没有影响,能否把ClickHouse相关的健康检查关闭呢?
很快查到通过下面命令:
management.health.elasticsearch.enabled=false可以关闭elasticsearch的健康检查。
再查了一下,配置名称中的elasticsearch换成:db、redis可以关闭相应的配置,但就是没有关闭clickhouse的配置。
自己尝试了一下改成这样的配置:
management.health.clickhouse.enabled=false发现果然不行。
改成下面的配置:
management.health.db.enabled=false竟然起作用了,服务重启之后,没有打印异常日志了。
但有个问题:目前mysql、clickhouse和Doris,都是用的阿里的DuridDataSource创建的数据源,使用db关闭健康检查,不光关闭了clickhouse的,把mysql和Doris也一起关闭了,这就不行了。
后来,查了一些资料,说要继承DataSourceHealthContributorAutoConfiguration类,重写createIndicator方法。
这样搞,就有点麻烦了,看看还有没有其他办法。
3. 屏蔽ClickHouse的Configuration类
上面的健康检查,之所以去检查了ClickHouse的数据源,是因为在代码中有个ClickHouseConfiguration类,在它里面配置了DataSource和SqlSessionFactory的bean实例。
@MapperScan(basePackages="net.susan.clickhousemappers",sqlSessionFactoryRef="sqlSessionFactory")
public class ClickHouseDataSourceConfiguration{
@ConfigurationProperties(prefix="spring.datasource.clickhouse")
public DataSourcegetDataSource(){
return new DruidDataSource();
}
public SqlSessionFactory sqlSessionFactory(DataSourcedataSource){
SqlSessionFactory Beanbean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
ResourcePatternResolverrpr=newPathMatchingResourcePatternResolver();
bean.setConfigLocation(rpr.getResource("classpath:/mybatis-config.xml"));
bean.setMapperLocations(rpr.getResource("classpath:net/susan/clickhousemappers/*/*.xml"))
return bean.getObject();
}
}这个类同时指定了扫描的mapper目录,已经xml文件的路径。
我们将这个ClickHouseConfiguration类屏蔽掉不就OK了?
重新项目之后发现,报了另外一个异常:The injection point has the following annotations:
-@org.springframework.beans.factory.annotation.Autowired(required=true)项目直接启动失败了。
有些Mapper类,通过@Autowired注解注入到了Service类当中。
@Service
public class UserService{
@Autowired
private UserMapper userMapper;
}但现在的问题是,@Autowired注解注入的bean,没有在实例化,所有Spring在依赖注入的时候失败了。
那么,如何解决依赖注入的问题?
4. 如何解决依赖注入的问题?
出现上面这个问题的根本原因是,Spring容器在启动的时候,会给那些非lazy的bean,做实例化、初始化以及依赖注入的工作。
而上面这个问题是由于在UserService类中,通过@Autowired注解注入了UserMapper接口,而这个UserMapper接口么有创建bean实例,所以会注入失败。
其实,ClickHouse的UserMapper相关的方法,在这个项目中,已经被替换成了另外一个doris的Mapper。所以这个UserMapper相关bean根本不需要实例化。
有没有办法不注入这个bean实例呢?
答:在@Autowired注解中其实有一个required参数,默认是true。如果在@Autowired时把它改成false,在Spring容器启动时,就可以不用依赖注入相关bean实例。
@Service
public class UserService{
@Autowired(required=false)
private User Mapper userMapper;
}果然,经过上述调整之后,解决顺利问题了。