spring aop , mysql 主从配置 实现读写分离,接下来把自己的配置过程,以及遇到的问题记录下来,方便下次操作,也希望给一些朋友带来帮助。
1.使用spring aop 拦截机制现数据源的动态选取。
- 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解决多数据源的问题
- import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
-
- public class ChooseDataSource extends AbstractRoutingDataSource {
-
- @Override
- protected Object determineCurrentLookupKey() {
- return HandleDataSource.getDataSource();
- }
-
- }
复制代码
4.利用ThreadLocal解决线程安全问题
- 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注解。
- 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
- <!-- 主库数据源 -->
- <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.使用注解,动态选择数据源,分别走读库和写库。
- @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中,自动更新:
- <repository>
- <id>nexus</id>
- <name>nexus</name>
- <url>http://repository.sonatype.org/content/groups/public/</url>
- <layout>default</layout>
- </repository>
复制代码
配置项目依赖的jar,主要是缺少这两个。
- <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>
复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持程序员之家。 |