面向切片编程(AOP)应用的一些实际例子
The definition of AOP in wikipedia seems a little bit difficult for beginners to understand, so in this blog I use an example to introduce why we need it.
Suppose I have an order command class which performs its core business logic in method doBusiness:
package aop;
import java.util.logging.Level;
import com.sun.istack.internal.logging.Logger;
public class OrderCommand {
public void execute(){
Logger logger = Logger.getLogger(OrderCommand.class);
logger.log(Level.INFO, "start processing");
// authorization check
logger.log(Level.INFO, "authorization check");
logger.log(Level.INFO, "begin performance trace");
// only this line implements real business logic
this.doBusiness();
logger.log(Level.INFO, "end performance trace");
}
private void doBusiness(){
System.out.println("Do business here");
}
public static void main(String[] args) {
new OrderCommand().execute();
}
}
In method execute(), it is flooded with too many non-functional code like logging, authorization check and performance trace.
It is not a good design, we can try to improve it via template method pattern.
Template method pattern
With this pattern, I create a new parent class BaseCommand, and put all non-functional code inside the execute method.
import java.util.logging.Level;
import com.sun.istack.internal.logging.Logger;
public abstract class BaseCommand {
public void execute(){
Logger logger = Logger.getLogger(this.getClass());
logger.log(Level.INFO, "start processing");
// authorization check
logger.log(Level.INFO, "authorization check");
logger.log(Level.INFO, "begin performance trace");
// only this line implements real business logic
this.doBusiness();
logger.log(Level.INFO, "end performance trace");
}
protected abstract void doBusiness();
}
Now the real business logic is defined in child class OrderCommand, whose implementation is very clean:
public class OrderCommand extends BaseCommand {
public static void main(String[] args) {
new OrderCommand().execute();
}
@Override
protected void doBusiness() {
System.out.println("Do business here");
}
}
Drawback of this solution: as the parent class has defined the template method execution, it is NOT possible for a child class to adapt it, for example, a child class cannot change the order sequence of authorization check and performance trace method. And suppose a child class does not want to implement authorization check at all – this could not be achieved with this solution. We have to use decorator pattern instead.
Decorator pattern
First I need to create an interface:
public interface Command {
public void execute();
}
And create a decorator to cover the log and authorization check function:
import java.util.logging.Level;
import com.sun.istack.internal.logging.Logger;
public class LoggerDecorator implements Command{
private Command cmd;
public LoggerDecorator(Command cmd){
this.cmd = cmd;
}
@Override
public void execute() {
Logger logger = Logger.getLogger(this.getClass());
logger.log(Level.INFO, "start processing");
// authorization check
logger.log(Level.INFO, "authorization check");
this.cmd.execute();
}
}
And a second decorator for performance trace:
package aop;
import java.util.logging.Level;
import com.sun.istack.internal.logging.Logger;
public class PerformanceTraceDecorator implements Command{
private Command cmd;
public PerformanceTraceDecorator(Command cmd){
this.cmd = cmd;
}
@Override
public void execute() {
Logger logger = Logger.getLogger(this.getClass());
logger.log(Level.INFO, "begin performance trace");
this.cmd.execute();
logger.log(Level.INFO, "end performance trace");
}
}
And the class to finish the real business logic. Now I have the full flexibility to constructor the instance according to real business case, with the help of different decorator. The following instance fullCmd owns the ability of both authorization check log and performance trace.
public class OrderCommand implements Command {
public static void main(String[] args) {
Command fullCmd = new LoggerDecorator( new PerformanceTraceDecorator( new OrderCommand()));
cmd.execute();
}
@Override
public void execute() {
System.out.println("Do business here");
}
}
Suppose in a given scenario, only performance trace is needed, we can just use the performance trace decorator:
Command cmd = new PerformanceTraceDecorator( new OrderCommand());
cmd.execute();
Drawback of decorator pattern: The decorator class and the business class have to implement the same interface, command, which is more business related. Is there possibility that the utility classes for non-functional implementation can just work without implementing the same interface which is implemented by business class?
AOP solution
I use a Java project implemented by Spring framework to demonstrate the idea.
Suppose I hope to add performance trace on this business method: save.
(1) You may have already observed the annotation @Log(nameI042416=”annotation for save method”) used in line10. This annotation is declared in file Log.java:
(2) Now I have to declare an Aspect class which contains a pointcut. A pointcut tells Spring framework which methods could be applied with AOP strategy, and the class annotated with @Aspect contains methods which should be called by Spring framework to “decorate” the methods identified by annotation. Here below I have declared a pointcut “logJerry” via annotation @Pointcut:
For example, since we have annotated the business method save() with “@Log(nameI042416=”annotation for save method”)”, we can define what logics must be done on it, with the help of @Before and @After plus declared pointcut.
With this approach, I can add performance trace function to save method without modifying its source code.
Set breakpoint on these beforeExec and afterExec methods, launch the project with Tomcat under debug mode, paste the following url to your browser: http://localhost:9498/springaop/aopRootJerry/aop2Jerry/i042416?string=sap Through callstack you can understand how the AOP call is working in Spring.
Why we say AOP can increase modularity by allowing the separation of cross-cutting concerns?
Suppose we have lots of methods all of which need common utilities like log, performance trace and authorization check. Before we use AOP, these utilities are scattered in every method:
After AOP is used, those common stuff are extracted into Aspect class and reusability is fulfilled. From the picture below we can see the cross-cutting concerns are now separated.
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- OAuth 2.0 单元测试解决方案
- 「性能提升」扩展 Spring Cache 支持多级缓存
- 如何以源码形式运行Nacos Server
- Spring Boot 2.4 配置文件将加载机制大变化
- OpenGL ES 多目标渲染(MRT)
- R包:gtable包用于处理ggplot2图像
- Sight——杀手级提升Laravel开发速度的组件现在开源了!
- 面试官带你学Android——面试中Handler 这些必备知识点你都知道吗?
- leetcode哈希表之前K个高频元素
- Kali之U盘启动加密与持久化存储制作流程
- Gentoo发行版系统基础使用记录
- Ubuntu-20.04-LTS桌面版与服务器版基础初始配置
- Snap容器基础入门使用
- zabbix入门学习
- 突击并发编程JUC系列-ReentrantReadWriteLock