为了方便理解代码中的实现,我们先对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;
}
}