最近在看Druid-spring-boot-starter模块,简单记录下。
我看的源码版本是1.2.20。
为啥有两个模块
可以看到项目里面有两个spring-boot-starter。而且看着大部分是一样的,为啥会有两个呢?
看着是为了兼容spring-boot 3.0及以上版本,因为在springboot3.0之后,自动配置的加载方式有了变化,3.0之前是,3.0之后变成了
META-INF/spring/
里,所以需要一个单独的模块去支持springboot3.0之后的加载方式,看github上的issue好像还有一些其他特性需要兼容,所以分了两个模块。
自动配置流程
我们这里以druid-spring-boot-3-starter为例,从配置文件可以看到入口在DruidDataSourceAutoConfigure这个类:
@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class,
DruidStatViewServletConfiguration.class,
DruidWebStatFilterConfiguration.class,
DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);
@Bean(initMethod = "init")
@ConditionalOnMissingBean
public DataSource dataSource() {
LOGGER.info("Init DruidDataSource");
return new DruidDataSourceWrapper();
}
}
它的实现其实比较简单,从上往下看,首先你要导入了Druid相关的包才会进行自动配置,然后它是DataSource自动装配之前,Druid是构件在数据源之上的一层封装,需要在装配数据源之前初始化完成。读取两个属性配置类DruidStatProperties和DataSourceProperties,后者是前者的补充,如果没有设置任何druid相关的参数,但是引入了druid的库,会直接用设置的jdbc属性。然后初始化Druid。最后引入了一些配置,包括AOP,监控页面,web-stat-filter配置,其他filter配置。
具体哪些值是可配的可以参考官方文档或者直接查看DruidStatProperties类的属性。
然后里面的会创建一个DruidDataSourceWrapper,注意这个类是同时实现了InitializingBean接口和initMethod方法的,但是会先执行afterPropertiesSet在执行init方法。
整个类实现很简单:
@ConfigurationProperties("")
public class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {
@Autowired
private DataSourceProperties basicProperties;
@Override
public void afterPropertiesSet() throws Exception {
//if not found prefix '' jdbc properties ,'' prefix jdbc properties will be used.
if (super.getUsername() == null) {
super.setUsername(basicProperties.determineUsername());
}
if (super.getPassword() == null) {
super.setPassword(basicProperties.determinePassword());
}
if (super.getUrl() == null) {
super.setUrl(basicProperties.determineUrl());
}
if (super.getDriverClassName() == null) {
super.setDriverClassName(basicProperties.getDriverClassName());
}
}
@Autowired(required = false)
public void autoAddFilters(List<Filter> filters) {
super.filters.addAll(filters);
}
/**
* Ignore the 'maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis' validate,
* it will be validated again in {@link DruidDataSource#init()}.
* <p>
* for fix issue #3084, #2763
*
* @since 1.1.14
*/
@Override
public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {
try {
super.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
} catch (IllegalArgumentException ignore) {
super.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
}
}
}
初看可能觉得比较奇怪,有几个问题
- 这里面没有init方法,它执行的什么呢?
- 这里面完全没有参数赋值,它那么多参数怎么赋值的呢?
答案都是在父类DruidDataSource里,它里面的init方法会被调用,且因为@ConfigurationProperties("")
注解,所有符合条件的参数都会被赋值,比如,就会赋值给DruidDataSource的userName属性,所以注释里才会有所谓的如果没有会使用的说法。
总结
可以看到整个自动配置还是比较简单的,核心是在spring加载其他DataSource之前创建DruidDataSource并完成初始化。