spring aop, mysql master-slave configuration achieves read-write separation. Next, record your own configuration process and problems encountered to facilitate next operation, and hope to help some friends.
1. Use spring aop interception mechanism to dynamically select data sources.
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. Use Spring's AbstractRoutingDataSource to solve the problem of multiple data sources
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class ChooseDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return HandleDataSource.getDataSource(); } }
4. Use ThreadLocal to solve the thread safety problem
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. Define a data source aspect class, access it through aop, and configure it in the spring configuration file, so aop annotations are not used.
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. Configure 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. Use annotations to dynamically select data sources and read and write libraries respectively.
@DataSource("write") public void update(User user) { userMapper.update(user); } @DataSource("read") public Document getDocById(long id) { return documentMapper.getById(id); }
Test the write operation: you can modify the data through the application, modify the main database data, and find that the data from the slave database is updated synchronously, so the defined write operations are all written to the database
Test read operation: Modify the slave database data in the background, check that the data in the main database has not been modified, refresh the application page, and find that the data from the slave database is read, indicating that the read-write separation is ok.
Summary of problems encountered:
Problem 1: The project is a maven project, which uses the Spring aop mechanism. In addition to the core jar package of spring, it also needs to use the jar package. There are aspectj.jar, aspectjweaver.jar, and aopalliance.jar. Check the pom in the project and find that the dependency package is missing. Since the local warehouse does not have these jars, find the maven central library that can provide downloading of jar packages, configure it in maven, and automatically update:
<repository> <id>nexus</id> <name>nexus</name> <url>http://repository.sonatype.org/content/groups/public/</url> <layout>default</layout> </repository>
Configure the jars that the project depends on, mainly because these two are missing.
<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>
The above is the entire content of this article. I hope it will be helpful to everyone’s learning. I also hope that everyone will support the PHP Chinese website.
For more detailed information on using spring aop to implement business layer mysql reading and writing separation, please pay attention to the PHP Chinese website!