V哥官网:http://www.vgxit.com
本博客对应视频教程:http://www.vgxit.com/course/23
面向切面-问题引入
1,概述
如果说IOC是Spring的核心,那么面向切面就是Spring最重要的功能之一了,面向切面的专业术语叫做AOP。在数据库事物中切面编程是广泛应用的。
2,AOP的概念和使用原因
我们就用我们的数据库编程来举例。比如,我们做了一个电商系统,我们要做一块使用账户余额支付的功能。这个功能一般情况下需要四个操作来支撑,分别是:1,商品库存减一。2,生成一个订单。3,账户余额扣款。4,生成一条交易记录。
按照没用Spring之前的操作我们可能会写如下伪代码:
商品扣库存;
if(商品库存扣成功) {
生成订单;
if(订单生成成功){
账户余额扣款;
if(账户余额扣款成功) {
生成一条交易记录;
if (交易记录生成成功) {
提交事物;
} else {
事物回滚;
}
} else {
事物回滚;
}
} else {
事物回滚;
}
} else {
事物回滚;
}
对应的流程图如下:
从上面的情况,我们可以看出来我们代码编写得真的是非常非常的复杂。如果我们采用Spring Aop来管理事物,我们可以用如下流程图来代替。
我们通过上图可以看到,如果我们使用了Spring Aop来管理事物之后,我们的流程就会变得非常的简单。我们只需要做到如果数据库操作失败就直接抛出异常就行了。其他的,我们完全不用关心。那么我们思考一下我们如何才能做到这样的逻辑呢?
AOP是通过动态代理模式来管控哥哥对象操作的切换环境。包括日志,数据库事物等操作。让我们可以在反射原有对象方法正常返回,异常之前和返回后插入自己的逻辑代码的能力。在一些常用的流程中,比如数据库事物,AOP会提供默认的实现逻辑,也会提供一些简单的配置,程序员就能方便的修改默认的实现,从而达到符合真实引用要求的效果。提升代码的可读性,将开发集中在也业务逻辑上。
3,自己手写Aop:
自己实现AOP需要用到动态代理的知识,我们在这里就不会给大家再来介绍动态代理的知识了。如果大家没有这方面的知识,你需要去学习一下,看看老师之前讲的《简单设计模式入门》这门课程。我们这里手写AOP我们使用添加用户这样的逻辑来实现。
1,定义一个PO:
package com.vgxit.learn.spring.ktdm.myaop.po;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private Short gender;
}
2,创建对应的业务逻辑层的接口
package com.vgxit.learn.spring.ktdm.myaop.service;
import com.vgxit.learn.spring.ktdm.myaop.po.User;
public interface IUserService {
void add(User user);
}
3,编写对应的业务逻辑层的实现类
package com.vgxit.learn.spring.ktdm.myaop.service.impl;
import com.vgxit.learn.spring.ktdm.myaop.po.User;
import com.vgxit.learn.spring.ktdm.myaop.service.IUserService;
public class UserService implements IUserService {
@Override
public void add(User user) {
System.out.println("添加用户[" + user + "]到数据库");
}
}
4,提供一个Interceptor拦截器接口,这个接口里面定义了再具体的业务方法之前,之后,正常返回,异常返回的时候我们要插入的代码:
package com.vgxit.learn.spring.ktdm.myaop.aop;
public interface Interceptor {
//业务方法开始之前调用的代码
void before(Object obj);
//业务方法开始之后调用的代码
void after(Object obj);
//业务方法正常返回之后调用的代码
void afterReturning(Object obj);
//业务方法异常返回之后调用的代码
void afterThrowing(Object obj);
}
5,编写Interceptor的具体实现类:
package com.vgxit.learn.spring.ktdm.myaop.aop;
public class TranInterceptor implements Interceptor {
@Override
public void before(Object obj) {
System.out.println("获取数据库连接");
System.out.println("开启事务");
}
@Override
public void after(Object obj) {
System.out.println("对应逻辑执行完成");
}
@Override
public void afterReturning(Object obj) {
System.out.println("提交事物");
}
@Override
public void afterThrowing(Object obj) {
System.out.println("事物回滚");
}
}
6,实现动态代理工具类:
package com.vgxit.learn.spring.ktdm.myaop.proxy;
import com.vgxit.learn.spring.ktdm.myaop.aop.Interceptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil implements InvocationHandler {
//被代理的对象
private Object target;
//对应的拦截器
private Interceptor interceptor;
//获取对应的代理对象
public static <T> T getBean(T target, Interceptor interceptor) {
ProxyUtil proxyUtil = new ProxyUtil();
proxyUtil.target = target;
proxyUtil.interceptor = interceptor;
return (T) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(), target.getClass().getInterfaces(), proxyUtil);
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//到时候要返回的数据
Object result = null;
boolean hasException = false;
interceptor.before(target);
try {
//调用真实方法
result = method.invoke(target, objects);
} catch (Throwable e) {
hasException = true;
} finally {
interceptor.after(target);
}
if (hasException) {
interceptor.afterThrowing(target);
} else {
interceptor.afterReturning(target);
}
return result;
}
}
7,测试代码:
package com.vgxit.learn.spring.ktdm.myaop.test;
import com.vgxit.learn.spring.ktdm.myaop.aop.Interceptor;
import com.vgxit.learn.spring.ktdm.myaop.aop.TranInterceptor;
import com.vgxit.learn.spring.ktdm.myaop.po.User;
import com.vgxit.learn.spring.ktdm.myaop.proxy.ProxyUtil;
import com.vgxit.learn.spring.ktdm.myaop.service.IUserService;
import com.vgxit.learn.spring.ktdm.myaop.service.impl.UserService;
public class MyAopTest001 {
public static void main(String[] args) {
IUserService target = new UserService();
Interceptor interceptor = new TranInterceptor();
IUserService userService = ProxyUtil.getBean(target, interceptor);
User user = new User();
user.setId(1);
user.setName("李一桐");
user.setGender((short) 2);
userService.add(user);
}
}