文档章节

Jodd 切面编程支持多数据源

韶华白首
 韶华白首
发布于 2017/05/27 19:12
字数 1185
阅读 71
收藏 0

最近因一个微信项目的升级需要,又打开了以前用Jodd写的一个小项目。Jodd这个微框架编译后,项目也就6到7M,麻雀虽小,五脏俱全。然而就一个weixin-java-tool就追加5M左右了。。。

是一个微信带参数二维码的问题:临时二维码有过期时间,然而只能带一个Int参数(坑爹,你TM让我传个字符串会死啊),但可以无限制生成。永久二维码可以传字符串,然而只能生成10万张。鉴于业务发展需要,10万张压根不可取,不过期也不安全。换言之只能生成临时二维码,传一个参数了。

项目的特性属于附属项目,需调用另一个项目的数据。但是两个项目依赖性比较大,使用rpc又得在两个项目集成rpc,通过http调用又太慢且要考虑安全问题。再说,传一个参数在那边执行几个sql查询再通过网络返回结果,这种损耗让人受不了。我想:要是Jodd支持多数据源那该多好啊!反正两个项目不需要分得太细,还不到拆分的时候。

于是,把Jodd的源码弄下来了:https://github.com/oblac/jodd.git

在官网上浏览了一遍,发现了这个叫Proxetta的组件:

官网上(http://jodd.org/doc/proxetta/)有详细的介绍,这里就不啰嗦了,直接引用原图:



 

有三种委托调用方式:

Proxy在程序启动的时候会生成一个目标类的子类,会重写父类需要代理执行的方法。Wrapper则会生成另一个类,持有对一个或多个目标类实例的引用,委托调用目标方法。InvokeReplacer会硬生生把目标类的方法替换掉。

于是,我选择Proxy的方式实现多数据源的切换:

1. 从官网得知,Jodd的切面可以通过自定义注解实现,从程序的入口DefaultAppCore.java中可以找到@Transaction是如何实现的。

protected ProxyAspect createTxProxyAspects() {
   return new ProxyAspect(
         AnnotationTxAdvice.class,
         new MethodAnnotationPointcut(jtxAnnotations) {
      @Override
      public boolean apply(MethodInfo methodInfo) {
         return
               isPublic(methodInfo) &&
               isTopLevelMethod(methodInfo) &&
               super.apply(methodInfo);
      }
   });
}

重点是AnnotationTxAdvice.class:

import static jodd.jtx.proxy.AnnotationTxAdviceSupport.manager;  //这个东西是静态的,SingletonScope,只管理一个数据源
public class AnnotationTxAdvice implements ProxyAdvice {

   public Object execute() throws Exception {
      Class type = targetClass();
      String methodName = targetMethodName();
      Class[] methodArgsTypes = createArgumentsClassArray();
      String methodDescription = targetMethodDescription();

      // read transaction mode from annotation
      JtxTransactionMode txMode = manager.getTxMode(type, methodName, methodArgsTypes, methodDescription);

      // request transaction
      JtxTransaction tx = null;
      try {
         String scope = manager.resolveScope(type, methodName);
         tx = manager.getJtxWorker().maybeRequestTransaction(txMode, scope);
         Object result = invoke();
         manager.getJtxWorker().maybeCommitTransaction(tx);
         return result;
      } catch (Exception ex) {
         manager.getJtxWorker().markOrRollbackTransaction(tx, ex);
         throw ex;
      }

   }
}

由此可得知在invoke()返回结果前,做了启动事务的操作,调用方法后,提交事务,发生异常则回滚。

到这一步一切都明了了,我需要替换AnnotationTxAdvice实现多数据源的切换。

2. 通过jndi的方式与mysql交互,在.props中定义多个数据源并在AppCore中获取,初始化多个数据源:

# 配置jndi数据源,第一个为默认数据源
hierarchical=test1,test2

test1.packageName=com.eyes.test1.entity
test1.dataSourceProvider=JNDI
test1.jndi=java:comp/env/TEST1
test1.tablePrefix=abc_
test1.tableUppercase=false
test1.columnUppercase=false

# 目录要与默认目录区分开,不能在子目录下
test2.packageName=com.eyes.test1.test2
test2.dataSourceProvider=JNDI
test2.jndi=java:comp/env/test2
test2.tablePrefix=haha_
test2.tableUppercase=false
test2.columnUppercase=false

3. 在重写的AppCore中一次性初始化多个AnnotationTxAdviceSupport放在一个Map中,并在PetiteContainer中注册为Bean。PetiteContainer的作用域也是SingletonScope,可以注册ThreadLocalScope的Bean,但这个map全局只有一个,无需考虑线程问题。

4. 自定义Annotation:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Source {
    String value();
}

@Source的作用在于在事务调用之前决定切换的数据源,value为数据源的名称。

5. 定义SourceAdvice,使用ThreadLocalScope的变量存放要使用的manager

public class SourceAdvice implements ProxyAdvice {

    @Override
    public Object execute() throws Exception {
        Class type = ProxyTarget.targetClass();
        String methodName = ProxyTarget.targetMethodName();
        Class[] methodArgsTypes = ProxyTarget.createArgumentsClassArray();
        Method method = type.getMethod(methodName,methodArgsTypes);
        Source source = method.getAnnotation(Source.class);
        String name = source.value();
        PetiteContainer pc = BeanUtil.declared.getProperty(ProxyTarget.target(), PetiteContainer.PETITE_CONTAINER_REF_NAME);
        HashMap map = pc.getBean(ANNOTATION_MANAGERS);
        AnnotationTxAdviceManager manager = (AnnotationTxAdviceManager) map.get(name);
        ThreadAdviceManagerHolder.set(manager);        //在这里决定使用哪个数据源管理器
        ThreadAdviceManagerHolder.setKey(name);
        Object result = ProxyTarget.invoke();        //@Transaction调用后。。。
        ThreadAdviceManagerHolder.set(Hierarchical.DEFAULT); //恢复默认数据源
        ThreadAdviceManagerHolder.setKey(Hierarchical.DEFAULT_KEY);
        return result;
    }
}

6. 配置proxetta:

ProxyAspect sourceProxy = new ProxyAspect(SourceAdvice.class,
        new MethodAnnotationPointcut(Source.class) {
            @Override
            public boolean apply(MethodInfo mi) {
                return isPublic(mi) &&
                        isTopLevelMethod(mi) &&
                        matchClassName(mi, "*Service") &&
                        super.apply(mi);
            }
        });
proxetta = ProxyProxetta.withAspects(sourceProxy,txProxy);    //使@Source,@Transaction生效
proxetta.setClassLoader(this.getClass().getClassLoader());

7. 调用

@Source("test2")
@Transaction(propagation = PROPAGATION_SUPPORTS,readOnly = false)
public Hi nihaoma(Hi e){
    ...
    return e;
}

总结:

在实现的过程中会遇到一些问题,然后把问题一一解决就OK了。

最好的学习方法就是看源码,参考源码,修改源码。本项目用的是Jodd v3.7.1,Jodd最新的版本是3.8.*,升级到java8了。

下班,回去吃麻麻寄来的粽子。

 

 

 

© 著作权归作者所有

韶华白首
粉丝 0
博文 7
码字总数 5391
作品 0
惠州
程序员
私信 提问
Jodd 3.4.4 发布,常用 Java 工具包

Jodd 3.4.4 发布了,改进包括:DbOom 支持 entityAware 模式,删除所有 *One* 类,修复bug,提升 Lagarto 解析器性能。 Jodd是一个普通开源Java包。你可以把Jodd想象成Java的"瑞士军刀",不仅...

oschina
2013/05/19
1K
6
spring多数据源的动态加载

近日来数据库的压力较大,所以做了读写分离。 为了尽量少的修改已有代码(数据库主从库还是要做的),在spring的配置中增加了数据源,使用注解的方式,增加一个切面,从controller层即确定访...

vvx1024
2016/10/20
0
0
Jodd 3.4.3 发布,常用 Java 工具包

Jodd 3.4.3 增加了 @PetiteProvier. Petite 人工注册更加顺畅,Petite 初始化方法可在三个生命周期点中调用;DbOom 支持缓存和一对多关系;改进 HTTP 对编码和字符集的支持;改进 Madvoc 动作...

oschina
2013/04/19
1K
3
Jodd 3.3.7 发布,常用 Java 工具包

Jodd 3.3.7 发布,该版本包含重要改进和 bug 修复,增加了 HtmlStapler 过滤器,Lagarto 和 Jerry 更好的支持有条件的注释,DbOomManager 支持表格和列的命名策略,增加通配符路径风格匹配。...

oschina
2012/07/28
1K
6
springboot(二)进阶篇

继之前的springboot(一)入门篇 ,本次主要讲springboot的一些其他特性,springboot内容很多,后续会持续更新. 多数据源 log4j日志 aop面向切面编程 缓存 定时任务 异步任务 自定义参数 多环境的...

zhuyuansj
2017/10/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【AI实战】手把手教你深度学习文字识别(文字检测篇:基于MSER, CTPN, SegLink, EAST等方法)

文字检测是文字识别过程中的一个非常重要的环节,文字检测的主要目标是将图片中的文字区域位置检测出来,以便于进行后面的文字识别,只有找到了文本所在区域,才能对其内容进行识别。 文字检...

雪饼
今天
16
0
思维导图XMind 8 Pro 绿化方法(附序列号)

按部就班: Step 1 -全新下载最新版本的 Xmind 8(注必须是英文官方的版本,中文代{过}{滤}理网站的版本修改过,无法使用pj); Step 2 -安装完毕后,点击文末的下载按钮下载pj补丁文件包,将...

一只小青蛙
今天
10
0
数据结构(ER数据库)设计规范

表命名规范 表命名的规则分为3个层级,层级之间通过_分割,例如b_r_identity、d_l_identity。规约为: [leavel]_[type]_[name] [leavel] 表示数据库表的层级和功能,分为: s:业务无关的系统...

随风溜达的向日葵
今天
10
0
阿里Sentinel控制台源码修改-对接Apollo规则持久化

https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel 动态规则扩展 https://github.com/alibaba/Sentinel/wiki......

jxlgzwh
昨天
14
0
在Linux系统中创建SSH服务器别名

如果你经常通过 SSH 访问许多不同的远程系统,这个技巧将为你节省一些时间。你可以通过 SSH 为频繁访问的系统创建 SSH 别名,这样你就不必记住所有不同的用户名、主机名、SSH 端口号和 IP 地...

老孟的Linux私房菜
昨天
16
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部