源码解析
AbstractRoutingDataSource
我们先看一下这个类里面包含了几个变量。
@Nullable
private Map<Object, Object> targetDataSources;
@Nullable
private Object defaultTargetDataSource;
private boolean lenientFallback = true;
private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
@Nullable
private Map<Object, DataSource> resolvedDataSources;
@Nullable
private DataSource resolvedDefaultDataSource;
这里我们先简单的说一下各个变量的作用:
- targetDataSources:存储未解析的目标的数据源Map
- defaultTargetDataSource: 未解析的默认的目标数据源
- lenientFallback:回退。稍后再说
- dataSourceLookup:dataSource解析器,默认是JNDI
- resolvedDataSources:将目标的数据源解析并保存成实际的数据链接后的map
- resolvedDefaultDataSource: 默认的数据源
Method afterPropertiesSet
afterPropertiesSet 是用来解析targetDataSources 来获取真正的dataSource
@Override
public void afterPropertiesSet() {
if (this.targetDataSources == null) {
throw new IllegalArgumentException("Property 'targetDataSources' is required");
}
this.resolvedDataSources = new HashMap<>(this.targetDataSources.size());
this.targetDataSources.forEach((key, value) -> {
Object lookupKey = resolveSpecifiedLookupKey(key);
DataSource dataSource = resolveSpecifiedDataSource(value);
this.resolvedDataSources.put(lookupKey, dataSource);
});
if (this.defaultTargetDataSource != null) {
this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
}
}
- 该方法会先判断targetDataSources是否为空,如果为空抛出异常。
- 新建一下HashMap,大小为targetDataSources的大小,然后赋值给resolvedDataSources
- 遍历targetDataSources,解析Key和DataSource,并存储到resolvedDataSources
- 如果设置了 defaultTargetDataSource【默认数据源】,则会将默认数据源解析后,存储到resolveDefaultDataSource
以上的流程可以看出,该方法必须在设置了targetDataSources后调用,才能生效。
所以我们在做多数据源时,有时会看到这样的代码:这是为了解析我们配置的数据源Bean。
AbstractRoutingDataSource.setTargetDataSources(Map(...))
AbstractRoutingDataSource.afterPropertiesSet();
Method resolveSpecifiedDataSource
- 当参数DataSource的类型为DataSource时候,则返回
- 如果是String时,因为默认时使用的JNDILookUp,所以这里会默认去根据JNDI解析。如果需要替换不同的datasourceLookUp,Spring内置了4种:
- BeanFactoryDataSourceLookup:在SpringBean容器里面查找dataSource。
- JndiDataSourceLookup:从JNDI数据源解析
- MapDataSourceLookup:从Map容器解析
- SingleDataSourceLookup:从单数据源直接返回
- 如果不是以上两种类型,则抛出异常信息。
/**
* Resolve the specified data source object into a DataSource instance.
* <p>The default implementation handles DataSource instances and data source
* names (to be resolved via a {@link #setDataSourceLookup DataSourceLookup}).
* @param dataSource the data source value object as specified in the
* {@link #setTargetDataSources targetDataSources} map
* @return the resolved DataSource (never {@code null})
* @throws IllegalArgumentException in case of an unsupported value type
*/
protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
if (dataSource instanceof DataSource) {
return (DataSource) dataSource;
}
else if (dataSource instanceof String) {
return this.dataSourceLookup.getDataSource((String) dataSource);
}
else {
throw new IllegalArgumentException(
"Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
}
}
protected <T> T lookup(String jndiName, @Nullable Class<T> requiredType) throws NamingException {
Assert.notNull(jndiName, "'jndiName' must not be null");
String convertedName = this.convertJndiName(jndiName);
Object jndiObject;
try {
jndiObject = this.getJndiTemplate().lookup(convertedName, requiredType);
} catch (NamingException var6) {
if (convertedName.equals(jndiName)) {
throw var6;
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Converted JNDI name [" + convertedName + "] not found - trying original name [" + jndiName + "]. " + var6);
}
jndiObject = this.getJndiTemplate().lookup(jndiName, requiredType);
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Located object with JNDI name [" + convertedName + "]");
}
return jndiObject;
}