项目说明:
框架:Spring + Spring MVC + Mybatis + AspectJ
开发环境:jdk1.7,Tomcat7
使用框架AspectJ实现自动切换多数据源,原理是AOP技术,类似于过滤器、监听器、拦截器底层也都是与AOP技术相似,都是将某个你想要做处理的“块”,当成切面或切点。Maven如何创建,我并没有作说明,因为网上的例子实在是太多了,如果有需要我也会添加上的。
本文大部分大部分是代码的实例,一些说明我都写在代码中的注释中了,唯有AspectJ框架需要道友们自己上网查了,这块的知识点还挺丰富的。最后,代码如有不足之处请多指教!
代码展示
pom.xml文件
4.0.0
com.test.multipleSource
multipleSource-ssm
war
0.0.1-SNAPSHOT
multipleSource-ssm Maven Webapp
http://maven.apache.org
4.0.2.RELEASE
3.2.6
1.7.7
1.2.17
org.springframework
spring-aspects
${spring.version}
org.springframework
spring-orm
${spring.version}
junit
junit
4.11
test
org.springframework
spring-core
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-oxm
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-test
${spring.version}
org.mybatis
mybatis
${mybatis.version}
org.mybatis
mybatis-spring
1.2.2
javax
javaee-api
7.0
mysql
mysql-connector-java
5.1.30
jstl
jstl
1.2
log4j
log4j
${log4j.version}
org.codehaus.jackson
jackson-mapper-asl
1.9.13
commons-fileupload
commons-fileupload
1.3.1
commons-io
commons-io
2.4
commons-codec
commons-codec
1.9
net.sf.json-lib
json-lib
2.4
jar
jdk15
commons-beanutils
commons-beanutils
1.8.0
commons-collections
commons-collections
3.2.1
compile
commons-lang
commons-lang
2.5
compile
multipleSource-ssm
org.apache.tomcat.maven
tomcat6-maven-plugin
2.1
http://localhost:9080
tomcat6
8080
UTF-8
spring-mvc.xml文件
text/html;charset=UTF-8
application/json;charset=UTF-8
spring-mybatis.xml
classpath:com/multiple/master/mapper/*.xml
classpath:com/multiple/secondary/mapper/*.xml
jdbc.properties
driver=com.mysql.jdbc.Driver
masterUrl=jdbc:mysql://127.0.0.1:3306/master
secondaryUrl=jdbc:mysql://127.0.0.1:3306/secondary
username=root
password=root
initialSize=0
maxActive=20
maxIdle=20
minIdle=1
maxWait=60000
log4j.properties
log4j.rootLogger=INFO,Console,File
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%c] - %m%n
log4j.appender.File = org.apache.log4j.RollingFileAppender
log4j.appender.File.File = logs/ssm.log
log4j.appender.File.MaxFileSize = 10MB
log4j.appender.File.Threshold = ALL
log4j.appender.File.layout = org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern =[%p] [%d{yyyy-MM-dd HH\:mm\:ss}][%c]%m%n
web.xml
Archetype Created Web Application
contextConfigLocation
classpath:spring-mybatis.xml
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
true
encoding
UTF-8
encodingFilter
/*
org.springframework.web.context.ContextLoaderListener
org.springframework.web.util.IntrospectorCleanupListener
SpringMVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
1
true
SpringMVC
/
/index.jsp
工程的目录结构图
在目录结构上我分com.multiple.master、com.multiple.secondary,这是为了对应AOP的拦截路径
DynamicDataSource.java
package com.multiple.utils;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author Wh
* 一个动态数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
public static final String DATA_SOURCE_MASTER = "dataSourceMaster"; //主数据源名称。与XML文件中名称对应
public static final String DATA_SOURCE_SECONDARY = "dataSourceSecondary"; //从数据源名称。与XML文件中名称对应
private static final ThreadLocal contextHolder = new ThreadLocal();
/**
* 参数为要切换的数据源名称,就是上面的final变量
* @param customerType
*/
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
/**
* 通过线程的方式获取数据源,目的是为了在并发的情况下依然能够正常切换
* @return
*/
public static String getCustomerType() {
return contextHolder.get();
}
/**
* 用于关闭当前切换的数据源
*/
public static void clearCustomerType() {
contextHolder.remove();
}
/**
* @author Wh
* 这个用于切换动态数据源必须要实现的一个方法
*/
@Override
protected Object determineCurrentLookupKey() {
return getCustomerType();
}
}
ServiceAspect.java
package com.multiple.utils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author Wh
* 通过AOP实现对数据源的自动切换,AOP是Spring的核心。通过AspectJ框架实现对AOP的操作
*/
@Component
@Aspect
// 设置当前加载的顺序
@Order(-1)
public class ServiceAspect {
/**
* 设置切点:路径为Secondary层的service层,*.*(..)代表其service报下的所有类和方法
*/
@Pointcut("execution(* com.multiple.secondary.service.*.*(..))")
public void aspect() {
}
/**
* @param joinPoint
* 进入切点方法之前(切点是专业术语,在本文中就是你要切换数据源的的方法)
* 切换数据源,当一个工程中两个数据源时可以对不常用的数据源写死,因为工程中必定有一个主数据源,当超过两个以上的数据源时可以通过传参的形式到该方法中
*/
@Before("aspect()")
public void before(JoinPoint joinPoint) {
DynamicDataSource
.setCustomerType(DynamicDataSource.DATA_SOURCE_SECONDARY);
}
/**
* @param joinPoint
* 当退出切点方法之后,关闭所切换额数据源
*/
@After("aspect()")
public void after(JoinPoint joinPoint) {
DynamicDataSource.clearCustomerType();
}
/**
* @param joinPoint
* @param ex
* 针对当前拦截的切点方法中出现异常时所做的处理
*/
@AfterThrowing(pointcut = "aspect()", throwing = "ex")
public void afterThrow(JoinPoint joinPoint, Exception ex) {
DynamicDataSource.clearCustomerType();
}
}
Master数据源
Goods.java
package com.multiple.master.entity;
import java.util.Date;
/**
* @author Wh
* 商品表
*/
public class Goods {
private String uuid; // uuid
private String goodsName; // 商品名称
private float prices; // 商品价格
private Date generateDate; // 生成日期
private int number; // 商品数量
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public float getPrices() {
return prices;
}
public void setPrices(float prices) {
this.prices = prices;
}
public Date getGenerateDate() {
return generateDate;
}
public void setGenerateDate(Date generateDate) {
this.generateDate = generateDate;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
GoodsMapper.xml
GoodsMapper.java
package com.multiple.master.dao;
import java.util.List;
import com.multiple.master.entity.Goods;
public interface GoodsMapper {
List queryGoodsList();
}
IGoodsService.java
package com.multiple.master.service;
import java.util.List;
import com.multiple.master.entity.Goods;
public interface IGoodsService {
List queryGoodsList();
}
GoodsServiceImpl.java
package com.multiple.master.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.multiple.master.dao.GoodsMapper;
import com.multiple.master.entity.Goods;
import com.multiple.master.service.IGoodsService;
/**
* @author Wh
* Master数据库下商品表的业务层
*/
@Service("goodsService")
public class GoodsServiceImpl implements IGoodsService {
@Resource
private GoodsMapper goodsMapper;
@Override
public List queryGoodsList() {
return goodsMapper.queryGoodsList();
}
}
Secondary层
Orders.java
package com.multiple.secondary.entity;
/**
* @author Wh
* 订单表
*/
public class Orders {
private String uuid; // 订单uuid
private String goods_uuids; // 商品UUID组
private float orderPrice; // 订单价格
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getGoods_uuids() {
return goods_uuids;
}
public void setGoods_uuids(String goods_uuids) {
this.goods_uuids = goods_uuids;
}
public float getOrderPrice() {
return orderPrice;
}
public void setOrderPrice(float orderPrice) {
this.orderPrice = orderPrice;
}
}
OrdersMapper.java
package com.multiple.secondary.dao;
import java.util.List;
import com.multiple.secondary.entity.Orders;
public interface OrdersMapper {
List queryOrdersList();
}
OrdersMapper.xml
IOrdersService.java
package com.multiple.secondary.service;
import java.util.List;
import com.multiple.secondary.entity.Orders;
public interface IOrdersService {
List queryOrdersList();
}
OrdersServiceImpl.java
package com.multiple.secondary.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.multiple.secondary.dao.OrdersMapper;
import com.multiple.secondary.entity.Orders;
import com.multiple.secondary.service.IOrdersService;
/**
* @author Wh
* Secondary数据库下的订单表的业务层
*/
@Service("ordersService")
public class OrdersServiceImpl implements IOrdersService {
@Resource
private OrdersMapper ordersMapper;
@Override
public List queryOrdersList() {
return ordersMapper.queryOrdersList();
}
}
TestController.java
package com.multiple.controller;
import java.io.UnsupportedEncodingException;
import java.util.List;
import javax.annotation.Resource;
import net.sf.json.JSONArray;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.multiple.master.entity.Goods;
import com.multiple.master.service.IGoodsService;
import com.multiple.secondary.entity.Orders;
import com.multiple.secondary.service.IOrdersService;
/**
* @author Wh
* 一个测试的Controller类
*/
@Controller
@RequestMapping("/testController")
public class TestController {
@Resource
@Qualifier("goodsService")
private IGoodsService goodsService;
@Resource
@Qualifier("ordersService")
private IOrdersService ordersService;
/**
*
* 实现对Master数据库的信息查询
*
* @return
* @throws UnsupportedEncodingException
*/
@RequestMapping(value = "/queryGoods", produces = "application/json;charset=GBK")
@ResponseBody
private String queryGoodsList() throws UnsupportedEncodingException {
List goodsList = goodsService.queryGoodsList();
JSONArray jsonObject = JSONArray.fromObject(goodsList);
String jsonStr = jsonObject.toString();
return jsonStr;
}
/**
* 实现对Secondary数据库的信息查询
*
* @return
* @throws UnsupportedEncodingException
*/
@RequestMapping(value = "/queryOrders", produces = "application/json;charset=GBK")
@ResponseBody
private String queryOrdersList() throws UnsupportedEncodingException {
List goodsList = ordersService.queryOrdersList();
JSONArray jsonObject = JSONArray.fromObject(goodsList);
String jsonStr = jsonObject.toString();
return jsonStr;
}
}