spring aop , mysql 主從配置 實(shí)現(xiàn)讀寫分離,接下來(lái)把自己的配置過程,以及遇到的問題記錄下來(lái),方便下次操作,也希望給一些朋友帶來(lái)幫助。
1.使用spring aop 攔截機(jī)制現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)選取。
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 * 編譯器將把注釋記錄在類文件中,在運(yùn)行時(shí) VM 將保留注釋,因此可以反射性地讀取。 * @author yangGuang * */ @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.METHOD) public @interface DataSource { String value(); } |
3.利用Spring的AbstractRoutingDataSource解決多數(shù)據(jù)源的問題
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.定義一個(gè)數(shù)據(jù)源切面類,通過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
|
<!-- 主庫(kù)數(shù)據(jù)源 --> < 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 > <!-- 從庫(kù)數(shù)據(jù)源 --> < 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, 事務(wù)管理 --> < bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > < property name = "dataSource" ref = "dataSource" /> </ bean > <!-- 注解自動(dòng)載入 --> < 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 > <!-- 激活自動(dòng)代理功能 --> < aop:aspectj-autoproxy proxy-target-class = "true" /> <!-- 配置數(shù)據(jù)庫(kù)注解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 > <!-- 配置數(shù)據(jù)庫(kù)注解aop --> |
7.使用注解,動(dòng)態(tài)選擇數(shù)據(jù)源,分別走讀庫(kù)和寫庫(kù)。
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); } |
測(cè)試寫操作:可以通過應(yīng)用修改數(shù)據(jù),修改主庫(kù)數(shù)據(jù),發(fā)現(xiàn)從庫(kù)的數(shù)據(jù)被同步更新了,所以定義的write操作都是走的寫庫(kù)
測(cè)試讀操作: 后臺(tái)修改從庫(kù)數(shù)據(jù),查看主庫(kù)的數(shù)據(jù)沒有被修改,在應(yīng)用頁(yè)面中刷新,發(fā)現(xiàn)讀的是從庫(kù)的數(shù)據(jù),說明讀寫分離ok。
遇到的問題總結(jié):
問題1:項(xiàng)目是maven工程,用到了Spring aop機(jī)制,除了spring的核心jar包以為,還需要用到的jar包有aspectj.jar,aspectjweaver.jar,aopalliance.jar查看項(xiàng)目中的pom,發(fā)現(xiàn)缺少依賴包,由于本地倉(cāng)庫(kù)沒有這些jar,查找可以提供下載jar包的maven中央庫(kù)庫(kù),配置到maven中,自動(dòng)更新:
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 > |
配置項(xiàng)目依賴的jar,主要是缺少這兩個(gè)。
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> |
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://blog.csdn.net/huoyunshen88/article/details/36674861