Spring-基于Spring使用自定义注解及Aspect实现数据库切换操作

实现思路

重写Spring的AbstractRoutingDataSource抽象类的determineCurrentLookupKey方法。

我们来看下Spring-AbstractRoutingDataSource的源码

AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey。

Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
.......
return dataSource;

lookupKey为数据源标识,因此通过重写这个查找数据源标识的方法就可以让spring切换到指定的数据源.

从变量定义中可以知道resolvedDataSources为Map类型的对象。

private Map<Object, DataSource> resolvedDataSources;

示例

步骤一 新建Maven工程

依赖如下: pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.artisan</groupId>
	<artifactId>dynamicDataSource</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>dynamicDataSource</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<file.encoding>UTF-8</file.encoding>
		<spring.version>4.3.9.RELEASE</spring.version>
		<servlet.version>3.1.0</servlet.version>
		<aspectj.version>1.8.1</aspectj.version>
		<commons-dbcp.version>1.4</commons-dbcp.version>
		<jetty.version>8.1.8.v20121106</jetty.version>
		<log4j.version>1.2.17</log4j.version>
		<log4j2.version>2.8.2</log4j2.version>
		<testng.version>6.8.7</testng.version>
		<oracle.version>11.2.0.4.0</oracle.version>
		<jstl.version>1.2</jstl.version>
	</properties>

	<dependencies>
		<!-- spring 依赖 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>${commons-dbcp.version}</version>
		</dependency>

		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${aspectj.version}</version>
		</dependency>

		<dependency>
			<groupId>org.testng</groupId>
			<artifactId>testng</artifactId>
			<version>${testng.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
			<scope>test</scope>
		</dependency>

		<!-- oracle jdbc driver -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>${oracle.version}</version>
		</dependency>

		<dependency>
			<groupId>org.testng</groupId>
			<artifactId>testng</artifactId>
			<version>${testng.version}</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
			<scope>test</scope>
		</dependency>
		<!--
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>${log4j.version}</version>
		</dependency>
	 	-->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>${log4j2.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>${log4j2.version}</version>
		</dependency>

	</dependencies>

	<build>
		<!-- 使用JDK1.7编译 -->
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

步骤二 继承AbstractRoutingDataSource并重写determineCurrentLookupKey方法获取特定数据源

package com.artisan.dynamicDB;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 *
 *
 * @ClassName: DynamicDataSource
 *
 * @Description:
 *  AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现数据源的route的核心
 *  .需要重写该方法
 *
 * @author: Mr.Yang
 *
 * @date: 2017年7月24日 下午8:28:46
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

	@Override
	protected Object determineCurrentLookupKey() {
		return DynamicDataSourceHolder.getDataSource();
	}
}

步骤三 创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识

package com.artisan.dynamicDB;

/**
 *
 *
 * @ClassName: DynamicDataSourceHolder
 *
 * @Description:创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识
 *
 * @author: Mr.Yang
 *
 * @date: 2017年7月24日 下午8:23:50
 */
public class DynamicDataSourceHolder {

 /**
 * 数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
 */
 private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<String>();

 /**
 *
 *
 * @Title: setDataSource
 *
 * @Description: 设置数据源
 *
 * @param dataSource
 *
 * @return: void
 */
 public static void setDataSource(String dataSource) {
 dataSourceHolder.set(dataSource);
 }

 /**
 *
 *
 * @Title: getDataSource
 *
 * @Description: 获取数据源
 *
 * @return
 *
 * @return: String
 */
 public static String getDataSource() {
 return dataSourceHolder.get();
 }

 /**
 *
 *
 * @Title: clearDataSource
 *
 * @Description: 清除数据源
 *
 *
 * @return: void
 */
 public static void clearDataSource() {
 dataSourceHolder.remove();
 }
}

步骤四 配置多个数据源和DynamicDataSource的bean

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd
 http://www.springframework.org/schema/tx
 http://www.springframework.org/schema/tx/spring-tx.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd
 http://www.springframework.org/schema/util
 http://www.springframework.org/schema/util/spring-util.xsd">

 <!-- 基类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
 <context:component-scan base-package="com.artisan"/>

 <!-- 使用context命名空间,在xml文件中配置数据库的properties文件 -->
 <context:property-placeholder location="classpath:jdbc.properties" />

 <!-- 配置数据源--> 

 <!-- 主站点的数据源 -->
 <bean id="dataSourcePR" class="org.apache.commons.dbcp.BasicDataSource"
 destroy-method="close"
 p:driverClassName="${jdbc.driverClassNamePR}"
 p:url="${jdbc.urlPR}"
 p:username="${jdbc.usernamePR}"
 p:password="${jdbc.passwordPR}" />

 <!-- 备用站点的数据源 -->
 <bean id="dataSourceDR" class="org.apache.commons.dbcp.BasicDataSource"
 destroy-method="close"
 p:driverClassName="${jdbc.driverClassNameDR}"
 p:url="${jdbc.urlDR}"
 p:username="${jdbc.usernameDR}"
 p:password="${jdbc.passwordDR}" /> 

 <!-- 主站点cc实例数据源 -->
 <bean id="dataSourceCC" class="org.apache.commons.dbcp.BasicDataSource"
 destroy-method="close"
 p:driverClassName="${jdbc.driverClassNameCC}"
 p:url="${jdbc.urlCC}"
 p:username="${jdbc.usernameCC}"
 p:password="${jdbc.passwordCC}" />

 <bean id="dynamicDataSource" class="com.artisan.dynamicDB.DynamicDataSource">
 <property name="targetDataSources" ref="dynamicDatasourceMap" />
 <!-- 默认数据源 -->
 <property name="defaultTargetDataSource" ref="dataSourcePR" />
 </bean>

 <!-- 指定lookupKey和与之对应的数据源 -->
 <util:map id="dynamicDatasourceMap" key-type="java.lang.String">
 <entry key="dataSourcePR" value-ref="dataSourcePR" />
 <entry key="dataSourceDR" value-ref="dataSourceDR" />
 <entry key="dataSourceCC" value-ref="dataSourceCC" />
 </util:map>

 <!-- 配置Jdbc模板 JdbcTemplate使用动态数据源的配置 -->
 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
 p:dataSource-ref="dynamicDataSource" />

 <!-- 配置数据源注解的拦截规则,比如拦截service层或者dao层的所有方法,这里拦截了com.artisan下的全部方法 -->
 <bean id="dataSourceAspect" class="com.artisan.dynamicDB.DataSourceAspect" />
 <aop:config>
  <aop:aspect ref="dataSourceAspect">
  <!-- 拦截所有XXX方法 -->
  <aop:pointcut id="dataSourcePointcut" expression="execution(* com.artisan..*(..))"/>
  <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
  </aop:aspect>
 </aop:config>

 <!-- 配置事务管理器 -->
 <bean id="transactionManager"
 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
 p:dataSource-ref="dynamicDataSource" />

 <!-- 通过AOP配置提供事务增强,让com.artisan包下所有Bean的所有方法拥有事务 -->
 <aop:config proxy-target-class="true">
 <aop:pointcut id="serviceMethod"
 expression="(execution(* com.artisan..*(..))) and (@annotation(org.springframework.transaction.annotation.Transactional))" />
 <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
 </aop:config>
 <tx:advice id="txAdvice" transaction-manager="transactionManager">
 <tx:attributes>
 <tx:method name="*" />
 </tx:attributes>
 </tx:advice>
</beans>

配置到这里,我们就可以使用多个数据源了,只需要在操作数据库之前只要DynamicDataSourceHolder.setDataSource(“dataSourcePR”)即可切换到数据源dataSourcePR并对数据库dataSourcePR进行操作了。

问题:每次使用都需要调用DynamicDataSourceHolder#setDataSource,十分繁琐,并且难以维护。

我们可以通过Spring的AOP和注解, 直接通过注解的方式指定需要访问的数据源。 继续改进下吧

步骤五 定义名为@DataSource的注解

package com.artisan.dynamicDB;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *
 *
 * @ClassName: DataSource
 *
 *
 * @Description: 注解@DataSource既可以加在方法上,也可以加在接口或者接口的实现类上,优先级别:方法>实现类>接口。
 *  如果接口、接口实现类以及方法上分别加了@DataSource注解来指定数据源,则优先以方法上指定的为准。
 *
 * @author: Mr.Yang
 *
 * @date: 2017年7月24日 下午9:59:29
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
 // 和配置文件中 dynamicDatasourceMap中的key保持一致
 public static String PR_RB = "dataSourcePR";

 public static String DR_RB = "dataSourceDR";

 public static String PR_CC = "dataSourceCC";

 /**
 *
 *
 * @Title: name
 *
 * @Description: 如果仅标注@DataSource 默认为PR_RB数据库实例
 *
 * @return
 *
 * @return: String
 */
 String name() default DataSource.PR_RB;

}

步骤六 定义AOP切面以便拦截所有带有注解@DataSource的方法,取出注解的值作为数据源标识放到DynamicDataSourceHolder的线程变量中

package com.artisan.dynamicDB;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

/**
 *
 *
 * @ClassName: DataSourceAspect
 *
 * @Description:
 *  定义AOP切面以便拦截所有带有注解@DataSource的方法,取出注解的值作为数据源标识放到DBContextHolder的线程变量中
 *
 * @author: Mr.Yang
 *
 * @date: 2017年7月25日 上午10:51:41
 */
public class DataSourceAspect {

 /**
 *
 *
 * @Title: intercept
 *
 * @Description: 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
 *
 * @param point
 * @throws Exception
 *
 * @return: void
 */
 public void intercept(JoinPoint point) throws Exception {
 Class<?> target = point.getTarget().getClass();
 MethodSignature signature = (MethodSignature) point.getSignature();
 // 默认使用目标类型的注解,如果没有则使用其实现接口的注解
 for (Class<?> clazz : target.getInterfaces()) {
 resolveDataSource(clazz, signature.getMethod());
 }
 resolveDataSource(target, signature.getMethod());
 }

 /**
 *
 *
 * @Title: resolveDataSource
 *
 * @Description: 提取目标对象方法注解和类型注解中的数据源标识
 *
 * @param clazz
 * @param method
 *
 * @return: void
 */
 private void resolveDataSource(Class<?> clazz, Method method) {
 try {
 Class<?>[] types = method.getParameterTypes();
 // 默认使用类型注解
 if (clazz.isAnnotationPresent(DataSource.class)) {
 DataSource source = clazz.getAnnotation(DataSource.class);
 DynamicDataSourceHolder.setDataSource(source.name());
 }
 // 方法注解可以覆盖类型注解
 Method m = clazz.getMethod(method.getName(), types);
 if (m != null && m.isAnnotationPresent(DataSource.class)) {
 DataSource source = m.getAnnotation(DataSource.class);
 DynamicDataSourceHolder.setDataSource(source.name());
 }
 } catch (Exception e) {
 System.out.println(clazz + ":" + e.getMessage());
 }
 }
}

步骤七 在spring配置文件中配置拦截规则

 <!-- 配置数据源注解的拦截规则,比如拦截service层或者dao层的所有方法,这里拦截了com.artisan下的全部方法 -->
 <bean id="dataSourceAspect" class="com.artisan.dynamicDB.DataSourceAspect" />
 <aop:config>
  <aop:aspect ref="dataSourceAspect">
  <!-- 拦截所有XXX方法 -->
  <aop:pointcut id="dataSourcePointcut" expression="execution(* com.artisan..*(..))"/>
  <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
  </aop:aspect>
 </aop:config>

步骤八 使用注解切换多数据源

ExtractDataService.java
package com.artisan.extractService;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Service;

import com.artisan.dynamicDB.DataSource;

/**
 *
 *
 * @ClassName: ExtractDataService
 *
 * @Description: 业务类,这里暂时作为测试多数据源切换用
 *
 * @author: Mr.Yang
 *
 * @date: 2017年7月24日 下午9:07:38
 */

@Service
public class ExtractDataService {

 private static final Logger logger = LogManager
 .getLogger(ExtractDataService.class.getName());

 private JdbcTemplate jdbcTemplate;

 @Autowired
 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
 this.jdbcTemplate = jdbcTemplate;
 }

 /**
 *
 *
 * @Title: selectDataFromPR
 *
 * @Description:
 *
 *
 * @return: void
 */
 @DataSource(name = DataSource.PR_RB)
 public void selectDataFromPR_RB() {
 String sql = "select subs_id from owe_event_charge where event_inst_id = 10229001 ";

 jdbcTemplate.query(sql, new RowCallbackHandler() {

 @Override
 public void processRow(ResultSet rs) throws SQLException {
 logger.info(rs.getInt("subs_id"));
 }
 });
 }

 @DataSource(name = DataSource.DR_RB)
 public void selectDataFromDR_RB() {
 // 改为通过注解指定DB
 // DynamicDataSourceHolder.setDataSource(DBContextHolder.DATA_SOURCE_DR);
 String sql = " select a.task_comments from nm_task_type a where a.task_name = 'ALARM_LOG_LEVEL' ";
 jdbcTemplate.query(sql, new RowCallbackHandler() {

 @Override
 public void processRow(ResultSet rs) throws SQLException {
 logger.info(rs.getString("task_comments"));
 }
 });
 }

 @DataSource(name = DataSource.PR_CC)
 public void selectDataFromPR_CC() {
 // DBContextHolder.setDataSource(DBContextHolder.DATA_SOURCE_CC);
 String sql = "select acc_nbr from acc_nbr where acc_nbr_id = 82233858 ";
 jdbcTemplate.query(sql, new RowCallbackHandler() {

 @Override
 public void processRow(ResultSet rs) throws SQLException {
 logger.info(rs.getString("acc_nbr"));
 }
 });

 }
}

步骤九 测试

package com.artisan;
import java.io.IOException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.artisan.extractService.ExtractDataService;

/**
 *
 *
 * @ClassName: App
 *
 * @Description: 入口类
 *
 * @author: Mr.Yang
 *
 * @date: 2017年7月24日 下午8:50:25
 */
public class App {
 public static void main(String[] args) {
 try {
 // 加载日志框架 log4j2
 LoggerContext context = (LoggerContext) LogManager
 .getContext(false);
 ResourceLoader loader = new PathMatchingResourcePatternResolver();
 Resource resource = loader.getResource("classpath:log4j2.xml");

 context.setConfigLocation(resource.getFile().toURI());

 // 加载spring配置信息
 ApplicationContext ctx = new ClassPathXmlApplicationContext(
 "classpath:spring-context.xml");
 // 从容器中获取Bean
 ExtractDataService service = ctx.getBean("extractDataService",
 ExtractDataService.class);
 // 从PR的RB实例中获取数据
 service.selectDataFromPR_RB();
 // 从DR的RB实例中获取数据
 service.selectDataFromDR_RB();
 // 从PR的CC实例中获取数据
 service.selectDataFromPR_CC();

 } catch (IOException e) {
 e.printStackTrace();
 }

 }
}

其他代码

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- log4j2使用说明:
使用方式如下:
private static final Logger logger = LogManager.getLogger(实际类名.class.getName());
-->

<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="info" monitorInterval="180">

 <!-- 文件路径和文件名称,方便后面引用 -->
 <Properties>
 <Property name="backupFilePatch">D:/workspace/workspace-sts/backupOracle/log/</Property>
 <Property name="fileName">backupOracle.log</Property>
 </Properties>
 <!--先定义所有的appender-->
 <appenders>
 <!--这个输出控制台的配置-->
 <Console name="Console" target="SYSTEM_OUT">
  <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
  <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY" />
  <!-- 输出日志的格式-->
  <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" />
 </Console>

 <!--这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
 <RollingFile name="RollingFile" fileName="${backupFilePatch}${fileName}"
  filePattern="${backupFilePatch}$${date:yyyy-MM}/app-%d{yyyyMMddHHmmssSSS}.log.gz">
  <PatternLayout
  pattern="%d{yyyy.MM.dd 'at' HH:mm:ss.SSS z} %-5level %class{36} %L %M - %msg%xEx%n" />
  <!-- 日志文件大小 -->
  <SizeBasedTriggeringPolicy size="20MB" />
  <!-- 最多保留文件数 DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
  <DefaultRolloverStrategy max="20"/>
 </RollingFile>
 </appenders>

 <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
 <loggers>
 <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
 <logger name="org.springframework" level="INFO"></logger>
 <logger name="org.mybatis" level="INFO"></logger>
 <root level="trace">
  <appender-ref ref="RollingFile"/>
  <appender-ref ref="Console"/>
  </root>
 </loggers>
</configuration>

jdbc.properties

##########################
##
##
## dbcp datasource pool ,basic configuration first.
## the other parameters keep default for now , you can change them if you want
##
##
##########################

#Database in Lapaz
jdbc.driverClassNamePR=oracle.jdbc.driver.OracleDriver
jdbc.urlPR=jdbc:oracle:thin:@172.25.243.4:1521:xx
jdbc.usernamePR=xxx
jdbc.passwordPR=xxxxxxxx

#Database in Scluz
jdbc.driverClassNameDR=oracle.jdbc.driver.OracleDriver
jdbc.urlDR=jdbc:oracle:thin:@172.25.246.1:1521:xx
jdbc.usernameDR=xxx
jdbc.passwordDR=xxxxxxx

#Database in Lapaz
jdbc.driverClassNameCC=oracle.jdbc.driver.OracleDriver
jdbc.urlCC=jdbc:oracle:thin:@172.25.243.3:1521:xx
jdbc.usernameCC=xxx
jdbc.passwordCC=xxxxxx

运行结果:

代码

https://github.com/yangshangwei/DynamicDataSource

以上这篇Spring-基于Spring使用自定义注解及Aspect实现数据库切换操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java注解Annotation与自定义注解详解

    一:Java注解简介 开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解. 下面列举开发中常见的注解 @Override:用于标识该方法继承自超类, 当父类的方法被删除或修改了,编译器会提示错误信息(我们最经常看到的toString()方法上总能看到这货) @Deprecated:表示该类或者该方法已经不推荐使用,已经过期了,如果用户还是要使用,会生成编译的警告 @SuppressWarnings:用于忽略的编译器警告信息

  • 谈谈Java中自定义注解及使用场景

    Java自定义注解一般使用场景为:自定义注解+拦截器或者AOP,使用自定义注解来自己设计框架,使得代码看起来非常优雅.本文将先从自定义注解的基础概念说起,然后开始实战,写小段代码实现自定义注解+拦截器,自定义注解+AOP. 一. 什么是注解(Annotation) Java注解是什么,以下是引用自维基百科的内容 Java注解又称Java标注,是JDK5.0版本开始支持加入源代码的特殊语法元数据. Java语言中的类.方法.变量.参数和包等都可以被标注.和Javadoc不同,Java标注可以通过反

  • spring Retryable注解实现重试详解

    spring-boot:1.5.3.RELEASE,spring-retry-1.2.0.RELEASE 使用方法 引入pom // 版本号继承spring-boot依赖管理的pom <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency>

  • Spring AOP的使用详解

    什么是AOP AOP(Aspect Oriented Programming 面向切面编程),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. 常用于日志记录,性能统计,安全控制,事务处理,异常处理等等. 定义AOP术语 切面(Aspect):切

  • Spring-基于Spring使用自定义注解及Aspect实现数据库切换操作

    实现思路 重写Spring的AbstractRoutingDataSource抽象类的determineCurrentLookupKey方法. 我们来看下Spring-AbstractRoutingDataSource的源码 AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey. Object lookupKey = determineCurrentLookupKey(); DataSource

  • Spring Boot如何通过自定义注解实现日志打印详解

    前言 在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些打印日志的代码相对而言还是比较重复的,那么我们可以通过什么样的方式来简化日志打印的代码呢? SpringBoot 通过自定义注解实现权限检查可参考我的博客:SpringBoot 通过自定义注解实现权限检查 正文 Spring AOP Spring AOP 即面向切面,是对OO

  • 详解使用Spring AOP和自定义注解进行参数检查

    引言 使用SpringMVC作为Controller层进行Web开发时,经常会需要对Controller中的方法进行参数检查.本来SpringMVC自带@Valid和@Validated两个注解可用来检查参数,但只能检查参数是bean的情况,对于参数是String或者Long类型的就不适用了,而且有时候这两个注解又突然失效了(没有仔细去调查过原因),对此,可以利用Spring的AOP和自定义注解,自己写一个参数校验的功能. 代码示例 注意:本节代码只是一个演示,给出一个可行的思路,并非完整的解决

  • Spring Boot中自定义注解结合AOP实现主备库切换问题

    摘要:本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的压力,在本篇文章中主要记录在Spring Boot中通过自定义注解结合AOP实现直接连接备库查询. 一.通过AOP 自定义注解实现主库到备库的切换 1.1 自定义注解 自定义注解如下代码所示 import java.lang.annotation.ElementType; import java.la

  • 基于spring如何实现事件驱动实例代码

    干货点 通过阅读该篇博客,你可以了解了解java的反射机制.可以了解如何基于spring生命周期使用自定义注解解决日常研发问题.具体源码可以点击链接. 问题描述 在日常研发中,经常会遇见业务A的某个action被触发后,同时触发业务B的action的行为,这种单对单的形式可以直接在业务A的action执行结束后直接调用业务B的action,那么如果是单对多的情况呢? 方案解决 这里提供一种在日常研发中经常使用到的机制,基于spring实现的事件驱动,即在业务A的action执行完,抛出一个事件,

  • Spring Boot整合mybatis使用注解实现动态Sql、参数传递等常用操作(实现方法)

    前面介绍了Spring Boot 整合mybatis 使用注解的方式实现数据库操作,介绍了如何自动生成注解版的mapper 和pojo类. 接下来介绍使用mybatis 常用注解以及如何传参数等数据库操作中的常用操作. 其实,mybatis 注解方式 和 XML配置方式两者的使用基本上相同,只有在构建 SQL 脚本有所区别,所以这里重点介绍两者之间的差异,以及增删改查,参数传递等注解的常用操作. 详解SpringBoot 快速整合Mybatis(去XML化+注解进阶)已经介绍过了,不清楚的朋友可

  • SpringBoot中利用AOP和拦截器实现自定义注解

    目录 前言 Spring实现自定义注解 1.引入相关依赖 2.相关类 Java实现自定义注解 通过Cglib实现 通过JDk动态代理实现 Cglib和JDK动态代理的区别 写在最后 前言 最近遇到了这样一个工作场景,需要写一批dubbo接口,再将dubbo接口注册到网关中,但是当dubbo接口异常的时候会给前端返回非常不友好的异常.所以就想要对异常进行统一捕获处理,但是对于这种service接口使用@ExceptionHandler注解进行异常捕获也是捕获不到的,应为他不是Controller的

  • 详解Spring 基于 Aspect 注解的增强实现

    整理文档,搜刮出一个Spring 基于 Aspect 注解的增强实现的代码,稍微整理精简一下做下分享 定义基本实体类 package com.advice; /** * @author Duoduo * @version 1.0 * @date 2017/4/25 23:41 */ public class Performer { public void doPerform() { System.out.println("Performer do perform ................

  • 基于spring@aspect注解的aop实现过程代码实例

    @AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格.通过在你的基于架构的 XML 配置文件中包含以下元素,@AspectJ 支持是可用的. 第一步:编写切面类 package com.dascom.hawk.app.web.tool; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.l

  • 在Spring 中使用@Aspect 控制自定义注解的操作

    Spring 中使用@Aspect 控制自定义注解 看这篇介绍@Aspect 1.定义系统日志注解类 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysLog { String value() default ""; } 2.定义切面处理类 package com.kxs.common.aspect; import com.google.gso

随机推荐