自定义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)
看一下几个核心的类:
一、过滤器(前端控制器)
核心方法
①.创建代理对象
②.将request参数放入到对象的属性中
③.将属性中的值自动setAttribute到request中
④.返回方法返回值
以上主要的就是第一步,
其他的都容易,2、3步就注意一点:这里使用了cglib代理,所以得到的对象的类getDeclaredFields得不到要的东西,要getSuperClass().getDeclaredFields才能得到想要的属性
二.cglib代理
其实就是做了一件事:
查看方法上是否有@Transaction注解,如果有,则使用TransactionManager来管理其中的方法。否则正常执行。
三、TransactionManager与SessionFactory
这里用的是mybatis持久框架,如果用其他的也差不多。
这边的TransactionManager与SessionFactory的耦合稍微有点高。
如果有事务:
之前代理的也看到了,首先调用的是manager.start()方法,这样在threadLocals里塞进去了2个东西,一个sqlSession一个状态。
然后调用action中的方法,action方法中调用了dao的方法,dao获取session的时候通过工厂获取,工厂检查事务状态,这里当然检查到有事务,那就得到一个被代理过的sqlSession,他的commit与close方法失效了。
由manager来控制dao的提交、回滚、关闭。
如果没有事务:
直接产生一个SqlSession返回去,dao怎么写就怎么来。
四、测试:
action
@Transaction加与不加不一样的效果,亲测好使,就是耦合高了一些。
但是如果真的要用的话,我可以把耦合的地方抽取到配置文件中来设置。
工程下载地址:
点击打开链接
现在结合之前写的简单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加与不加不一样的效果,亲测好使,就是耦合高了一些。
但是如果真的要用的话,我可以把耦合的地方抽取到配置文件中来设置。
工程下载地址:
点击打开链接
相关文章推荐
- JAVA学习第七天
- Java参数传递问题
- java进阶
- spring中关于定时器的配置
- 在Struts2使用集合对象保存表单提交参数
- java.lang.IllegalStateException: WRITER
- java可变参数使用总结
- Java 敏感字验证
- java注解入门(含源码下载)
- Struts2的声明式异常处理
- 基于注解的 Spring MVC 简单入门
- java注解再学习
- 使用Struts2的iterator标签遍历复杂Map种类
- java NIO原理和应用
- Java基础学习总结——Java对象的序列化和反序列化
- java exception使用的性能影响,用数据说话
- java查看jar源代码
- Spring与Mybatis整合环境搭建
- java统计图echarts
- org.eclipse.e4.core.di.InjectionException:org.eclipse.swt.SWTException: Widget is disposed