Alter dataSource in Spring By AOP And Annotation

简介:

Here is an article of how to use AOP and Annotation mechanism to alter dataSource elegantly.
First, I want make sure that everyone knows how to build multiple dataSource. Please check this article Dynamic-DataSource-Routing
After this, we will have a DataSourceHolder class, in the case above, it is called CustomerContextHolder.
Let's remove the customer logic and make Holder purer.

public class DataSourceHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static String getCurrentDataSource() {
        return (String) contextHolder.get();
    }   

    public static void setDataSource(String dataSource){
        contextHolder.set(dataSource);
    }

    public static void setDefaultDataSource(){
        contextHolder.set(null);
    }

    public static void clearCustomerType() {
        contextHolder.remove();   
    }  

}

When should we call setDataSource

In the project I take charge of, they invoke setDataSource in each controller. IMHO, I don't think it's an elegant way. I think dataSource should be an attribute of a DAO method or a Service method. And since transactionManager is aadvice to Service method in this project, dataSource must be an attribute of a Service method.

Use Annotation to describe a Service method

First, we should define a runtime annotation.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    String name() default DataSource.DEFAULT;

    public final static String DEFAULT     = "foo";

    public final static String BAR           = "bar";

    public final static String BAZ           = "baz";

}

Then, we use the annotation to describe a Service method.

    @Override
    @DataSource(name=DataSource.BAR)
    public Object getSomething() {
        return dao.getSomething();
    }

Use AOP to invoke setDataSource

First, define a pointcut.

        <aop:pointcut id="serviceWithAnnotation"
    expression="@annotation(com.yourpackageName.DataSource)" />

Second, define a advisor.

    <aop:advisor advice-ref="dataSourceExchange" pointcut-ref="serviceWithAnnotation" order="1"/>
    <bean id="dataSourceExchange" class="com.yourpackageName.DataSourceExchange"/>

Now, the AOP mechanism will make sure that some methods of DataSourceExchange will run if Service method whichDataSource annotation decorated is invoked.

Last, define DataSourceExchange.

class DataSourceExchange implements MethodInterceptor {

    private Logger             logger = LoggerFactory.getLogger(DataSourceExchange.class);

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Method name : "
                + invocation.getMethod().getName());
        System.out.println("Method arguments : "
                + Arrays.toString(invocation.getArguments()));
        DataSource dataSource = this.getDataSource(invocation);
        if(dataSource == null) {
            logger.error("dataSource in invocation is null");
        }
        String dbnameString = dataSource.name();
        Object result;
        try {
            DataSourceHolder.setDataSource(dbnameString);
            result = invocation.proceed();
        } finally {
            DataSourceHolder.setDefaultDataSource();
        }
        return result;
    }

    private DataSource getDataSource(MethodInvocation invocation) throws Throwable {
  //TODO
  }

The hardest part in this bunch of code is how should us impl the getDataSource method. I spent several hours of this method. First, I've seen some code online, which tell me it's quite simple to do this. Just like the code below

    private DataSource getDataSource(MethodInvocation invocation) throws Throwable {
        return invocation.getMethod().getAnnotation(DataSource.class);
  }

But it won't work, because invocation.getMethod() will not return the method you defined above, it will return a proxymethod. It's a mechanism called Proxy in Spring framework.
So we should find out the real method.
Again I searched stackoverflow.com, some answers tell me AnnotationUtils.findAnnotation will be useful to me.

    private DataSource getDataSource(MethodInvocation invocation) throws Throwable {
        return AnnotationUtils.findAnnotation(invocation.getMethod(), DataSource.class);
  }

AnnotationUtils.findAnnotation will recursively find the super class of the proxy method, to find the annotation decorated on the real method you defined above.
But it's not the complete answer. 
Let's see the source code of AnnotationUtils.findAnnotation

    /**
     * Get a single {@link Annotation} of <code>annotationType</code> from the supplied {@link Method},
     * traversing its super methods if no annotation can be found on the given method itself.
     * <p>Annotations on methods are not inherited by default, so we need to handle this explicitly.
     * @param method the method to look for annotations on
     * @param annotationType the annotation class to look for
     * @return the annotation found, or <code>null</code> if none found
     */
    public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {
        A annotation = getAnnotation(method, annotationType);
        Class<?> cl = method.getDeclaringClass();
        if (annotation == null) {
            annotation = searchOnInterfaces(method, annotationType, cl.getInterfaces());
        }
        while (annotation == null) {
            cl = cl.getSuperclass();
            if (cl == null || cl == Object.class) {
                break;
            }
            try {
                Method equivalentMethod = cl.getDeclaredMethod(method.getName(), method.getParameterTypes());
                annotation = getAnnotation(equivalentMethod, annotationType);
                if (annotation == null) {
                    annotation = searchOnInterfaces(method, annotationType, cl.getInterfaces());
                }
            }
            catch (NoSuchMethodException ex) {
                // We're done...
            }
        }
        return annotation;
    }

Here we have a precondition to let AnnotationUtils.findAnnotation works, that is the Proxy mechanism is implemented by inherit. There are two ways of proxy in Spring. What is the difference between JDK dynamic proxy and CGLibCGLib is implemented by inherit but JDK dynamic proxy is not.
So AnnotationUtils.findAnnotation won't work for JDK dynamic proxy. We should write some more code to deal with this situation. Here is my final solution.

    private DataSource getDataSource(MethodInvocation invocation) throws Throwable {
        DataSource dataSource = AnnotationUtils.findAnnotation(invocation.getMethod(), DataSource.class);
        if(dataSource != null) {
            return dataSource; // if use CGlib proxy
        }

        Method proxyedMethod = invocation.getMethod(); // or use jdk proxy
        Method realMethod = invocation.getThis().getClass().getDeclaredMethod(proxyedMethod.getName(), proxyedMethod.getParameterTypes());
        dataSource =  AnnotationUtils.findAnnotation(realMethod, DataSource.class);
        return dataSource;
    }

Summary

In this case, I learnt

  • how to use AOP and annotation
  • there is a mechanism called proxy used by Spring
  • there are two implements of proxy mechanism, they are different
  • how to use reflection in Java

I hope it would help u.


目录
相关文章
|
21天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
28 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
6天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
15 1
|
2天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
6 0
|
2月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
1月前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
51 2
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
118 9
|
30天前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
44 0
|
2月前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
3月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
42 0
Spring高手之路22——AOP切面类的封装与解析
|
3月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
51 0