AOP是Aspect Oriented Programming,即面向切面编程。
什么是AOP?
我们先回顾一下OOP:Object Oriented Programming,OOP作为面向对象编程的模式,获得了巨大的成功,OOP的主要功能是数据封装、继承和多态。
而AOP是一种新的编程方式,它和OOP不同,OOP把系统看作多个对象的交互,AOP把系统分解为不同的关注点,或者称之为切面(Aspect)。
AOP原理
如何把切面织入到核心逻辑中?这正是AOP需要解决的问题。换句话说,如果客户端获得了BookService的引用,当调用bookService.createBook()时,如何对调用方法进行拦截,并在拦截前后进行安全检查、日志、事务等处理,就相当于完成了所有业务功能。
在Java平台上,对于AOP的织入,有3种方式:
- 编译期:在编译时,由编译器把切面调用编译进字节码,这种方式需要定义新的关键字并扩展编译器,AspectJ就扩展了Java编译器,使用关键字aspect来实现织入;
- 类加载器:在目标类被装载到JVM时,通过一个特殊的类加载器,对目标类的字节码重新“增强”;
- 运行期:目标对象和切面都是普通Java类,通过JVM的动态代理功能或者第三方库实现运行期动态织入。
最简单的方式是第三种,Spring的AOP实现就是基于JVM的动态代理。由于JVM的动态代理要求必须实现接口,如果一个普通类没有业务接口,就需要通过CGLIB或者Javassist这些第三方库实现。
AOP技术看上去比较神秘,但实际上,它本质就是一个动态代理,让我们把一些常用功能如权限检查、日志、事务等,从每个业务方法中剥离出来。
需要特别指出的是,AOP对于解决特定问题,例如事务管理非常有用,这是因为分散在各处的事务代码几乎是完全相同的,并且它们需要的参数(JDBC的Connection)也是固定的。另一些特定问题,如日志,就不那么容易实现,因为日志虽然简单,但打印日志的时候,经常需要捕获局部变量,如果使用AOP实现日志,我们只能输出固定格式的日志,因此,使用AOP时,必须适合特定的场景。
AOP实例
某大学城区为严格执行防疫安全登记,对不同的区域入口实行健康码登记,进入校区,若健康码为绿色(以字符Y表示),学生信息方可提交健康系统;若健康码为非绿色(以字符N表示),学生信息不能提交健康系统。
文件结构:
数据库结构:
具体代码:
当工作人员发送请求/login,录入以下表格信息
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>学生健康信息录入</h1>
<form th:action="@{/add}" method="post">
<tr>
<td>用户编号</td>
<td><input type="text" name="uid" id="uid"/></td>
</tr>
<br>
<tr>
<td>姓名</td>
<td><input type="text" name="uname" id="uname"/></td>
<td>年龄</td>
<td><input type="text" name="age" id="age"/></td>
<td>区域编号</td>
<td><input type="text" name="area_no" id="area_no"/></td>
</tr>
<a th:href="@{/add}"><button>提交</button></a>
</form>
</body>
</html>
当点击提交按钮时,由于学生健康码是现场查阅,设计一个AOP,确认学生的健康码状态,由工作人员现场手工录入:绿码用y表示,非绿码用n表示。
在WebControllerApp中注入AOP对获取信息进行拦截:
@Before("executeService()")
public void dobeforeAdvice(JoinPoint joinPoint) {
//获取ReqAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
Enumeration<String> enumeration = request.getParameterNames();
Map<String, String> parameterMap = Maps.newHashMap();
while (enumeration.hasMoreElements()) {
String paramname = enumeration.nextElement();
parameterMap.put(paramname, request.getParameter(paramname));
}
String per = JSON.toJSONString(parameterMap);
System.out.println("健康码y/n");
System.out.println(per);
Scanner scanner = new Scanner(System.in);
String jkm = scanner.nextLine();
session.setAttribute("jkm",jkm);
}
拦截过后对健康码颜色进行y/n判断
@Controller
public class IndexController {
@Autowired
private PersonService personService;
@RequestMapping(value = "/index.html")
public String toAdd() {
return "index";
}
//添加
@RequestMapping("/add")
public String doAdd(Person person,
@RequestParam("uid") Integer uid,
@RequestParam("uname") String uname,
@RequestParam("age") Integer age,
@RequestParam("area_no") Integer area_no,
Model model,
HttpSession session){
Object jkm = session.getAttribute("jkm");
String a = (String) jkm;
if (a.equals("y")) {
session.setAttribute("uId",uid);
session.setAttribute("uName",uname);
session.setAttribute("age",age);
session.setAttribute("jkm",jkm);
session.setAttribute("areaNo",area_no);
personService.add(person);
System.out.println("AOP拦截成功");
return "redirect:/reget.html";
} else {
System.out.println("AOP拦截成功");
return "redirect:/error.html";
}
}
对健康码颜色进行判断。绿色则为y,拦截通过并写入数据库。
红黄则为n,对信息进行拦截,不写入数据库,同时提示核酸不通过,不进行放行。
项目下载:
链接:https://pan.baidu.com/s/1F8-Gdsfh6fi2uLG0fldiZQ
提取码:jgyy
AOP优点
在AOP中类与切面关系:
从可以看出,通过Aspect(切面)分别在Class1和Class2的方法中加入了事务、日志、权限和异常等功能。
AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多的关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。
目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring2.0开始,Spring AOP引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。