cglib实现jfinal service上添加事务 多数据源切换改进

注:本文参考

http://www.oschina.net/code/snippet_188964_26555

http://my.oschina.net/jally/blog/180366

实现进行改进。

一、思路

想在service层开事务,想到的是代理service的方法,在代理中开启事务,然后执行被代理方法,最后提交事务。

二、实现

cglib的MethodInterceptor可以代理对象的所有方法,使用非常方便,所以这里使用cglib来代理service对象。

为了表明那个方法需要开启事务,这里新建一个注解MethodTx,用来表示要开启事务。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MethodTx {
	public abstract String config() default (String) "main";
}

注解中的config用来指定使用的数据源,默认使用主数据源,在jfinal中主数据源默认命名为main。

下面是实现代理类:

public class TxProxy implements MethodInterceptor {

	private static final Logger log = LoggerFactory.getLogger(TxProxy.class);

	/**
	 * 事务包装
	 * 
	 * @author shizc
	 *
	 */
	private class TxInvoke implements IAtom {
		private Object target = null;
		private MethodProxy proxy = null;
		private Object[] args = null;
		private Object result = null;
		private String dsConfig = null;

		@SuppressWarnings("unused")
		private TxInvoke() {

		}

		public TxInvoke(Object target, MethodProxy proxy, Object[] args) {
			super();
			this.target = target;
			this.proxy = proxy;
			this.args = args;
		}

		public boolean run() throws SQLException {
			boolean flag = false;
			try {
				result = proxy.invokeSuper(target, args);
				flag = true;
			} catch (Throwable e) {
				log.error("调用事务失败", e);
			}
			return flag;
		}

		public Object getResult() {
			return result;
		}

	}

	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {

		Object result = null;
		if (method.isAnnotationPresent(MethodTx.class)) {
			MethodTx methodTx = method.getAnnotation(MethodTx.class);
			String conf = methodTx.config();
			// 包装成事务
			TxInvoke invoke = new TxInvoke(obj, proxy, args);
			log.debug("开始事务--------->{}", conf);
			if (StringUtils.isNotBlank(conf) && !"main".equalsIgnoreCase(conf)) {
				Db.use(conf).tx(invoke);
			} else {
				Db.tx(invoke);
			}
			log.debug("结束事务--------->{}", conf);
			result = invoke.getResult();
		} else {
			// 没有事务,直接执行
			result = proxy.invokeSuper(obj, args);
		}

		return result;
	}

}

核心思路就是在实现intercept方法中,判断被代理的方法是否有MethodTx注解。如果有MethodTx注解,则选择对应的数据源开启事务执行。如果没有则执行原始方法。

最后,为了得到被代理对象,需要一个工厂类来生成被代理的代理对象:

/**
 * service代理工厂
 * @author shizc
 *
 */
public class TxProxyFactory {
	private static final Logger log = LoggerFactory.getLogger(TxProxyFactory.class);

	/**
	 * 获取要代理的对象
	 * 
	 * @param targetClass
	 *            被代理的对象
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T newProxy(Class<T> targetClass) {

		if (targetClass == null) {
			return null;
		}
		Object proxy = null;
		Enhancer en = new Enhancer();
		en.setSuperclass(targetClass);
		// 代理回调
		en.setCallback(new TxProxy());
		proxy = en.create();
		log.debug("创建代理类:{}", targetClass.getName());

		return (T) proxy;
	}
}

使用方法:

public class TestService {
	public static final TestService me = TxProxyFactory.newProxy(TestService.class);
	}

三、总结

关键是cglib的使用结合Db.Tx来生成代理类。其中切换数据源使用的也是Db.use方法。jfinal使用起来确实比较简单方便。最后感谢 @JFinal 开源这么优秀的框架 感谢@泡泡队长 @hyanqing 提供cglib实现事务代理类的思路

本人能力有限,有失误的地方请指正。

时间: 2024-10-27 03:01:02

cglib实现jfinal service上添加事务 多数据源切换改进的相关文章

springmvc 用注解方式添加事务不生效解决方法

springmvc 事务注册有很多种方法,在此我只mark 用注解方式添加transaction不生效的解决办法. springmvc 注解方法添加事务步骤: 1.在 spring的 root-context.xml (WEB-INF/)文件中添加事物管理: <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSo

Android 四大组件之Service(上)

1.Service简介 Service是Android四大组件中最与Activity相似的组件,他们都代表可执行的程序.Service一直运行于后台,不会与用户交互,可用来处理一些耗时的任务(比如:后台播放音乐,I/O操作等).它的创建.配置与Activity基本相似,下面将详细介绍Android Service的开发. 2.创建.配置Service 2.1 定义一个继承Service类的子类 2.2 在AndroidManifest.xml中配置该Service 需要注意的是 Service和

怎样在网站上添加在线客服

在线沟通,是每个网站都必不可少的功能,那么要怎样在网站上添加在线功能呢?下面星翼创想就列出了添加MSN和skype的方法.(下面提及的后台是只针对于在我司做网站的客户网站管理后台.) 直接复制以下代码到后台客服管理位置. MSN代码: <a href="msnim:chat?contact=帐号"><img src="http://www.iswweb.com/Uploadfiles/msn.gif"> 称谓</a> 备注: 把以

知识点:Quartz添加事务回滚

自动任务类: @PersistJobDataAfterExecution @DisallowConcurrentExecution public class ReCodeBack implements Job { private static final Logger LOGGER = LoggerFactory.getLogger(ReCodeBack.class); @Autowired ReCodeBackTag reCodeBackTag; @Override public void e

在js版搜索地图上添加标记

由于我们做的是有关于旅游方面的项目,所以涉及到了地图功能.我接到的其中一个任务就是,在地图上显示指定的几个景点,并在地图上加上标记. 我们项目用的是搜狗地图,使用的是js版本.大家有兴趣的话,可以参考搜索地图api以及示例代码. 在地图上添加标记是地图的一个基本功能.这个标记叫做Marker.可以从这里看官网上对于Marker类的介绍. 实现的基本步骤,首先在页面上创建一个地图,然后地图上添加一个marker.你可以对这个marker指定位置.显示内容,在地图上的显隐等.具体请看一下代码: <h

Android控件上添加图片

项目中有一个点赞功能,点赞的小图标添加在点赞列表旁边,在xml里可以进行设置,也可以在代码中进行绘图. 下面是两种方法的设置: 1.xml里:一些控件:button.textView等等里面有个属性是android:drawableLeft 就可以将pic设置到text的左边.good.... 2.代码中: TextView txtlikedList = new TextView(this.getContext()); Drawable drawable= getResources().getD

在地图上添加POI

使用Tangram的Marker, 可以在地图上做各种标记, 效果图: Tangram是通过Marker在地图上添加标记的,Marker分Point, Polyline和Polygon三种, 分别对应点.线.面三种几何体. Tangram使用统一的Marker接口来管理Marker: namespace Tangram { class Map { public: // Add a marker object to the map and return an ID for it; an ID of

在android设备上添加thttpd及CGI

============问题描述============ 我想在android系统上添加一个WEB服务,可以使用pc机上的浏览器对于设备的一些参数进行设置.现在选定 httpd+CGI+Sqlite3.但是不知道怎样将httpd+CGI编译并加载到android系统上.请不吝赐教.谢谢. ============解决方案1============ 请问一下:移动端能使用CGI接口么 ============解决方案2============ 你app上设置设备的参数具体是设置什么参数呀,难道不需

python 图片上添加文字

1 import PIL 2 from PIL import ImageFont 3 from PIL import Image 4 from PIL import ImageDraw 5 6 #设置字体,如果没有,也可以不设置 7 font = ImageFont.truetype("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",13) 8 9 #打开底版图片 10 imageFile = "base.png&qu