通过在mapper接口上添加自定数据源注解@DataSource来制定数据源
1.添加切面aspectjweaver依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
2.启动类添加切面支持注解@EnableAspectJAutoProxy(proxyTargetClass = true)
@Import({DynamicDataSourceConfig.class})
exclude = {DataSourceAutoConfiguration.class} 不加这个会报错,循环依赖问题(查了好久才解决)
import org.linlinjava.litemall.db.datasources.DynamicDataSourceConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@MapperScan("org.linlinjava.litemall.db.dao")
@EnableTransactionManagement
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableScheduling
@Import({DynamicDataSourceConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3自定义注解DataSource.java
import java.lang.annotation.*;
/**
* 多数据源注解
*
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default "";
}
数据源名称枚举
/**
* 增加多数据源,在此配置
*
*/
public interface DataSourceNames {
String LITEMALL = "mall";
String ZOOM = "zoom";
}
动态数据源配置类
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 动态数据源
*
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<String, DataSource> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(new HashMap<>(targetDataSources));
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
配置多数据源
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* 配置多数据源
*
*/
@Configuration
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.mall")
public DataSource liteMallDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.zoom")
public DataSource zoomDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource(DataSource liteMallDataSource, DataSource zoomDataSource) {
Map<String, DataSource> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceNames.LITEMALL, liteMallDataSource);
targetDataSources.put(DataSourceNames.ZOOM, zoomDataSource);
return new DynamicDataSource(liteMallDataSource, targetDataSources);
}
@Aspect
@Component
class DataSourceAspect implements Ordered {
protected Logger logger = LoggerFactory.getLogger(getClass());
public DataSourceAspect() {
System.out.println(0);
}
@Pointcut("@within(org.linlinjava.litemall.db.datasources.annotation.DataSource)")
public void dataSourcePointCut() {
System.out.println(0);
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
org.linlinjava.litemall.db.datasources.annotation.DataSource ds = null;
Class clazz = point.getTarget().getClass();
ds = (org.linlinjava.litemall.db.datasources.annotation.DataSource) clazz.getAnnotation(org.linlinjava.litemall.db.datasources.annotation.DataSource.class);
if (ds == null) {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
ds = method.getAnnotation(org.linlinjava.litemall.db.datasources.annotation.DataSource.class);
}
if (ds == null) {
DynamicDataSource.setDataSource(DataSourceNames.LITEMALL);
logger.debug("set datasource is " + DataSourceNames.LITEMALL);
} else {
DynamicDataSource.setDataSource(ds.name());
logger.debug("set datasource is " + ds.name());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
logger.debug("clean datasource");
}
}
@Override
public int getOrder() {
return 1;
}
}
}
4.Yaml配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
zoom: #数据源2
url: xxxxxx/zoom?useUnicode=true&characterEncoding=utf8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: 0000
password: 00000
filter:
slf4j:
enabled: true
mall:
url: jdbc:xxxxxx/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: 00000
password: 00000
initial-size: 10
max-active: 50
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
validation-query: SELECT 1 FROM DUAL
test-on-borrow: false
test-on-return: false
test-while-idle: true
time-between-eviction-runs-millis: 60000
webStatFilter:
enabled: true
statViewServlet:
enabled: false
filter:
stat:
enabled: false
5.测试
import org.junit.Test;
import org.junit.runner.RunWith;
import org.linlinjava.litemall.db.dao.StatMapper;
import org.linlinjava.litemall.db.dao.ZoomMapper;
import org.linlinjava.litemall.db.datasources.DynamicDataSourceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import javax.annotation.Resource;
@WebAppConfiguration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
@RunWith(SpringRunner.class)
@SpringBootTest
@Import({DynamicDataSourceConfig.class})
public class DbTest {
@Autowired
private ZoomMapper zoomMapper;
@Test
public void test() {
System.out.println(zoomMapper.queryName());
}
}
6,可能出现的异常
*************************** APPLICATION FAILED TO START ***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
zoomMapperService (field org.linlinjava.litemall.db.dao.ZoomMapper org.linlinjava.litemall.db.dao.impl.ZoomMapperService.zoomMapper) ↓ zoomMapper defined in file [F:\workcode\litemall\litemall-db\target\classes\org\linlinjava\litemall\db\dao\ZoomMapper.class] ↓ sqlSessionFactory defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class] ┌─────┐ | dataSource defined in class path resource [org/linlinjava/litemall/db/datasources/DynamicDataSourceConfig.class] ↑ ↓ | firstDataSource defined in class path resource [org/linlinjava/litemall/db/datasources/DynamicDataSourceConfig.class] ↑ ↓ | org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker
循环依赖问题
2yaml配置问题
Caused by: org.yaml.snakeyaml.parser.ParserException: while parsing a block mapping ?in 'reader', line 9, column 5: ? ? ? ? type: com.alibaba.druid.pool.Dru ...? ? ? ? ? ^ expected <block end>, but found '<block mapping start>' ?in 'reader', line 19, column 7: ? ? ? ? ? mall: ? ? ? ? ? ^
?? ?at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java:572) ?? ?at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:158) ?? ?at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java:148) ?? ?at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:214) ?? ?at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:144) ?? ?at org.yaml.snakeyaml.composer.Composer.composeValueNode(Composer.java:236) ?? ?at org.yaml.snakeyaml.composer.Composer.composeMappingChildren(Composer.java:227) ?? ?at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:215) ?? ?at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:144) ?? ?at org.yaml.snakeyaml.composer.Composer.composeValueNode(Composer.java:236) ?? ?at org.yaml.snakeyaml.composer.Composer.composeMappingChildren(Composer.java:227) ?? ?at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:215) ?? ?at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:144) ?? ?at org.yaml.snakeyaml.composer.Composer.getNode(Composer.java:85) ?? ?at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:123) ?? ?at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:547) ?? ?at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:160) ?? ?at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:134) ?? ?at org.springframework.boot.env.OriginTrackedYamlLoader.load(OriginTrackedYamlLoader.java:75) ?? ?at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:50) ?? ?at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadDocuments(ConfigFileApplicationListener.java:574) ?? ?at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:528) ?? ?... 48 common frames omitted
|