手写Sping(3) :手写AOP与事务管理

为了方便理解代码中的实现,我们先对AOP与事务的概念做简单介绍:

AOP

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或者责任(例如事务处理、日志管理、权限控制)封装起来,以便减少系统的重复代码,降低模块的耦合度,并有利于未来的可扩展性与可维护性。
在这里插入图片描述
在这里插入图片描述

  • 横切关注点:跨越应用程序多个模块的方法或者功能。即:与我们的业务逻辑无关,但是是我们需要关注的部分,也即横切关注点
  • 切面(Apect):横切关注点被模块化的特殊对象。即,它是一个类
  • 通知(Advice):切面必须完成的工作,即它是类中的一个方法
  • 目标(Target):被通知对象
  • 代理(Proxy):向目标对象应用通知之后创建的对象
  • 切入点(PointCut):切面通知执行的地点的定义
  • 连接点(JointPoint):与切入点匹配的执行点

事务

事务是逻辑上的一组操作,要么都执行要么都不执行,事务具有四大特性:

  • 原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性是确保动作要么全部完成要么完全不起作用。
  • 一致性(Consistency):一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中。执行事务前后,数据保持一致,多个事务对同一个数据的读取结果是相同的。
  • 隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间的数据库访问是独立的
  • 持久性(Durability):一个事务被提交之后。它对数据库中的数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

在本项目中,我们注重前三点,因为第四点将由数据库本身进行保证。

核心代码实现

创建代理对象,每次我们调用被AOP代理的类(切面类)中的方法时,都会回调执行intercept()函数,在这个函数中,我们将递归创建一个代理链,这个递归的代理链将会执行。

public class ProxyFactory {

    //要代理的真实对象
    private  Object obj;

    /**
     * 输入一个目标类和一组Proxy接口实现, 输出一个代理对象
     */
//    @SuppressWarnings("unchecked")
    //<T> T代表返回值T是泛型,T是一个占位符,在编译的时候确定
    public <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList) {
        //CGLIB enhancer增强类对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * 代理方法, 每次调用目标方法时都会先创建一个 ProxyChain 对象, 然后调用该对象的 doProxyChain() 方法.
             */
            public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
                ProxyChain proxyChain=new ProxyChain(targetClass, targetObject, targetMethod, methodProxy, methodParams, proxyList);
//                methodProxy.invokeSuper(targetObject,methodParams);
                return proxyChain.doProxyChain();
            }
        });
        return  (T) enhancer.create();
    }
}

上面的递归代理链是如何执行的,以及最终执行的结果是什么样?我们结合下面的代码给出解答:
doProxyChain()中,对proxyList中的AOP实现类,依此执行doProxy()进行代理,在AspectProxy 的doProxy()方法中,代理链将先执行每个AOP实现类的前置通知,当proxyIndex==proxyList.size(),意味着所有的AOP实现类都已经执行了它的前置通知,此时我们执行被代理类的方法methodProxy.invokeSuper(targetObject,methodParams)。

随后,将递归栈中的元素依次弹出,此时会按照执行proxyList执行前置通知相反的顺序执行后置通知。这就实现了完整的多重的AOP代理。

代理链:

public class ProxyChain {

    private final Class<?> targetClass; //目标类
    private final Object targetObject; //目标对象
    private final Method targetMethod; //目标方法
    private final MethodProxy methodProxy; //方法代理
    private final Object[] methodParams; //方法参数

    private List<Proxy> proxyList = new ArrayList<Proxy>(); //代理列表
    private int proxyIndex = 0; //代理索引

    public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
        this.targetClass = targetClass;
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
        this.methodProxy = methodProxy;
        this.methodParams = methodParams;
        this.proxyList = proxyList;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public Method getTargetMethod() {
        return targetMethod;
    }

    public Object[] getMethodParams() {
        return methodParams;
    }

    /**
     * 递归执行
     */
    public Object doProxyChain() throws Throwable {
        Object methodResult;
        if (proxyIndex<proxyList.size()){
            //执行增强方法
            methodResult=proxyList.get(proxyIndex++).doProxy(this);
        }else{
            //目标方法最后执行且值执行一次
            methodResult=methodProxy.invokeSuper(targetObject,methodParams);
        }
        return methodResult;
    }
}
public abstract class AspectProxy implements Proxy {

    private static final Logger logger = LoggerFactory.getLogger(AspectProxy.class);

    public final Object doProxy(ProxyChain proxyChain) throws Throwable{
        Object result=null;

        Method method=proxyChain.getTargetMethod();
        Object[] params=proxyChain.getMethodParams();

        begin();
        try {
            if (intercept(method,params)){
                before(method,params);
                //开始代理
                result=proxyChain.doProxyChain();
                after(method,params);
            }else {
                result=proxyChain.doProxyChain();
            }
        } catch (Exception e) {
            logger.error("proxy failure", e);
            error(method, params, e);
            throw e;
        } finally {
            end();
        }

        return result;
    }
}

切面类织入目标类,在这个过程中,我们会将单例bean池中应该被代理的bean替换为经过AopHelper处理后的对应的bean

public class AopHelper {
    private static Map<Class<?>, Object> aopMap=new HashMap<Class<?>, Object>();
    private static ClassHelper classHelper;
    private static BeanHelper beanHelper;
//    public AopHelper(){
    static {
        classHelper=new ClassHelper();
        beanHelper=new BeanHelper();
        //将AOP类织入被代理类
        weavIn();
    }

    public static void weavIn(){
        try {
            //切面的实现类-被Aspect注解的目标类集合的映射
            Map<Class<?>,Set<Class<?>>> aspectMap=createAspectMap();
            //目标类-切面对象列表的映射
            Map<Class<?>, List<Proxy>> targetMap=createTargetMap(aspectMap);
            //把切面类织入目标类中,创建代理对象
            //TODO : 什么是切面类织入目标类,忘记了
            for (Map.Entry<Class<?>,List<Proxy>> targetEntry:targetMap.entrySet()){
                Class<?> targetClass = targetEntry.getKey();
                List<Proxy> proxyList=targetEntry.getValue();
//                Object instance=doCreateBean(targetClass,beanHelper.getSingletonObjects());
                Object proxy= new ProxyFactory().createProxy(targetClass,proxyList);
                //覆盖Bean容器里目标类对应的实例, 下次从Bean容器获取的就是代理对象了
                BeanHelper.setBeanPraoxy(targetClass,proxy);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
    ...
   
}

关于事务

事务的相关处理较为简单,为了保持独立性,我们在每次对数据库进行操作时,都创立一个Connection线程,即Connection conn = getConnection(),这保持了事务的独立性。
然后,commitTransaction()保证了事务的一致性。当出错时,commitTransaction()将执行回滚,确保事务的原子性。

public class DatabaseHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);

    private static final ThreadLocal<Connection> CONNECTION_HOLDER;

    private static final QueryRunner QUERY_RUNNER;

    private static final BasicDataSource DATA_SOURCE;

    static {
        CONNECTION_HOLDER = new ThreadLocal<Connection>();

        QUERY_RUNNER = new QueryRunner();

        DATA_SOURCE = new BasicDataSource();
        DATA_SOURCE.setDriverClassName("com.mysql.cj.jdbc.Driver");
        DATA_SOURCE.setUrl("jdbc:mysql://localhost:3306/tyshawn_test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT");
        DATA_SOURCE.setUsername("root");
        DATA_SOURCE.setPassword("xduykc");
    }

    /**
     * 获取数据源
     */
    public static DataSource getDataSource() {
        return DATA_SOURCE;
    }

    /**
     * 获取数据库连接
     */
    public static Connection getConnection() {
        Connection conn = CONNECTION_HOLDER.get();
        if (conn == null) {
            try {
                conn = DATA_SOURCE.getConnection();
            } catch (SQLException e) {
                LOGGER.error("get connection failure", e);
                throw new RuntimeException(e);
            } finally {
                CONNECTION_HOLDER.set(conn);
            }
        }
        return conn;
    }

    /**
     * 开启事务
     */
    public static void beginTransaction() {
        Connection conn = getConnection();
        if (conn != null) {
            try {
                conn.setAutoCommit(false);
            } catch (SQLException e) {
                LOGGER.error("begin transaction failure", e);
                throw new RuntimeException(e);
            } finally {
                CONNECTION_HOLDER.set(conn);
            }
        }
    }

    /**
     * 提交事务
     */
    public static void commitTransaction() {
        Connection conn = getConnection();
        if (conn != null) {
            try {
                conn.commit();
                conn.close();
            } catch (SQLException e) {
                LOGGER.error("commit transaction failure", e);
                throw new RuntimeException(e);
            } finally {
                CONNECTION_HOLDER.remove();
            }
        }
    }

    /**
     * 回滚事务
     */
    public static void rollbackTransaction() {
        Connection conn = getConnection();
        if (conn != null) {
            try {
                conn.rollback();
                conn.close();
            } catch (SQLException e) {
                LOGGER.error("rollback transaction failure", e);
                throw new RuntimeException(e);
            } finally {
                CONNECTION_HOLDER.remove();
            }
        }
    }

    /**
     * 执行更新语句(包括:update、insert、delete)
     */
    public static int update(String sql, Object... params) {
        int rows;
        try {
            Connection conn = getConnection();
            rows = QUERY_RUNNER.update(conn, sql, params);
        } catch (SQLException e) {
            LOGGER.error("execute update failure", e);
            throw new RuntimeException(e);
        }
        return rows;
    }

    /**
     * 更新实体
     */
    public static <T> boolean updateEntity(Class<T> entityClass, long id, Map<String, Object> fieldMap) {
        if (MapUtils.isEmpty(fieldMap)) {
            LOGGER.error("can not update entity: fieldMap is empty");
            return false;
        }

        String sql = "UPDATE " + entityClass.getSimpleName() + " SET ";
        StringBuilder columns = new StringBuilder();
        for (String fieldName : fieldMap.keySet()) {
            columns.append(fieldName).append(" = ?, ");
        }
        sql += columns.substring(0, columns.lastIndexOf(", ")) + " WHERE id = ?";

        List<Object> paramList = new ArrayList<Object>();
        paramList.addAll(fieldMap.values());
        paramList.add(id);
        Object[] params = paramList.toArray();

        return update(sql, params) == 1;
    }

}
public class TransactionProxy implements Proxy {

    @Override
    public Object doProxy(ProxyChain proxyChain) throws Throwable {
        Object result;
        Method method=proxyChain.getTargetMethod();
        //加入了Transactional的方法要做事务处理
        if (method.isAnnotationPresent(Transactional.class)){
            try {
                DatabaseHelper.beginTransaction();
                System.out.println("开启事务");
                result=proxyChain.doProxyChain();
                DatabaseHelper.commitTransaction();
                System.out.println("提交事务");
            }catch (Exception e){
                DatabaseHelper.rollbackTransaction();
                System.out.println("事务回滚");
                throw e;
            }
        }else{
            result=proxyChain.doProxyChain();
        }

        return result;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值