spring aop, la configuration maître-esclave mysql réalise la séparation lecture-écriture. Ensuite, j'enregistrerai mon processus de configuration et les problèmes rencontrés pour faciliter la prochaine opération, j'espère aussi aider quelques amis.
1. Utilisez le mécanisme d'interception Spring Aop pour sélectionner dynamiquement les sources de données.
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. Utilisez AbstractRoutingDataSource de Spring pour résoudre le problème de plusieurs sources de données
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class ChooseDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return HandleDataSource.getDataSource(); } }
4. Utilisez ThreadLocal pour résoudre le problème de sécurité des threads.
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. Définissez une classe d'aspect de source de données, accédez-y via aop et configurez-la dans le fichier de configuration Spring, afin que les annotations aop ne soient pas utilisées.
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. Configurez 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. Utilisez des annotations pour sélectionner dynamiquement les sources de données et lire et écrire respectivement des bibliothèques.
@DataSource("write") public void update(User user) { userMapper.update(user); } @DataSource("read") public Document getDocById(long id) { return documentMapper.getById(id); }
Testez l'opération d'écriture : vous pouvez modifier les données via l'application, modifier les données de la base de données principale et constater que les données de la base de données esclave sont mises à jour de manière synchrone, de sorte que les opérations d'écriture définies sont toutes écrites à la base de données
Testez l'opération de lecture : modifiez les données de la base de données esclave en arrière-plan, vérifiez que les données de la base de données principale n'ont pas été modifiées, actualisez la page de l'application et constatez que le les données de la base de données esclave sont lues, indiquant que la séparation de la lecture et de l'écriture est correcte.
Résumé des problèmes rencontrés :
Problème 1 : Le projet est un projet maven et utilise le mécanisme Spring aop En plus du package jar de base de spring, il doit également. utilisez le package jar Il y a aspectj.jar, aspectjweaver.jar et aopalliance.jar. Vérifiez le pom dans le projet et constatez que le package de dépendances est manquant. Puisque l'entrepôt local n'a pas ces jars, recherchez la bibliothèque centrale maven qui. peut permettre le téléchargement de packages jar, le configurer dans maven et mettre à jour automatiquement :
<repository> <id>nexus</id> <name>nexus</name> <url>http://repository.sonatype.org/content/groups/public/</url> <layout>default</layout> </repository>
Configurez les jars dont dépend le projet, principalement parce que ces deux-là sont manquants.
<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>
Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'apprentissage de chacun, et j'espère également que tout le monde soutiendra le site Web PHP chinois. .
Pour des explications plus détaillées sur l'utilisation de Spring Aop pour implémenter la séparation de lecture et d'écriture MySQL dans la couche métier, veuillez faire attention au site Web PHP chinois pour les articles connexes !