Home Java javaTutorial Detailed explanation of using spring aop to implement read-write separation of mysql in the business layer

Detailed explanation of using spring aop to implement read-write separation of mysql in the business layer

Jan 24, 2017 am 10:49 AM

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();
}
Copy after login

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();
   }
     
 }
Copy after login


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();
  }  
}
Copy after login


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();
      }
    }
}
Copy after login


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 -->
Copy after login

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);
}
Copy after login

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>
Copy after login


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>
Copy after login


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!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Is the company's security software causing the application to fail to run? How to troubleshoot and solve it? Is the company's security software causing the application to fail to run? How to troubleshoot and solve it? Apr 19, 2025 pm 04:51 PM

Troubleshooting and solutions to the company's security software that causes some applications to not function properly. Many companies will deploy security software in order to ensure internal network security. ...

How to simplify field mapping issues in system docking using MapStruct? How to simplify field mapping issues in system docking using MapStruct? Apr 19, 2025 pm 06:21 PM

Field mapping processing in system docking often encounters a difficult problem when performing system docking: how to effectively map the interface fields of system A...

How to elegantly obtain entity class variable names to build database query conditions? How to elegantly obtain entity class variable names to build database query conditions? Apr 19, 2025 pm 11:42 PM

When using MyBatis-Plus or other ORM frameworks for database operations, it is often necessary to construct query conditions based on the attribute name of the entity class. If you manually every time...

How do I convert names to numbers to implement sorting and maintain consistency in groups? How do I convert names to numbers to implement sorting and maintain consistency in groups? Apr 19, 2025 pm 11:30 PM

Solutions to convert names to numbers to implement sorting In many application scenarios, users may need to sort in groups, especially in one...

How does IntelliJ IDEA identify the port number of a Spring Boot project without outputting a log? How does IntelliJ IDEA identify the port number of a Spring Boot project without outputting a log? Apr 19, 2025 pm 11:45 PM

Start Spring using IntelliJIDEAUltimate version...

How to safely convert Java objects to arrays? How to safely convert Java objects to arrays? Apr 19, 2025 pm 11:33 PM

Conversion of Java Objects and Arrays: In-depth discussion of the risks and correct methods of cast type conversion Many Java beginners will encounter the conversion of an object into an array...

How to elegantly get entity class variable name building query conditions when using TKMyBatis for database query? How to elegantly get entity class variable name building query conditions when using TKMyBatis for database query? Apr 19, 2025 pm 09:51 PM

When using TKMyBatis for database queries, how to gracefully get entity class variable names to build query conditions is a common problem. This article will pin...

E-commerce platform SKU and SPU database design: How to take into account both user-defined attributes and attributeless products? E-commerce platform SKU and SPU database design: How to take into account both user-defined attributes and attributeless products? Apr 19, 2025 pm 11:27 PM

Detailed explanation of the design of SKU and SPU tables on e-commerce platforms This article will discuss the database design issues of SKU and SPU in e-commerce platforms, especially how to deal with user-defined sales...

See all articles