您的位置:首页 > 编程语言 > Java开发

自定义struts(二)--FakeStruts实现@Transaction 注解事务控制

2015-07-22 18:55 465 查看
接着前两篇的:

现在结合之前写的简单struts以及transactionManager,完成一个能够通过@Transaction完成事务控制的功能。

我的想法是这样的:

随便写个action类,里面的方法只要加上了@Transaction注解,在里面调用dao的方法,执行的就是事务的处理。如果没加,那就正常处理。

实现原理:

1.将action中的方法进行代理,查看注解,如果需要事务,则添加事务操作。(cglib)

2.写一个TransactionManager,用来控制事务操作。(ThreadLocal)

2.这里用的是mybatis持久层,所以代理了一下他的sqlSession,这个封装在我写的SessionFacoty类中。(Proxy)



看一下几个核心的类:

一、过滤器(前端控制器)

核心方法

private String doAction(HttpServletRequest request, Class clazz,
			Method method) {
		Object object = this.newTransactionProxyInstance(clazz, method);
		this.setParameterToField(request, object);
		try {
			String returnValue = (String) method.invoke(object, null);
			this.setFiledToAttribute(request, object);
			return returnValue;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}


①.创建代理对象

②.将request参数放入到对象的属性中

③.将属性中的值自动setAttribute到request中

④.返回方法返回值

以上主要的就是第一步,

其他的都容易,2、3步就注意一点:这里使用了cglib代理,所以得到的对象的类getDeclaredFields得不到要的东西,要getSuperClass().getDeclaredFields才能得到想要的属性

private Object newTransactionProxyInstance(Class clazz, Method method) {
		Object object = new TransactionActionFactory(clazz, method).newAction();
		return object;
	}


二.cglib代理

package com.aii.mybatis.transaction;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;

public class TransactionActionFactory {
	private Class clazz;
	private Method method;

	public TransactionActionFactory(Class clazz, Method method) {
		this.clazz = clazz;
		this.method = method;
	}

	public Object newAction() {
		Enhancer en = new Enhancer();
		// 进行代理
		en.setSuperclass(clazz);
		en.setCallback(new TransactionCglibProxy(method));
		// 生成代理实例
		return  en.create();
	}
}
package com.aii.mybatis.transaction;

import java.lang.reflect.Method;
import java.util.logging.Logger;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import com.aii.struts.annotation.Transaction;
import com.aii.struts.transcation.TransactionManager;

public class TransactionCglibProxy implements MethodInterceptor {
	private Method method = null;

	TransactionCglibProxy(Method method) {
		this.method = method;
	}

	public Object intercept(Object arg0, Method method, Object[] arg2,
			MethodProxy methodProxy) throws Throwable {
		System.out.println("调用的方法是:" + method.getName());
		if (method.isAnnotationPresent(Transaction.class)) {
			System.out.println("do by transaction");

			TransactionManager manager = MyBatisTransactionManager
					.newInstance();
			manager.startTranscation();
			Object result = null;
			try {
				result = methodProxy.invokeSuper(arg0, arg2);
				manager.commit();
			} catch (Exception e) {
				e.printStackTrace();
				manager.rollback();
			}
			return result;
		}
		System.out.println("do without transaction");
		return methodProxy.invokeSuper(arg0, arg2);
	}

}


其实就是做了一件事:

查看方法上是否有@Transaction注解,如果有,则使用TransactionManager来管理其中的方法。否则正常执行。

三、TransactionManager与SessionFactory

这里用的是mybatis持久框架,如果用其他的也差不多。

这边的TransactionManager与SessionFactory的耦合稍微有点高。

package com.aii.mybatis.transaction;

import org.apache.ibatis.session.SqlSession;

import com.aii.struts.transcation.TransactionManager;

public class MyBatisTransactionManager implements TransactionManager {

	private static MyBatisTransactionManager newInstance = new MyBatisTransactionManager();

	private MyBatisTransactionManager() {
	};

	private ThreadLocal<SqlSession> sessions = new ThreadLocal<SqlSession>();
	private ThreadLocal<Boolean> states = new ThreadLocal<Boolean>();

	public static MyBatisTransactionManager newInstance() {
		return newInstance;
	}

	public void startTranscation() {
		System.out.println("------------------开启事务--------------------");
		SqlSession sqlSession = SessionFactory.getOriginalSession();
		sessions.set(sqlSession);
		states.set(true);
	}

	public void commit() {
		System.out.println("------------------提交事务--------------------");
		SqlSession sqlSession = sessions.get();
		sqlSession.commit();
		sqlSession.close();
		sessions.remove();
		states.remove();
	}

	public void rollback() {
		System.out.println("------------------回滚事务--------------------");
		SqlSession sqlSession = sessions.get();
		sqlSession.rollback();
		sqlSession.close();
		sessions.remove();
		states.remove();
	}

	Boolean getState() {
		return states.get();
	}

	SqlSession getSession() {
		return sessions.get();
	}
}


package com.aii.mybatis.transaction;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class SessionFactory {
	public static SqlSessionFactory factory = null;
	private static final String CONFIGURATION_PATH = "configuration.xml";
	static {
		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

		factory = builder.build(Thread.currentThread().getContextClassLoader()
				.getResourceAsStream(CONFIGURATION_PATH));
	}

	public static SqlSession getSession() {
		MyBatisTransactionManager manager = MyBatisTransactionManager
				.newInstance();
		Boolean state = manager.getState();
		// 否则自己产生,直接返回
		if (state == null || !state) {
			System.out.println("未开启事务,正常执行");
			return getOriginalSession();
		}
		// 如果有事务处理,则从manager中拿
		System.out.println("开启事务,返回代理过的sqlSession");
		return (SqlSession) Proxy.newProxyInstance(Thread.currentThread()
				.getContextClassLoader(), new Class[] { SqlSession.class },
				new ProxySqlSessionHandler(manager.getSession()));
	}

	static SqlSession getOriginalSession() {
		return factory.openSession();
	}

	static class ProxySqlSessionHandler implements InvocationHandler {
		private SqlSession sqlSession;

		public ProxySqlSessionHandler(SqlSession sqlSession) {
			this.sqlSession = sqlSession;
		}

		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			// 查看transactionManager状态,根据状态确定session.commit,session.close
			Boolean state = MyBatisTransactionManager.newInstance().getState();
			if (method.getName().equals("commit") && state) {
				return null;
			}
			if (method.getName().equals("close") && state) {
				return null;
			}
			return method.invoke(sqlSession, args);
		}

	}
}


package com.aii.mybatis.dao;

import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import com.aii.mybatis.transaction.SessionFactory;
import com.aii.mybatis.vo.User;

public class UserDAO {

	public User getUser(String name, String password) {
		SqlSession session = null;
		User user = null;
		try {
			session = SessionFactory.getSession();
			Map<String, String> map = new HashMap<String, String>();
			map.put("name", name);
			map.put("password", password);
			user = session.selectOne("user.getUser", map);
		} finally {
			session.close();
		}
		return user;
	}

	public void update(User user) {
		SqlSession session = null;
		try {
			session = SessionFactory.getSession();
			session.update("user.update", user);
			session.commit();
		} finally {
			session.close();
		}
	}

	public void delete(int id) {
		SqlSession session = null;
		try {
			session = SessionFactory.getSession();
			session.delete("user.delete", id);
			session.commit();
		} finally {
			session.close();
		}
	}
}


如果有事务:
之前代理的也看到了,首先调用的是manager.start()方法,这样在threadLocals里塞进去了2个东西,一个sqlSession一个状态。

然后调用action中的方法,action方法中调用了dao的方法,dao获取session的时候通过工厂获取,工厂检查事务状态,这里当然检查到有事务,那就得到一个被代理过的sqlSession,他的commit与close方法失效了。

由manager来控制dao的提交、回滚、关闭。

如果没有事务:

直接产生一个SqlSession返回去,dao怎么写就怎么来。

四、测试:

action

package com.aii.struts.action;

import com.aii.mybatis.dao.UserDAO;
import com.aii.mybatis.vo.User;

public class UpdateAndDeleteAction {
	private int id;
	private String name;
	private String password;

	// @Transaction
	public String execute() {
		System.out.println("userName:" + name + "\npassowrd:" + password);
		User user = new User();
		user.setId(id);
		user.setName(name);
		user.setPassword(password);

		UserDAO dao = new UserDAO();
		dao.update(user);

		if (true) {
			throw new RuntimeException("抛出异常,检验回滚");
		}

		dao.delete(id);
		return null;
	}
}


@Transaction加与不加不一样的效果,亲测好使,就是耦合高了一些。

但是如果真的要用的话,我可以把耦合的地方抽取到配置文件中来设置。

工程下载地址:

点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: