AbstractRoutingDataSource实现动态数据源切换 专题

一. 为什么要连接多数据源

springboot下使用spring-data-jpa连接数据库配置非常方便,只需要在application.properties简单的几行配置就能搞定。有些时候我们需要在一个项目里面连接多个数据库,如常见的数据库主从分离,将部分查询请求分流到只读从库里,降低主库的压力。这种时候,就不能通过简单的几行配置来搞定了;需要手动进行一些配置才行。

 

←←←←←←←←←←←← 快,点关注!

二. springboot下连接多数据源的两种方案

目前有两种方案可以解决这个问题

  1. 为每个数据源配置一套dataSource,并针对每个dataSource配置一套jpa和事务管理器。
  2. 为每个数据源配置一套dataSource,使用AbstractRoutingDataSource将所有数据源集成到一起成为动态数据源,在代码调用的时候随时切换数据源。

这两套方案都可以满足日常使用需要,各位看官可以根据个人喜好选用。

需求:系统中要实现切换数据库(业务数据库和his数据库)

说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务。我们项目是后者的模式,网上找了很多,大都是根据jpa来做多数据源解决方案,要不就是老的spring多数据源解决方案,还有的是利用aop动态切换,感觉有点小复杂,其实我只是想找一个简单的多数据支持而已,折腾了两个小时整理出来,供大家参考。

三. 配置多套entityManagerFactory

先来讲解为每个数据源配置一套dataSource,并针对每个dataSource配置一套jpa和事务管理器的方案。废话不多说,直接上代码。具体样例代码点此查看。

首先,我们要有两个数据库。在application.properties中如下配置。

#数据库通用配置spring.datasource.driverClassName=com.mysql.cj.jdbc.Driverspring.datasource.hikari.maximum-pool-size=5spring.jpa.database=MYSQLspring.jpa.hibernate.dll-auto=nonespring.jpa.show-sql=truespring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect#主库配置spring.datasource.primary.url=jdbc:mysql://wuxiaodong.mysql.rds.aliyuncs.com:3306/test_for_blog?serverTimezone=GMT%2B8spring.datasource.primary.username=test_for_blogspring.datasource.primary.password=A1b2c3d4e5#二库配置spring.datasource.secondary.url=jdbc:mysql://wuxiaodong.mysql.rds.aliyuncs.com:3306/test_for_blog2?serverTimezone=GMT%2B8spring.datasource.secondary.username=test_for_blogspring.datasource.secondary.password=A1b2c3d4e5

主库配置

@Configuration@EnableTransactionManagement@EnableJpaRepositories(entityManagerFactoryRef="entityManagerFactoryPrimary", transactionManagerRef="transactionManagerPrimary", basePackages= {"com.test.dao.primary"})public class DataSourcePrimaryConfig{ @Value("${spring.datasource.driverClassName}") private String driverClassName; @Value("${spring.datasource.hikari.maximum-pool-size}") private Integer maximumPoolSize; @Value("${spring.datasource.primary.url}") private String primaryUrl; @Value("${spring.datasource.primary.username}") private String primaryUsername; @Value("${spring.datasource.primary.password}") private String primaryPassword; /** * 主库数据源配置 * @return */ @Primary @Bean(name = "dataSourcePrimary") public DataSource dataSourcePrimary() { HikariDataSource dataSourcePrimary = new HikariDataSource(); dataSourcePrimary.setDriverClassName(driverClassName); dataSourcePrimary.setJdbcUrl(primaryUrl); dataSourcePrimary.setUsername(primaryUsername); dataSourcePrimary.setPassword(primaryPassword); dataSourcePrimary.setMaximumPoolSize(maximumPoolSize); return dataSourcePrimary; } /** * 主库jpa 实例管理器工厂配置 */ @Primary @Bean(name = "entityManagerFactoryPrimary") public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) { LocalContainerEntityManagerFactoryBean em = builder .dataSource(dataSourcePrimary .packages("com.test.model") .build(); Properties properties = new Properties(); properties.setProperty("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy"); em.setJpaProperties(properties); return em; } /** * 主库事务管理器配置 */ @Primary @Bean(name = "transactionManagerPrimary") public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(entityManagerFactoryPrimary.getObject; return txManager; }}

第二数据库配置

@Configuration@EnableTransactionManagement@EnableJpaRepositories(entityManagerFactoryRef="entityManagerFactorySecondary", transactionManagerRef="transactionManagerSecondary", basePackages= {"com.test.dao.secondary"})public class DataSourceSecondaryConfig{ @Value("${spring.datasource.driverClassName}") private String driverClassName; @Value("${spring.datasource.hikari.maximum-pool-size}") private Integer maximumPoolSize; @Value("${spring.datasource.secondary.url}") private String secondaryUrl; @Value("${spring.datasource.secondary.username}") private String secondaryUsername; @Value("${spring.datasource.secondary.password}") private String secondaryPassword; /** * 二库数据源配置 * @return */ @Bean(name = "dataSourceSecondary") public DataSource dataSourceSecondary() { HikariDataSource dataSourceSecondary = new HikariDataSource(); dataSourceSecondary.setDriverClassName(driverClassName); dataSourceSecondary.setJdbcUrl(secondaryUrl); dataSourceSecondary.setUsername(secondaryUsername); dataSourceSecondary.setPassword(secondaryPassword); dataSourceSecondary.setMaximumPoolSize(maximumPoolSize); return dataSourceSecondary; } /** * 二库jpa 实例管理器工厂配置 */ @Bean(name = "entityManagerFactorySecondary") public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary(EntityManagerFactoryBuilder builder) { LocalContainerEntityManagerFactoryBean em = builder .dataSource(dataSourceSecondary .packages("com.test.model") .build(); Properties properties = new Properties(); properties.setProperty("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy"); em.setJpaProperties(properties); return em; } /** * 二库事务管理器配置 */ @Bean(name = "transactionManagerSecondary") public PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(entityManagerFactorySecondary.getObject; return txManager; }}

大部分代码相信各位看官一眼就能看明白,不过有几个关键单还是要额外说明下。

@EnableJpaRepositories(entityManagerFactoryRef="entityManagerFactoryPrimary", transactionManagerRef="transactionManagerPrimary", basePackages= {"com.test.dao.primary"})

这里是指定使用我们自定义的jpa实体管理工厂entityManagerFactoryPrimary,事务管理器transactionManagerPrimary来自定义jpa实现。这个jpa只扫描com.test.dao.primary这个包下的Repository。也就是com.test.dao.primary这个包下面的JpaRepository使用我们在配置文件中定义的主库。

主库配置中,我们定义的几个bean都加上了@Primary注解;而二库的配置中,并没有加上。这是因为spring中,dataSourceentityManagerFactorytransactionManager这几个如果初始化的时候实例化了多个,项目直接无法启动,会给出如下这样的提示

Parameter 0 of constructor in org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration required a single bean, but 2 were found: - dataSourcePrimary: defined by method 'dataSourcePrimary' in class path resource [com/test/config/DataSourcePrimaryConfig.class] - dataSourceSecondary: defined by method 'dataSourceSecondary' in class path resource [com/test/config/DataSourceSecondaryConfig.class]Action:Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

但是如果你把主库和二库都加上了@Primary,又会给出这样的错误提示。

No qualifying bean of type 'javax.sql.DataSource' available: more than one 'primary' bean found among candidates: [dataSourcePrimary, dataSourceSecondary]

dataSourceentityManagerFactorytransactionManager这几个如果必须实例化多个的话,必须使用使用@Primary指定其中一个为默认值。一般,建议使用主库为默认数据源。

当我们需要使用事务的时候,单数据源的时候,是直接使用@Transactional。但是因为我们配置了多数据源,然后配置主库事务管理器的时候,加上了@Primary的将其指定为默认事务管理器。所有这时候使用@Transactional其实是开启了主库的事务,如果你这时候试图对二库进行事务管理,会发现完全不会生效。如果你希望对二库进行事务管理,需要指定使用二库的事务管理器@Transactional(transactionManager="transactionManagerSecondary")。代码如下

@Transactional public void updateTestTable1() { TestTable testTable = new TestTable(); testTable.setName; testTable.setStatus; testTablePrimaryRepository.save(testTable); testTable = testTablePrimaryRepository.findOne; testTable.setName; testTablePrimaryRepository.save(testTable); } @Transactional(transactionManager="transactionManagerSecondary") public void updateTestTable2() { TestTable testTable = new TestTable(); testTable.setName; testTable.setStatus; testTableSecondaryRepository.save(testTable); testTable = testTableSecondaryRepository.findOne; testTable.setName; testTableSecondaryRepository.save(testTable); }

网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的

废话不多说直接上代码吧

四. 使用AbstractRoutingDataSource实现动态数据源切换

下面来讲解动态数据源的方案。废话不多说,继续上代码。具体样例代码点此查看。

public class DBContextHolder { /** * 动态数据源key holder */ private static ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static final String DB_TYPE_PRIMARY = "dataSourceKeyPrimary"; public static final String DB_TYPE_SECONDARY = "dataSourceKeySecondary"; public static String getDbType() { String db = contextHolder.get(); if (db == null) { db = DB_TYPE_PRIMARY;// 默认是主库 } return db; } /** * 设置本线程的dbtype */ public static void setDbType(String str) { contextHolder.set; } /** * 清理连接类型 */ public static void clearDBType() { contextHolder.remove(); }}

public class DynamicDataSource extends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey() { String dbType = DBContextHolder.getDbType(); return dbType; }}

@Configurationpublic class DataSourceConfig{ @Value("${spring.datasource.driverClassName}") private String driverClassName; @Value("${spring.datasource.hikari.maximum-pool-size}") private Integer maximumPoolSize; @Value("${spring.datasource.primary.url}") private String primaryUrl; @Value("${spring.datasource.primary.username}") private String primaryUsername; @Value("${spring.datasource.primary.password}") private String primaryPassword; @Value("${spring.datasource.secondary.url}") private String secondaryUrl; @Value("${spring.datasource.secondary.username}") private String secondaryUsername; @Value("${spring.datasource.secondary.password}") private String secondaryPassword; @Bean(name = "dataSource") public DataSource dynamicDataSource() { //配置主库数据源 HikariDataSource dataSourcePrimary = new HikariDataSource(); dataSourcePrimary.setDriverClassName(driverClassName); dataSourcePrimary.setJdbcUrl(primaryUrl); dataSourcePrimary.setUsername(primaryUsername); dataSourcePrimary.setPassword(primaryPassword); dataSourcePrimary.setMaximumPoolSize(maximumPoolSize); //配置二库数据源 HikariDataSource dataSourceSecondary = new HikariDataSource(); dataSourceSecondary.setDriverClassName(driverClassName); dataSourceSecondary.setJdbcUrl(secondaryUrl); dataSourceSecondary.setUsername(secondaryUsername); dataSourceSecondary.setPassword(secondaryPassword); dataSourceSecondary.setMaximumPoolSize(maximumPoolSize); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("dataSourceKeyPrimary", dataSourcePrimary); targetDataSources.put("dataSourceKeySecondary", dataSourceSecondary); //配置动态数据源 DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources); return dynamicDataSource; }}

下面讲解一下重要的代码。动态数据源方案的核心是spring的抽象类AbstractRoutingDataSource。将多个数据源配置到自定义的动态数据源中

 Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("dataSourceKeyPrimary", dataSourcePrimary); targetDataSources.put("dataSourceKeySecondary", dataSourceSecondary); //配置动态数据源 DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources);

在动态数据源类中,要重写一个方法,来告诉每次调用动态数据源的时候,使用哪个key对应的数据源。

@Override protected Object determineCurrentLookupKey() { String dbType = DBContextHolder.getDbType(); return dbType; }

DBContextHolder中,我们使用ThreadLocal来针对每个线程使用哪个数据源来进行控制。默认使用主库的数据源。如果需要进行切换,如下代码进行切换。

 public List<TestTable> getTestTables2() { //切换数据源至二库 DBContextHolder.setDbType(DBContextHolder.DB_TYPE_SECONDARY); List<TestTable> testTables = testTableRepository.findAll(); return testTables; }

这套动态数据源切换方案,在使用jdbcTemplateMybatis的时候非常好用;但是配合jpa的时候,却发现个问题。jpa在一个线程中拿过一个数据源后,后续使用就一直用那个数据源,即使你加上切换数据源的代码要求切换,但因为jpa根本就没有走动态数据源获取第二次,所以根本切换不了。

 public List<TestTable> getTestTables3() { //拿到默认数据源,即主库数据源 List<TestTable> testTables1 = testTableRepository.findAll(); //要求切换到二库数据源 DBContextHolder.setDbType(DBContextHolder.DB_TYPE_SECONDARY); //因为jpa只拿一次数据源,所以这里依然沿用上一个数据源,即主库数据源 List<TestTable> testTables2 = testTableRepository.findAll(); List<TestTable> testTables = new ArrayList<>(); testTables.addAll(testTables1); testTables.addAll(testTables2); return testTables; }

五.
两套方案各自的应用场景动态数据源方案,配置完成后,只需要简单加上一行代码就可以随意切换使用哪个数据源,不需要对原有代码结构进行很大的变动。为优先考虑方案。但因为在jpahibernate中,框架帮我们做了很多事,有时候数据源并不能自由的切换。所以,建议如下:

  • jdbcTemplatemybaits框架下,推荐使用动态数据源方案。
  • jpahibernate框架下,推荐使用配置多套entityManagerFactory的方案。

在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介,
能有在运行时,
根据某种key值来动态切换到真正的DataSource上。

pom包就不贴了比较简单该依赖的就依赖,主要是数据库这边的配置:

   
 Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。

mybatis.config-locations=classpath:mybatis/mybatis-config.xmlspring.datasource.test1.driverClassName = com.mysql.jdbc.Driverspring.datasource.test1.url = jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8spring.datasource.test1.username = rootspring.datasource.test1.password = rootspring.datasource.test2.driverClassName = com.mysql.jdbc.Driverspring.datasource.test2.url = jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8spring.datasource.test2.username = rootspring.datasource.test2.password = root

Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况: 
一是,表级上的跨数据库。即,对于不同的数据库却有相同的表(表名和表结构完全相同)。 
二是,非表级上的跨数据库。即,多个数据源不存在相同的表。 
Spring2.x的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。 
具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑

一个test1库和一个test2库,其中test1位主库,在使用的过程中必须指定主库,不然会报错。

一、原理

@Configuration@MapperScan(basePackages = "com.neo.mapper.test1", sqlSessionTemplateRef = "test1SqlSessionTemplate")public class DataSource1Config { @Bean(name = "test1DataSource") @ConfigurationProperties(prefix = "spring.datasource.test1") @Primary public DataSource testDataSource() { return DataSourceBuilder.create; } @Bean(name = "test1SqlSessionFactory") @Primary public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test1/*.xml")); return bean.getObject(); } @Bean(name = "test1TransactionManager") @Primary public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "test1SqlSessionTemplate") @Primary public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); }}

首先看下AbstractRoutingDataSource类结构,继承了AbstractDataSource:

最关键的地方就是这块了,一层一层注入,首先创建DataSource,然后创建SqlSessionFactory再创建事务,最后包装到SqlSessionTemplate中。其中需要指定分库的mapper文件地址,以及分库dao层代码

public abstract class AbstractRoutingDataSource extends org.springframework.jdbc.datasource.AbstractDataSource implements org.springframework.beans.factory.InitializingBean
@MapperScan(basePackages = "com.neo.mapper.test1", sqlSessionTemplateRef = "test1SqlSessionTemplate")

既然是AbstractDataSource,当然就是javax.sql.DataSource的子类,于是我们自然地回去看它的getConnection方法:

这块的注解就是指明了扫描dao层,并且给dao层注入指定的SqlSessionTemplate。所有@Bean都需要按照命名指定正确。

图片 1

dao层和xml需要按照库来分在不同的目录,比如:test1库dao层在com.neo.mapper.test1包下,test2库在com.neo.mapper.test1

public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
    }

    public Connection getConnection(String username, String password) throws SQLException {
        return determineTargetDataSource().getConnection(username, password);
    }
public interface User1Mapper { List<UserEntity> getAll(); UserEntity getOne; void insert(UserEntity user); void update(UserEntity user); void delete;}

图片 2

xml层

原来关键就在determineTargetDataSource()里:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="com.neo.mapper.test1.User1Mapper" > <resultMap type="com.neo.entity.UserEntity" > <id column="id" property="id" jdbcType="BIGINT" /> <result column="userName" property="userName" jdbcType="VARCHAR" /> <result column="passWord" property="passWord" jdbcType="VARCHAR" /> <result column="user_sex" property="userSex" javaType="com.neo.enums.UserSexEnum"/> <result column="nick_name" property="nickName" jdbcType="VARCHAR" /> </resultMap> <sql > id, userName, passWord, user_sex, nick_name </sql> <select resultMap="BaseResultMap" > SELECT <include ref /> FROM users </select> <select parameterType="java.lang.Long" resultMap="BaseResultMap" > SELECT <include ref /> FROM users WHERE id = #{id} </select> <insert parameterType="com.neo.entity.UserEntity" > INSERT INTO users (userName,passWord,user_sex) VALUES (#{userName}, #{passWord}, #{userSex}) </insert> <update parameterType="com.neo.entity.UserEntity" > UPDATE users SET <if test="userName != null">userName = #{userName},</if> <if test="passWord != null">passWord = #{passWord},</if> nick_name = #{nickName} WHERE id = #{id} </update> <delete parameterType="java.lang.Long" > DELETE FROM users WHERE id =#{id} </delete></mapper>
protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();//业务代码能更改这个值,就可使用指定的DB
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);//此处来获取指定的DB
        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;
    }

测试可以使用SpringBootTest,也可以放到Controller中,这里只贴Controller层的使用

 

@RestControllerpublic class UserController { @Autowired private User1Mapper user1Mapper; @Autowired private User2Mapper user2Mapper; @RequestMapping("/getUsers") public List<UserEntity> getUsers() { List<UserEntity> users=user1Mapper.getAll(); return users; } @RequestMapping("/getUser") public UserEntity getUser { UserEntity user=user2Mapper.getOne; return user; } @RequestMapping public void save(UserEntity user) { user2Mapper.insert; } @RequestMapping(value="update") public void update(UserEntity user) { user2Mapper.update; } @RequestMapping(value="/delete/{id}") public void delete(@PathVariable Long id) { user1Mapper.delete; } }

这里用到了我们需要进行实现的抽象方法determineCurrentLookupKey(),该方法返回需要使用的DataSource的key值,然后根据这个key从resolvedDataSources这个map里取出对应的DataSource,如果找不到,则用默认的resolvedDefaultDataSource。

欢迎大家加入粉丝群:963944895,群内免费分享Spring框架、Mybatis框架SpringBoot框架、SpringMVC框架、SpringCloud微服务、Dubbo框架、Redis缓存、RabbitMq消息、JVM调优、Tomcat容器、MySQL数据库教学视频及架构学习思维导图

回过头看AbstractDataSource的afterPropertiesSet方法:

图片 3

 1 public void afterPropertiesSet() {
 2         if (this.targetDataSources == null) {
 3             throw new IllegalArgumentException("Property 'targetDataSources' is required");
 4         }
 5         this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
 6         for (Map.Entry entry : this.targetDataSources.entrySet()) {   
 7             Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
 8             DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
 9             this.resolvedDataSources.put(lookupKey, dataSource);//这个值就是所有DataSource的集合
10         }
11         if (this.defaultTargetDataSource != null) {
12             this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
13         }
14     }

图片 4

 

扩展:afterPropertiesSet方法调用时机:javaconfig配置场景,打断点时显示,在@Bean所在方法执行完成后,会调用此方法

package org.springframework.beans.factory;

/**
 * Interface to be implemented by beans that need to react once all their
 * properties have been set by a BeanFactory: for example, to perform custom
 * initialization, or merely to check that all mandatory properties have been set.
 *
 * <p>An alternative to implementing InitializingBean is specifying a custom
 * init-method, for example in an XML bean definition.
 * For a list of all bean lifecycle methods, see the BeanFactory javadocs.
 *
 * @author Rod Johnson
 * @see BeanNameAware
 * @see BeanFactoryAware
 * @see BeanFactory
 * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
 * @see org.springframework.context.ApplicationContextAware
 */
public interface InitializingBean {

    /**
     * Invoked by a BeanFactory after it has set all bean properties supplied
     * (and satisfied BeanFactoryAware and ApplicationContextAware).
     * <p>This method allows the bean instance to perform initialization only
     * possible when all bean properties have been set and to throw an
     * exception in the event of misconfiguration.
     * @throws Exception in the event of misconfiguration (such
     * as failure to set an essential property) or if initialization fails.
     */
    void afterPropertiesSet() throws Exception;

}

 

 

配置数据源实例:

图片 5

    <bean id="onlineDynamicDataSource" class="com.xx.stat.base.dynamic.DynamicDataSource">
       <property name="targetDataSources">   
          <map key-type="java.lang.String">   
             <entry key="xx" value-ref="dataSourceXX"/>   
             <entry key="yy" value-ref="dataSourceYY"/>   
          </map>   
       </property>   
       <property name="defaultTargetDataSource" ref="dataSource"/>  
    </bean>

图片 6

 

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图