>Java >java지도 시간 >비즈니스 계층에서 mysql의 읽기-쓰기 분리를 구현하기 위해 spring aop를 사용하는 방법에 대한 자세한 설명

비즈니스 계층에서 mysql의 읽기-쓰기 분리를 구현하기 위해 spring aop를 사용하는 방법에 대한 자세한 설명

高洛峰
高洛峰원래의
2017-01-24 10:49:311914검색

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를 통해 액세스한 후 스프링 구성 파일에서 구성하면 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);
}

쓰기 작업 테스트: 애플리케이션을 통해 데이터를 수정하고, 기본 데이터베이스 데이터를 수정하고, 슬레이브 데이터베이스의 데이터가 동기적으로 업데이트되어 정의된 쓰기 작업이 모두 데이터베이스

읽기 작업 테스트: 백그라운드에서 슬레이브 데이터베이스의 데이터를 수정하고, 기본 데이터베이스의 데이터가 수정되지 않았는지 확인하고, 애플리케이션 페이지를 새로 고치고, 슬레이브 데이터베이스를 읽었으며 이는 읽기와 쓰기의 분리가 정상임을 나타냅니다.

발생한 문제 요약:

문제 1: 프로젝트는 Maven 프로젝트이며 Spring의 핵심 jar 패키지 외에도 Spring AOP 메커니즘을 사용해야 합니다. jar 패키지를 사용하십시오. 프로젝트에서 pom을 확인하고 로컬 웨어하우스에 이러한 jar가 없으므로 Maven 중앙 라이브러리를 찾으십시오. jar 패키지 다운로드를 제공하고, 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>


위 내용은 이 글의 전체 내용입니다. 모든 분들의 학습에 도움이 되길 바라며, 또한 PHP 중국어 홈페이지를 응원해주시기 바랍니다.

spring Aop를 사용하여 비즈니스 레이어 mysql 읽기 및 쓰기 분리 구현에 대한 자세한 설명을 보려면 PHP 중국어 웹사이트에서 관련 기사를 주목하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.