- Dependências Principias
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
- Configuração do application.yml
# Configuração de fontes de dados
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
druid:
# Fonte de dados primária
principal:
url: jdbc:mysql://127.0.0.1:3306/principal?characterEncoding=UTF-8
username: root
password: root
# Fonte de dados secundária
secundaria:
enabled : true
url: jdbc:mysql://127.0.0.1:3306/secundaria?characterEncoding=UTF-8
username: root
password: root
# Número inicial de conexões
initial-size: 10
# Número máximo de conexões no pool
max-active: 100
# Número mínimo de conexões no pool
min-idle: 10
# Tempo máximo de espera por uma conexão em milissegundos
max-wait: 60000
# Habilitar PSCache e definir o tamanho para cada conexão
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# Intervalo para verificação de conexões ociosas em milissegundos
timeBetweenEvictionRunsMillis: 60000
# Tempo mínimo de vida de uma conexão no pool em milissegundos
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true
- Implementação
3.1 - Enumeração OrigemDados e Enotação @OrigemDados
/**
* Tipos de origem de dados disponíveis
*/
public enum OrigemDados
{
/**
* Fonte de dados primária
*/
PRINCIPAL,
/**
* Fonte de dados secundária
*/
SECUNDARIA
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Anotação para seleção dinâmica de origem de dados
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OrigemDados
{
/**
* Nome da origem de dados a ser utilizada
*/
public OrigemDados value() default OrigemDados.PRINCIPAL;
}
3.2 - Gerenciador de Conetxto de Origem de Dados
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Gerenciador de contexto para alternância de origens de dados
*/
public class GerenciadorContextoDados
{
public static final Logger logger = LoggerFactory.getLogger(GerenciadorContextoDados.class);
/**
* ThreadLocal mantém variáveis específicas para cada thread,
* permitindo que cada thread altere sua própria cópia sem afetar outras threads.
*/
private static final ThreadLocal<String> CONTEXTO = new ThreadLocal<>();
/**
* Define a origem de dados atual
*/
public static void definirOrigemDados(String tipoOrigem)
{
logger.info("Alternando para origem de dados: {}", tipoOrigem);
CONTEXTO.set(tipoOrigem);
}
/**
* Obtém a origem de dados atual
*/
public static String obterOrigemDados()
{
return CONTEXTO.get();
}
/**
* Limpa a origem de dados do contexto
*/
public static void limparOrigemDados()
{
CONTEXTO.remove();
}
}
3.3 - Extensão de AbstractRoutingDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* Implementação de fonte de dados dinâmica
*/
public class FonteDadosDinamica extends AbstractRoutingDataSource
{
public FonteDadosDinamica(DataSource fonteDadosPadrao, Map<Object, Object> fontesDadosAlvo)
{
super.setDefaultTargetDataSource(fonteDadosPadrao);
super.setTargetDataSources(fontesDadosAlvo);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey()
{
return GerenciadorContextoDados.obterOrigemDados();
}
}
3.4 - Definição do Aspecto
import com.starfast.admin.common.annotation.OrigemDados;
import com.starfast.admin.datasource.GerenciadorContextoDados;
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.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* Aspecto para manipulação de múltiplas origens de dados
*/
@Aspect
@Order(1)
@Component
public class AspectoOrigemDados
{
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(com.duchong.common.annotation.OrigemDados)")
public void pontoCorteDados()
{
// Ponto de corte para métodos anotados
}
@Around("pontoCorteDados()")
public Object interceptor(ProceedingJoinPoint ponto) throws Throwable
{
MethodSignature assinatura = (MethodSignature) ponto.getSignature();
Method metodo = assinatura.getMethod();
OrigemDados origemDados = metodo.getAnnotation(OrigemDados.class);
if (origemDados != null)
{
GerenciadorContextoDados.definirOrigemDados(origemDados.value().name());
}
try
{
return ponto.proceed();
}
finally
{
// Remove a origem de dados após a execução do método
GerenciadorContextoDados.limparOrigemDados();
}
}
}
3.5 - Configuração do Spring
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.starfast.admin.common.enums.OrigemDados;
import com.starfast.admin.datasource.FonteDadosDinamica;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* Configuração do Druid para múltiplas origens de dados
*/
@Configuration
public class ConfiguracaoDruid
{
@Bean
@ConfigurationProperties("spring.datasource.druid.principal")
public DataSource fonteDadosPrincipal()
{
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.secundaria")
@ConditionalOnProperty(prefix = "spring.datasource.druid.secundaria", name = "enabled", havingValue = "true")
public DataSource fonteDadosSecundaria()
{
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "fonteDadosDinamica")
@Primary
public FonteDadosDinamica fonteDados()
{
Map<Object, Object> mapasFontesDados = new HashMap<>();
mapasFontesDados.put(OrigemDados.PRINCIPAL.name(), fonteDadosPrincipal());
mapasFontesDados.put(OrigemDados.SECUNDARIA.name(), fonteDadosSecundaria());
return new FonteDadosDinamica(fonteDadosPrincipal(), mapasFontesDados);
}
}
3.6 - Utilização
Para alternar a origem de dados em um método específico, adicione a anotação:
@OrigemDados(value = OrigemDados.SECUNDARIA)