spring aop , mysql 主从配置 实现读写分离,接下来把自己的配置过程,以及遇到的问题记录下来,方便下次操作,也希望给一些朋友带来帮助。
1.使用spring aop 拦截机制现数据源的动态选取。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * RUNTIME * 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。 * @author yangGuang * */ @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.METHOD) public @interface DataSource { String value(); } |
3.利用Spring的AbstractRoutingDataSource解决多数据源的问题
1
2
3
4
5
6
7
8
9
10
|
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class ChooseDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return HandleDataSource.getDataSource(); } } |
4.利用ThreadLocal解决线程安全问题
1
2
3
4
5
6
7
8
9
10
|
public class HandleDataSource { public static final ThreadLocal<String> holder = new ThreadLocal<String>(); public static void putDataSource(String datasource) { holder.set(datasource); } public static String getDataSource() { return holder.get(); } } |
5.定义一个数据源切面类,通过aop访问,在spring配置文件中配置了,所以没有使用aop注解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; 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.springframework.stereotype.Component; //@Aspect //@Component public class DataSourceAspect { //@Pointcut("execution(* com.apc.cms.service.*.*(..))") public void pointCut(){}; // @Before(value = "pointCut()") public void before(JoinPoint point) { Object target = point.getTarget(); System.out.println(target.toString()); String method = point.getSignature().getName(); System.out.println(method); Class<?>[] classz = target.getClass().getInterfaces(); Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()) .getMethod().getParameterTypes(); try { Method m = classz[ 0 ].getMethod(method, parameterTypes); System.out.println(m.getName()); if (m != null && m.isAnnotationPresent(DataSource. class )) { DataSource data = m.getAnnotation(DataSource. class ); HandleDataSource.putDataSource(data.value()); } } catch (Exception e) { e.printStackTrace(); } } } |
6.配置applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
<!-- 主库数据源 --> < bean id = "writeDataSource" class = "com.jolbox.bonecp.BoneCPDataSource" destroy-method = "close" > < property name = "driverClass" value = "com.mysql.jdbc.Driver" /> < property name = "jdbcUrl" value = "jdbc:mysql://172.22.14.6:3306/cpp?autoReconnect=true" /> < property name = "username" value = "root" /> < property name = "password" value = "root" /> < property name = "partitionCount" value = "4" /> < property name = "releaseHelperThreads" value = "3" /> < property name = "acquireIncrement" value = "2" /> < property name = "maxConnectionsPerPartition" value = "40" /> < property name = "minConnectionsPerPartition" value = "20" /> < property name = "idleMaxAgeInSeconds" value = "60" /> < property name = "idleConnectionTestPeriodInSeconds" value = "60" /> < property name = "poolAvailabilityThreshold" value = "5" /> </ bean > <!-- 从库数据源 --> < bean id = "readDataSource" class = "com.jolbox.bonecp.BoneCPDataSource" destroy-method = "close" > < property name = "driverClass" value = "com.mysql.jdbc.Driver" /> < property name = "jdbcUrl" value = "jdbc:mysql://172.22.14.7:3306/cpp?autoReconnect=true" /> < property name = "username" value = "root" /> < property name = "password" value = "root" /> < property name = "partitionCount" value = "4" /> < property name = "releaseHelperThreads" value = "3" /> < property name = "acquireIncrement" value = "2" /> < property name = "maxConnectionsPerPartition" value = "40" /> < property name = "minConnectionsPerPartition" value = "20" /> < property name = "idleMaxAgeInSeconds" value = "60" /> < property name = "idleConnectionTestPeriodInSeconds" value = "60" /> < property name = "poolAvailabilityThreshold" value = "5" /> </ bean > <!-- transaction manager, 事务管理 --> < bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > < property name = "dataSource" ref = "dataSource" /> </ bean > <!-- 注解自动载入 --> < context:annotation-config /> <!--enale component scanning (beware that this does not enable mapper scanning!)--> < context:component-scan base-package = "com.apc.cms.persistence.rdbms" /> < context:component-scan base-package = "com.apc.cms.service" > < context:include-filter type = "annotation" expression = "org.springframework.stereotype.Component" /> </ context:component-scan > < context:component-scan base-package = "com.apc.cms.auth" /> <!-- enable transaction demarcation with annotations --> < tx:annotation-driven /> <!-- define the SqlSessionFactory --> < bean id = "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > < property name = "dataSource" ref = "dataSource" /> < property name = "typeAliasesPackage" value = "com.apc.cms.model.domain" /> </ bean > <!-- scan for mappers and let them be autowired --> < bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer" > < property name = "basePackage" value = "com.apc.cms.persistence" /> < property name = "sqlSessionFactory" ref = "sqlSessionFactory" /> </ bean > < bean id = "dataSource" class = "com.apc.cms.utils.ChooseDataSource" > < property name = "targetDataSources" > < map key-type = "java.lang.String" > <!-- write --> < entry key = "write" value-ref = "writeDataSource" /> <!-- read --> < entry key = "read" value-ref = "readDataSource" /> </ map > </ property > < property name = "defaultTargetDataSource" ref = "writeDataSource" /> </ bean > <!-- 激活自动代理功能 --> < aop:aspectj-autoproxy proxy-target-class = "true" /> <!-- 配置数据库注解aop --> < bean id = "dataSourceAspect" class = "com.apc.cms.utils.DataSourceAspect" /> < aop:config > < aop:aspect id = "c" ref = "dataSourceAspect" > < aop:pointcut id = "tx" expression = "execution(* com.apc.cms.service..*.*(..))" /> < aop:before pointcut-ref = "tx" method = "before" /> </ aop:aspect > </ aop:config > <!-- 配置数据库注解aop --> |
7.使用注解,动态选择数据源,分别走读库和写库。
1
2
3
4
5
6
7
8
9
|
@DataSource ( "write" ) public void update(User user) { userMapper.update(user); } @DataSource ( "read" ) public Document getDocById( long id) { return documentMapper.getById(id); } |
测试写操作:可以通过应用修改数据,修改主库数据,发现从库的数据被同步更新了,所以定义的write操作都是走的写库
测试读操作: 后台修改从库数据,查看主库的数据没有被修改,在应用页面中刷新,发现读的是从库的数据,说明读写分离ok。
遇到的问题总结:
问题1:项目是maven工程,用到了Spring aop机制,除了spring的核心jar包以为,还需要用到的jar包有aspectj.jar,aspectjweaver.jar,aopalliance.jar查看项目中的pom,发现缺少依赖包,由于本地仓库没有这些jar,查找可以提供下载jar包的maven中央库库,配置到maven中,自动更新:
1
2
3
4
5
6
|
< repository > < id >nexus</ id > < name >nexus</ name > < url >http://repository.sonatype.org/content/groups/public/</ url > < layout >default</ layout > </ repository > |
配置项目依赖的jar,主要是缺少这两个。
1
2
3
4
5
6
7
8
9
10
|
< dependency > < groupId >aspectj</ groupId > < artifactId >aspectjrt</ artifactId > < version >1.5.4</ version > </ dependency > < dependency > < groupId >aspectj</ groupId > < artifactId >aspectjweaver</ artifactId > < version >1.5.4</ version > lt;/dependency> |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://blog.csdn.net/huoyunshen88/article/details/36674861