AOPによるダイナミックデータソースの実現
AOPによるダイナミックデータソースの実現
ページ要求がservice層に処理されると、呼び出し方法におけるブロッキングタイプDataSourceIntercepterをトリガし、現在スレッド中のデータソースをHandlerDataSourceに格納されているhandle ThredLocalのセットに引用し、service層内のトランザクションブロックに入り、事務管理を開始し、DataSourceTransation ManagerのBedogin接続方法データベースを取得します。AbstractRoutingDataSource類のget Connection方法を呼び出しますが、この方法の中のdetermine Target DataSource方法はdetermine Curent Look up Key方法を呼び出すことができます。しかし、AbstractRoutingDataSource類のこの方法は、自分で実現する必要があります。DataSourceInterceptorブロック類にハンドル・ThredLocalのオブジェクトに格納されたデータソースをkey値を引用して取り出します。このようにdetermine Target Data Source方法のDataSource=this.reolvedDataSources.get(lookukey);具体的なデータソース接続を取得します。
determine Target Data Source().get Connection();
例:
ソース分析の実現:
ページ要求がservice層に処理されると、呼び出し方法におけるブロッキングタイプDataSourceIntercepterをトリガし、現在スレッド中のデータソースをHandlerDataSourceに格納されているhandle ThredLocalのセットに引用し、service層内のトランザクションブロックに入り、事務管理を開始し、DataSourceTransation ManagerのBedogin接続方法データベースを取得します。AbstractRoutingDataSource類のget Connection方法を呼び出しますが、この方法の中のdetermine Target DataSource方法はdetermine Curent Look up Key方法を呼び出すことができます。しかし、AbstractRoutingDataSource類のこの方法は、自分で実現する必要があります。DataSourceInterceptorブロック類にハンドル・ThredLocalのオブジェクトに格納されたデータソースをkey値を引用して取り出します。このようにdetermine Target Data Source方法のDataSource=this.reolvedDataSources.get(lookukey);具体的なデータソース接続を取得します。
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
determine Target Data Source().get Connection();
例:
@Service("userService")
public class UserServiceImpl implements UserService{
@Resource
private UserDao userDao;
@Override
public int save(User user) {
// TODO Auto-generated method stub
return userDao.save(user);
}
@Override
public int deleteById(int id) {
// TODO Auto-generated method stub
return userDao.deleteById(id);
}
@Override
public int update(User user) {
// TODO Auto-generated method stub
return userDao.update(user);
}
}
// ThreadLocal , ;
public class HandlerDataSource {
private static ThreadLocal handlerThredLocal = new ThreadLocal();
/**
* @desction: AOP
* @param: [datasource]
*/
public static void putDataSource(String datasource) {
handlerThredLocal.set(datasource);
}
/**
* @desction: AbstractRoutingDataSource , key
* @date: 2017/8/21
*/
public static String getDataSource() {
return handlerThredLocal.get();
}
/**
* @desction:
*/
public static void clear() {
handlerThredLocal.remove();
}
}
// spring AbstractRoutingDataSource determineCurrentLookupKey, key ;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// TODO Auto-generated method stub
return HandlerDataSource.getDataSource();
}
}
//AOP , ,
@Aspect
@Component
@Order(-999)// AOP , ,
public class DataSourceInterceptor {
@Pointcut("execution(* *.service.impl.*.save(..))")
public void save() {
};
@Before("save()")
public void beforeFirst1(JoinPoint jp) {
System.out.println("save dataSource1");
HandlerDataSource.putDataSource("dataSource1");
}
@Pointcut("execution(* *.service.impl.*.update(..))")
public void update() {
};
@Before("update()")
public void beforeFirst2(JoinPoint jp) {
System.out.println("update dataSource2");
HandlerDataSource.putDataSource("dataSource2");
}
}
ソース分析の実現:
/**
* Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
* calls to one of various target DataSources based on a lookup key. The latter is usually
* (but not necessarily) determined through some thread-bound transaction context.
*
* @author Juergen Hoeller
* @since 2.0.1
* @see #setTargetDataSources
* @see #setDefaultTargetDataSource
* @see #determineCurrentLookupKey()
*/
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
private Map
2)注釈タイプの実現://aop
@Aspect
@Component
@Order(-999)
public class HandlerDataSourceAop {
//@within
//@annotation
@Pointcut("@within(dataSource.handler.DynamicSwitchDataSource)||@annotation(dataSource.handler.DynamicSwitchDataSource)")
public void pointcut() {}
@Before("pointcut()") //
public void testBefore(JoinPoint point){
// class
Class> className = point.getTarget().getClass();
DynamicSwitchDataSource dataSourceAnnotation = className.getAnnotation(DynamicSwitchDataSource.class);
if (dataSourceAnnotation != null ) {
//
String methodName = point.getSignature().getName();
//
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DATA_SOURCE_A;
try {
Method method = className.getMethod(methodName, argClass);
if (method.isAnnotationPresent(DynamicSwitchDataSource.class)) {
DynamicSwitchDataSource annotation = method.getAnnotation(DynamicSwitchDataSource.class);
dataSource = annotation.dataSource();
System.out.println("DataSource Aop ====> "+dataSource);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DataSourceContextHolder.setDbType(dataSource);
}
}
@After("pointcut()") //
public void testAfter(JoinPoint point){
// class
Class> className = point.getTarget().getClass();
DynamicSwitchDataSource dataSourceAnnotation = className.getAnnotation(DynamicSwitchDataSource.class);
if (dataSourceAnnotation != null ) {
//
String methodName = point.getSignature().getName();
//
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DATA_SOURCE_A;
try {
Method method = className.getMethod(methodName, argClass);
if (method.isAnnotationPresent(DynamicSwitchDataSource.class)) {
DynamicSwitchDataSource annotation = method.getAnnotation(DynamicSwitchDataSource.class);
dataSource = annotation.dataSource();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(dataSource != null && !DataSourceContextHolder.DATA_SOURCE_A.equals(dataSource)) DataSourceContextHolder.clearDbType();
}
}
}
カスタムコメント;@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DynamicSwitchDataSource {
String dataSource() default "";
}
@Service("userService")
@DynamicSwitchDataSource
public class UserServiceImpl implements UserService{
@Resource
private UserDao userDao;
@DynamicSwitchDataSource(dataSource = "datasource1")
public int save(User user) {
// TODO Auto-generated method stub
return userDao.save(user);
}
@Override
public int deleteById(int id) {
// TODO Auto-generated method stub
return userDao.deleteById(id);
}
@DynamicSwitchDataSource(dataSource = "datasource2")
public int update(User user) {
// TODO Auto-generated method stub
return userDao.update(user);
}
}