大家好,又见面了,我是你们的朋友全栈君。
用过spring的,应该都知道@transactional这个注解。这个注解给我们日常开发带来了很大便利,让我们无需为了数据的提交和回滚操心,只需要关注自己的业务逻辑即可。这里我模拟一下spring的注解是如何执行的。
这个是项目结构
首先我们需要一个数据库的连接
为了保证事务的完整性,这里我们用ThreadLocal保存当前线程的数据库连接
/**
* Created by zhoum on 2019-06-28.
*/
public class ConnectionUtils {
private static DruidDataSource dataSource = new DruidDataSource();
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
//静态代码块,设置连接数据库的参数
static {
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/web");
dataSource.setUsername("root");
dataSource.setPassword("root");
}
public static Connection getConection() throws SQLException {
Connection conn = threadLocal.get();
if (conn == null) {
conn = dataSource.getConnection();
threadLocal.set(conn);
}
return conn;
}
}
说到自定义事务注解,肯定是要用自己写的注解
新建一个自己的事务注解
/**
* Created by zhoum on 2019-06-28.
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Transactional {
}
接下来,新建一个业务类,也就是我们常常写service
我在类上打上刚刚新建的自定义事务注解
/**
* Created by zhoum on 2019-06-28.
*/
@Transactional
public class UserServiceImpl implements UserService {
@Override
public void add(User user) throws SQLException {
Connection conn = ConnectionUtils.getConection();
PreparedStatement preparedStatement = conn.prepareStatement("insert into USER (name,age)" +
" VALUES (?,?)");
preparedStatement.setObject(1, user.getName());
preparedStatement.setObject(2, user.getAge());
preparedStatement.execute();
preparedStatement = conn.prepareStatement("insert into USER (name,age)" +
" VALUES (?,?)");
preparedStatement.setObject(1, user.getName());
preparedStatement.setObject(2, user.getAge());
preparedStatement.execute();
}
}
接下来新建一个bean的工厂类,这就像我们找spring要bean一样,我们在xml或是注解上定义这个bean的基本信息,spring为我们创建这些bean并保存,当我需要时,只要打上autowire就可以拿到了
/**
* Created by zhoum on 2019-07-02.
*/
public class BeanFactory {
public static Object getBean(Class clazz) throws IllegalAccessException, InstantiationException, SQLException, NoSuchMethodException, InvocationTargetException {
Object o = clazz.newInstance();
if (o.getClass().isAnnotationPresent(Transactional.class)) {
Connection conection = ConnectionUtils.getConection();
ProxyBean proxyBean = new ProxyBean(o, conection);
return proxyBean.getBean();
}
return o;
}
}
代码中有一行 ProxyBean ,这个类是专门用来获取代理对象的。
target是当前目标执行类,connection是当前数据库连接
我们将UserServiceImpl进行代理,将connection开启事务,提交,回滚的代码把代理对象的add方法夹住
/**
* Created by zhoum on 2019-07-02.
*/
public class ProxyBean {
private Object target;
private Connection connection;
public ProxyBean(Object target, Connection connection) {
this.target = target;
this.connection = connection;
}
public Object getBean() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
connection.setAutoCommit(false);
try {
method.invoke(target, args);
connection.commit();
} catch (Exception e) {
connection.rollback();
} finally {
connection.close();
}
return null;
}
});
return o;
}
}
最后一步就是测试
/**
* Created by zhoum on 2019-06-28.
*/
public class Main {
public static void main(String[] args) throws SQLException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
UserService bean = (UserService) BeanFactory.getBean(UserServiceImpl.class);
User user = new User();
user.setName("伟哥");
user.setAge(1);
bean.add(user);
}
}
1:没打上自定义事务注解
执行run main方法,可以看到成功插入了2条
接下来我们在UserServiceImpl.add()方法加上异常代码,可以看到只添加了一条数据
说明没有加上@transactional注解的UserServiceImpl不是我们动态代理的事务对象。所以事务并没有生效
2:打上自定义事务注解
没有加上任何异常代码,正常执行,成功添加2条记录
在UserServiceImpl.add()方法加入异常代码,可以看到没有新增任何数据,说明事务生效了。
总结
其实spring提供的事务并没有那么难理解,无非就是利用jdk或是cglib的动态代理,帮你代理一个对象,并对这个对象做一些加工。当我们找spring要这个对象时,spring给我们的是这个对象的代理对象。
当我把UserServiceImpl类上的transactional注解删除时,我得到对象就是正常new出来的对象。
当加上时,得到的就是动态代理的对象。其实这跟spring给我对象时是一样,我们把项目中的类spring提供的transactional删除后,得到对象就是普通new出来的对象吗,加上后spring才会为我们去动态代理这些对象
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/129033.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...