用Spring aop配置动态数据源

用Spring aop配置动态数据源(annotation配置,annotation动态参数)

方法很简单,直接贴代码了。

首先继承spring的一个类AbstractRoutingDataSource

import java.util.Map;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.jdbc.datasource.lookup.DataSourceLookup;

/**
 *类名:DynamicDataSource.java
 *功能:动态数据源类
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

	/* 
	 * 该方法必须要重写  方法是为了根据数据库标示符取得当前的数据库
	 */	
	protected Object determineCurrentLookupKey() {
		String dataSourceType= DataSourceContextHolder.getDataSourceType();
		
		return dataSourceType;
	}
	public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
		super.setDataSourceLookup(dataSourceLookup);
	}
	public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
		super.setDefaultTargetDataSource(defaultTargetDataSource);
	}
	public void setTargetDataSources(Map targetDataSources) {
		super.setTargetDataSources(targetDataSources);
	}	

}

determineCurrentLookupKey必须重写用来判断是需要那个数据源。

public class DataSourceContextHolder {
	private static final ThreadLocal contextHolder=new ThreadLocal();
	public static void setDataSourceType(String dataSourceType){
		contextHolder.set(dataSourceType);
	}
	public static String getDataSourceType(){
		return (String) contextHolder.get();
	}
	public static void clearDataSourceType(){
		contextHolder.remove();
	}	
}

DataSourceContextHolder类用来在当前线程中设置和保存数据源关键字。setDataSourceType的参数我设置为了String,如果需要可以自己定义枚举类来设值
创建一个annotation,用来让aop切入,并配置所用数据源信息

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DynamicSwitchDataSource {
    String dataSource() default "";
}

创建aop类进行数据源信息切换

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
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 com.doframework.core.extend.annotation.DynamicSwitchDataSource;
import com.doframework.core.extend.datasource.DataSourceContextHolder;

/**
 * descrption: 使用AOP拦截特定的注解去动态的切换数据源
 * authohr: 武润泽
 * date: 2019.07.18   
 */
@Aspect
@Component
@Order(0)
public class DynamicDataSourceAop {
	private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceAop.class);
    //@within在类上设置
    //@annotation在方法上进行设置
	
	//within拦截类上的注解,annotation拦截方法上的注解
	@Pointcut("@within(com.doframework.core.extend.annotation.DynamicSwitchDataSource)||@annotation(com.doframework.core.extend.annotation.DynamicSwitchDataSource)")
    public void pointcut() {}

    
    @Before("pointcut()")
    public void doBefore(JoinPoint joinPoint)
    {	Method realMethod=null;
    	try {
    		//获取方法上的注解必须用getDeclaredMethod()获取,用((MethodSignature)joinPoint.getSignature()).getMethod(),获取到的为代理对象不带注解信息
			realMethod = joinPoint.getTarget().getClass().getDeclaredMethod(((MethodSignature)joinPoint.getSignature()).getMethod().getName(),((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes());
			if(realMethod==null) {
				realMethod= joinPoint.getTarget().getClass().getSuperclass().getDeclaredMethod(((MethodSignature)joinPoint.getSignature()).getMethod().getName(),((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes());
			}
    	} catch (NoSuchMethodException e) {
    		try {
				realMethod= joinPoint.getTarget().getClass().getSuperclass().getDeclaredMethod(((MethodSignature)joinPoint.getSignature()).getMethod().getName(),((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes());
			} catch (NoSuchMethodException e1) {
				e1.printStackTrace();
			} catch (SecurityException e1) {
				e1.printStackTrace();
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		}
			DynamicSwitchDataSource annotationClass=realMethod.getAnnotation(DynamicSwitchDataSource.class);//获取方法上的注解
	        if(annotationClass == null){
	            annotationClass = joinPoint.getTarget().getClass().getAnnotation(DynamicSwitchDataSource.class);//获取类上面的注解
	            if(annotationClass == null) return;
	        }

	        //获取注解上的数据源的值的信息
	    	String dataSourceKey = annotationClass.dataSource();
	        if(dataSourceKey !=null){
	            //给当前的执行SQL的操作设置特殊的数据源的信息
	        	DataSourceContextHolder.setDataSourceType(dataSourceKey);
	        	log.info("AOP动态切换数据源,className"+(joinPoint.getTarget().getClass().getName())+"methodName"+(((MethodSignature)joinPoint.getSignature()).getMethod().getName())+";切换致dataSourceKey:"+(DataSourceContextHolder.getDataSourceType()==""?"默认数据源":DataSourceContextHolder.getDataSourceType()));
	        }
    }

    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        //清理掉当前设置的数据源,让默认的数据源不受影响
    	DataSourceContextHolder.clearDataSourceType();
    	log.info("AOP动态切换数据源,className"+(joinPoint.getTarget().getClass().getName())+"methodName"+(((MethodSignature)joinPoint.getSignature()).getMethod().getName())+";关闭切换还原默认dataSourceKey:"+(DataSourceContextHolder.getDataSourceType()==""?"默认数据源":DataSourceContextHolder.getDataSourceType()));
    }
}

数据源配置按正常配置类,然后配置个动态数据源bean,类型为我们自己写的DynamicDataSource类型

<!-- 配置数据源1(主库数据源) -->
	<bean name="dataSource_one" class="com.alibaba.druid.pool.DruidDataSource"
		init-method="init" destroy-method="close">
		<property name="url" value="${jdbc.url.dod}" />
		<property name="username" value="${jdbc.username.dod}" />
		<property name="password" value="${jdbc.password.dod}" />
		<!-- 初始化连接大小 -->
		<property name="initialSize" value="0" />
		<!-- 连接池最大使用连接数量 -->
		<property name="maxActive" value="50" />
		<!-- 连接池最小空闲 -->
		<property name="minIdle" value="5" />
		
		<!-- 获取连接最大等待时间 -->
		<property name="maxWait" value="60000" />
		
		<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
		<property name="minEvictableIdleTimeMillis" value="300000" />
		
		<property name="validationQuery" value="${validationQuery.sql}" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		<property name="testWhileIdle" value="true" />

		<!-- 开启Druid的监控统计功能 -->
		<property name="filters" value="stat" />
		
		<!-- 打开removeAbandoned功能 -->
		<property name="removeAbandoned" value="true" />
		<!-- 1800秒,也就是30分钟 -->
		<property name="removeAbandonedTimeout" value="3600" />
		<!-- 关闭abanded连接时输出错误日志 -->
		<property name="logAbandoned" value="true" />
		<!-- Oracle连接是获取字段注释 -->
		<property name="connectProperties">
			<props>
				<prop key="remarksReporting">true</prop>
			</props>
		</property>
	</bean>

	<!-- 配置数据源2(客户库) -->
	<bean name="dataSource_two" class="com.alibaba.druid.pool.DruidDataSource"
		init-method="init" destroy-method="close">
		<property name="url" value="${jdbc.url.customer}" />
		<property name="username" value="${jdbc.username.customer}" />
		<property name="password" value="${jdbc.password.customer}" />
		<!-- 初始化连接大小 -->
		<property name="initialSize" value="0" />
		<!-- 连接池最大使用连接数量 -->
		<property name="maxActive" value="50" />
		<!-- 连接池最小空闲 -->
		<property name="minIdle" value="5" />
		
		<!-- 获取连接最大等待时间 -->
		<property name="maxWait" value="60000" />
		
		<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
		<property name="minEvictableIdleTimeMillis" value="300000" />
		
		<property name="validationQuery" value="${validationQuery.sql}" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		<property name="testWhileIdle" value="true" />

		<!-- 开启Druid的监控统计功能 -->
		<property name="filters" value="stat" />
		
		<!-- 打开removeAbandoned功能 -->
		<property name="removeAbandoned" value="true" />
		<!-- 1800秒,也就是30分钟 -->
		<property name="removeAbandonedTimeout" value="3600" />
		<!-- 关闭abanded连接时输出错误日志 -->
		<property name="logAbandoned" value="true" />
		<!-- Oracle连接是获取字段注释 -->
		<property name="connectProperties">
			<props>
				<prop key="remarksReporting">true</prop>
			</props>
		</property>
	</bean>

	
	
	<!-- 数据源集合 -->
	<bean id="dataSource"
		class="com.doframework.core.extend.datasource.DynamicDataSource">
		<property name="targetDataSources">
			<!-- key和数据库对应!!! -->
			<map key-type="java.lang.String">
				<entry key="Demo3" value-ref="dataSource_one" />
				<entry key="Test" value-ref="dataSource_two"></entry>
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="dataSource_dodDB" />
	</bean>


	<!-- 基于DynamicDataSource的key可进行指定数据库 -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="entityInterceptor" ref="hiberAspect" />
		<property name="hibernateProperties">
			<props>
				<!--<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> -->
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
				<prop key="hibernate.show_sql">false</prop>
				<prop key="hibernate.format_sql">false</prop>
				<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
			</props>
		</property>
		
		<!-- 注解方式配置 -->
		<property name="packagesToScan">
			<list>
				<value>com.doframework.web.system.pojo.*</value>
			</list>
		</property>
	</bean>

如果需要动态修改注解的数据源参数可以参考↓
下文链接

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * Created by jiangzeyin on 2018/4/15.
 */
public class test {

    @testA("a")
    public static void main(String[] args) throws Exception {
        Method method = test.class.getMethod("main", String[].class);
        testA testA = method.getAnnotation(testA.class);
        if (testA == null)
            throw new RuntimeException("please add testA");
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(testA);
        Field value = invocationHandler.getClass().getDeclaredField("memberValues");
        value.setAccessible(true);
        Map<String, Object> memberValues = (Map<String, Object>) value.get(invocationHandler);
        String val = (String) memberValues.get("value");
        System.out.println("改变前:" + val);
        val = "b";
        memberValues.put("value", val);
        System.out.println("改变后:" + testA.value());

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值