详解使用spring aop实现业务层mysql 读写分离

发布时间 - 2026-01-10 22:20:35    点击率:

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> 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# spring  # mysql读写分离  # aop  # 读写分离  # Spring AOP切面解决数据库读写分离实例详解  # SpringMVC4+MyBatis+SQL Server2014实现数据库读写分离  # SpringBoot集成Spring Data JPA及读写分离  # 详解Spring AOP 实现主从读写分离  # Spring+MyBatis实现数据库读写分离方案  # Spring配置动态数据源实现读写分离的方法  # Spring boot实现数据库读写分离的方法  # Spring 实现数据库读写分离的示例  # 使用Spring AOP实现MySQL数据库读写分离案例分析(附demo)  # Spring动态数据源实现读写分离详解  # 自己的  # 都是  # 是从  # 这两个  # 可以通过  # 还需要  # 配置文件  # 将把  # 大家多多  # 自动更新  # 主要是  # 同步更新  # 下次  # target  # service  # pointCut  # point  # getSignature  # method  # Class 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 昵图网官方站入口 昵图网素材图库官网入口  Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】  电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?  Laravel用户密码怎么加密_Laravel Hash门面使用教程  黑客如何通过漏洞一步步攻陷网站服务器?  齐河建站公司:营销型网站建设与SEO优化双核驱动策略  想要更高端的建设网站,这些原则一定要坚持!  手机网站制作与建设方案,手机网站如何建设?  Windows驱动无法加载错误解决方法_驱动签名验证失败处理步骤  香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧  如何用PHP快速搭建高效网站?分步指南  佐糖AI抠图怎样调整抠图精度_佐糖AI精度调整与放大细化操作【攻略】  Laravel Facade的原理是什么_深入理解Laravel门面及其工作机制  如何快速搭建虚拟主机网站?新手必看指南  湖南网站制作公司,湖南上善若水科技有限公司做什么的?  如何做网站制作流程,*游戏网站怎么搭建?  C#如何调用原生C++ COM对象详解  linux写shell需要注意的问题(必看)  如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框  如何快速上传建站程序避免常见错误?  Laravel如何实现多对多模型关联?(Eloquent教程)  Laravel路由怎么定义_Laravel核心路由系统完全入门指南  阿里云高弹*务器配置方案|支持分布式架构与多节点部署  用yum安装MySQLdb模块的步骤方法  如何彻底删除建站之星生成的Banner?  JavaScript如何操作视频_媒体API怎么控制播放  如何在IIS7上新建站点并设置安全权限?  在线制作视频的网站有哪些,电脑如何制作视频短片?  如何挑选最适合建站的高性能VPS主机?  Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区  jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?  大连 网站制作,大连天途有线官网?  如何在万网利用已有域名快速建站?  如何在Windows环境下新建FTP站点并设置权限?  如何在云指建站中生成FTP站点?  Win11搜索栏无法输入_解决Win11开始菜单搜索没反应问题【技巧】  深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?  千问怎样用提示词获取健康建议_千问健康类提示词注意事项【指南】  Python正则表达式进阶教程_复杂匹配与分组替换解析  Laravel怎么解决跨域问题_Laravel配置CORS跨域访问  Laravel定时任务怎么设置_Laravel Crontab调度器配置  Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程  html文件怎么打开证书错误_https协议的html打开提示不安全【指南】  Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】  Python结构化数据采集_字段抽取解析【教程】  *服务器网站为何频现安全漏洞?