基于springAop动态切换数据源实现读写分离

读写分离的好处:高并发互联网下减少数据库压力。详细请自行百度。

现在需求:读数据从test库中,写数据从test2中。根据调用方法的不同实现动态切换。

直接代码:

bean.xml:



	

   	
   	
	dataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        
        
        
    
    dataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        
        
        
    
    
       
    	  
	         
	             
	               
	               
	           
      	  
    	 
    	   
 	  
    
 	
        dynamicDataSource" />
        
    
    
    
        
        
        
    
    
   
      
	
	
		dynamicDataSource" />
	
	
 	  
 	
	












HandleDataSource.java:

/**
 * 把当前请求的数据源(xml配置targetDataSources下的key)塞入到ThreadLocal中
 */
public class HandleDataSource {
	public static final ThreadLocal holder=new ThreadLocal();

	public static void putDataSource(String dataSource){
		holder.set(dataSource);
	}
	/**
	 * ThreadLocal里面拿出当前请求的数据源
	 */
	public static String getDataSource(){
		return holder.get();
	}
	public static void clearDataSource() {
		holder.remove();
	}
}

DynamicDataSource.java:

/**
 * 数据源选择类:拿到动态切换的Key spring给你选择数据源
 * @author Administrator
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
	/**
	 * 告诉spring使用哪个数据源
	 */
	@Override
	protected Object determineCurrentLookupKey() {
		return HandleDataSource.getDataSource();
	}

}

注解类:在方法上使用,为方法指定一个数据源

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TargetDataSource {
	String value() default "";
}

service.java:使用事务时,传播属性很重要,要不调用的时候会在一个数据源上添加事务,就不对了。事务内部调用另一个事务时,走代理类的方法。(aop失效情况,查看我另一篇帖子)

@Service
public class ServiceImpl /*implements ServiceI*/ {
	@Autowired
	Dao dao;
	/**
	 *方法执行前对数据源进行动态切换
	 * @return
	 */
	
	@TargetDataSource("test")
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public List findUser(){
		List list= dao.findUser();
		int a=10/0;
		return list;
	}
	
	
	@TargetDataSource("test2")
	@Transactional
	public void addOrder(){
		dao.addOrder();
		/**
		 * 当前数据源事务调用另一个数据源事务时,设置事务的传播属性,否则会在一个数据源上添加事务,那就不对了。
		 */
		ServiceImpl service=(ServiceImpl)AopContext.currentProxy();
		System.out.println(service.findUser());
	}
}

测试类;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:cn/rjx/spring/mutidatasource1/bean.xml"}) //加载配置文件   
@Component
public class Test01 {
	Logger logger=LoggerFactory.getLogger(ServiceImpl.class);
	@Resource
	ServiceImpl serviceImpl;
	
	@Test
	public void test01(){
		logger.info("#################################");
		List list=serviceImpl.findUser();	
		System.out.println(list);
	}
	@Test
	public void test02(){
		logger.info("---------------------------------------");
		serviceImpl.addOrder();	
	}
}

(******)动态选择的切面:1.注解加到接口上,解析的时候根据接口上的注解解析。2.没接口情况下,注解直接加到实现方法上。我注释的地方是第一种情况,没注释的是加载实现类上的,也是我现在写的。


增强类:

@Aspect
@Order(-1)//多个aop配置时需要指定加载顺序(事务也是一个) -1为最先执行
@Component
//@EnableAspectJAutoProxy
public class DataSourceAspectJ  /*implements MethodBeforeAdvice,AfterReturningAdvice*/  {
	@Pointcut(value = "execution(* cn.rjx.spring.mutidatasource1.ServiceImpl.*(..))")
	public void join(){
	}
	@Before("join()")
	public void before(JoinPoint joinPoint){
		System.out.println("before advice!");
		//1.获取被代理类
		Object target = joinPoint.getTarget();
		//获取方法名称
		String targetMethodName=joinPoint.getSignature().getName();
		//2.拿到被代理类的接口
		//Class[] interfacezz=target.getClass().getInterfaces();
		//3.拿到被代理的方法的入参
//		 Class[] parameterTypes = ((MethodSignature)joinPoint.getSignature())
//								.getMethod().getParameterTypes();
		   Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();
		   if(method.isAnnotationPresent(TargetDataSource.class)){
				TargetDataSource annotation = method.getAnnotation(TargetDataSource.class);
				HandleDataSource.putDataSource(annotation.value());
			}else{
				HandleDataSource.putDataSource("test");
			}
		 
		//4. 获取接口方法上的注解
//		 for(Class intfzz:interfacezz){
//			 try {
//				Method method = intfzz.getMethod(targetMethodName, parameterTypes);
//				if(method!=null){
//					if(method.isAnnotationPresent(TargetDataSource.class)){
//						TargetDataSource annotation = method.getAnnotation(TargetDataSource.class);
//						HandleDataSource.putDataSource(annotation.value());
//					}else{
//						HandleDataSource.putDataSource("test");
//					}
//				}else{
//					continue;
//				}
//			 
//			 } catch (NoSuchMethodException e) {
//				e.printStackTrace();
//			} catch (SecurityException e) {
//				e.printStackTrace();
//			}
//		 }
	}
	@AfterReturning("join()")
	public void after(){
		System.out.println("after");
		HandleDataSource.clearDataSource();
	}
}


测试:执行test02()没异常情况下,往test2表中插入一条数据,并读取test中的的数据。如果有异常10/0事务回滚,两条操作都不执行。回滚指定事务段我没试,可以把我另一帖子的回滚段代码拿出来应该可以。


注意点:1.引入事务是切面那个order(-1)要加,表示位于增强链的最前。2.事务操作时要注意设置广播机制,尤其一个方法中调用另一个方法的事务时。


*************静态切换我研究研究,就是绑定sqlSessionFactory***************************



求助:能不能帮我把

  
       
    	  
	         
	             
	               
	               
	           
      	  
    	 
    	   
 	  

转换成DynamicDataSource中的@Configuration+@bean形式?


你可能感兴趣的:(java,spring,mybaties)