Druid-spring-boot-starter源码阅读-DataSource自动配置实现

最近在看Druid-spring-boot-starter模块,简单记录下。

我看的源码版本是1.2.20。

为啥有两个模块

可以看到项目里面有两个spring-boot-starter。而且看着大部分是一样的,为啥会有两个呢?

看着是为了兼容spring-boot 3.0及以上版本,因为在springboot3.0之后,自动配置的加载方式有了变化,3.0之前是spring.factories,3.0之后变成了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurtoation.imports里,所以需要一个单独的模块去支持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的库,会直接用spring.datasource设置的jdbc属性。然后初始化Druid。最后引入了一些配置,包括AOP,监控页面,web-stat-filter配置,其他filter配置。

具体哪些值是可配的可以参考官方文档或者直接查看DruidStatProperties类的属性。

然后里面的会创建一个DruidDataSourceWrapper,注意这个类是同时实现了InitializingBean接口和initMethod方法的,但是会先执行afterPropertiesSet在执行init方法。

整个类实现很简单:

@ConfigurationProperties("spring.datasource.druid")
public class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {
    @Autowired
    private DataSourceProperties basicProperties;

    @Override
    public void afterPropertiesSet() throws Exception {
        //if not found prefix 'spring.datasource.druid' jdbc properties ,'spring.datasource' 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()}.
     * 

* 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; } } }

初看可能觉得比较奇怪,有几个问题

  1. 这里面没有init方法,它执行的什么呢?
  2. 这里面完全没有参数赋值,它那么多参数怎么赋值的呢?

答案都是在父类DruidDataSource里,它里面的init方法会被调用,且因为@ConfigurationProperties("spring.datasource.druid")注解,所有符合条件的参数都会被赋值,比如spring.datasource.druid.userName,就会赋值给DruidDataSource的userName属性,所以注释里才会有所谓的如果没有spring.datasource.druid会使用spring.datasource的说法。

总结

可以看到整个自动配置还是比较简单的,核心是在spring加载其他DataSource之前创建DruidDataSource并完成初始化。

你可能感兴趣的:(源码阅读,Druid,笔记,数据库,java,spring)