数据源是在什么时候注入的
ibatis中 Dao实现类都需要继承SqlMapClientDaoSupport,如下图中,SqlMapClientDaoSupport 中注入的 DataSource

spring留下的拓展点

//key和数据库连接的映射关系 private Map<Object, Object> targetDataSources; // 标识默认的连接 private Object defaultTargetDataSource; // 这个数据结构是通过targetDataSources构建而来,存储结构也是数据库标识和数据源的映射关系 private Map<Object, DataSource> resolvedDataSources; XML 配置 <bean id= "dataSource" class = "com.xxx.DynamicDataSource" > <property name= "targetDataSources" > <map key-type= "java.lang.String" > <entry key= "master" value-ref= "masterDataSource" /> <entry key= "test" value-ref= "testDataSource" /> <entry key= "official" value-ref= "officialDataSource" /> </map> </property> <property name= "defaultTargetDataSource" ref= "masterDataSource" /> </bean> |
com.xxx.DynamicDataSource是继承了AbstractRoutingDataSource类
而AbstractRoutingDataSource实现了InitializingBean接口,并实现了afterPropertiesSet方法。afterPropertiesSet方法是初始化bean的时候执行,可以针对某个具体的bean进行执行。
@Override public void afterPropertiesSet() { if ( this .targetDataSources == null ) { throw new IllegalArgumentException( "Property 'targetDataSources' is required" ); } this .resolvedDataSources = new HashMap<Object, DataSource> ( this .targetDataSources.size()); //初始化resolvedDataSources //循环targetDataSources,并添加到resolvedDataSources中 for (Map.Entry<Object, Object> entry : this .targetDataSources.entrySet()) { Object lookupKey = resolveSpecifiedLookupKey(entry.getKey()); DataSource dataSource = resolveSpecifiedDataSource(entry.getValue()); this .resolvedDataSources.put(lookupKey, dataSource); } //如果默认数据源不为空则指定对应数据源 if ( this .defaultTargetDataSource != null ) { this .resolvedDefaultDataSource = resolveSpecifiedDataSource( this .defaultTargetDataSource); } } |
这里的targetDataSources属性(map)是存储将要切换的多数据源bean信息。
而如果数据源在数据库中,则需要重写方法determineCurrentLookupKey(),因为数据源bean是动态生成的,然后需要添加到targetDataSources中,此时需要调用afterPropertiesSet()方法,来通知spring有bean更新。
因为此抽象类中都是引用resolvedDataSources属性,所以在此方法中将targetDataSources属性的键值信息存储到resolvedDataSources属性中,以便后续调用。
3.连接数据库的getConnection()方法,调用的是determineTargetDataSource()方法,来创建连接。
@Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); } |
而determineTargetDataSource()方法是决定spring容器连接那个数据源
protected DataSource determineTargetDataSource() { Assert.notNull( this .resolvedDataSources, "DataSource router not initialized" ); //具体选择哪个数据源 Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this .resolvedDataSources.get(lookupKey); if (dataSource == null && ( this .lenientFallback || lookupKey == null )) { dataSource = this .resolvedDefaultDataSource; } if (dataSource == null ) { throw new IllegalStateException( "Cannot determine target DataSource for lookup key [" + lookupKey + "]" ); } return dataSource; } |
而选择哪个数据源又是由determineCurrentLookupKey()方法来决定的,此方法是抽象方法,需要我们继承AbstractRoutingDataSource抽象类来重写此方法。该方法返回一个key,该key是bean中的beanName,并赋值给lookupKey,由此key可以通过resolvedDataSources属性的键来获取对应的DataSource值,从而达到数据源切换的功能。
protected abstract Object determineCurrentLookupKey(); |