AOP和OOP
使用Java开发,大家应该都听过这两个概念,其实两者很好分辨:
- OOP(Object-Oriended Programming,面向对象编程):自顶向下
- AOP(Aspect-Oriented Programming,面向方面编程):切面编程
OOP编程下,代码自顶向下查看就能了解它的业务含义;AOP编程,需要去查看它的切入点(PointCut)和通知(Advice)
关于AOP的详细概念可以参考资料一,感觉讲得很不错!
为啥要用AOP
有些OOP编程会造成重复的胶水代码,这些可以通过AOP切面编程消灭它们,例如下面我遇到过的业务场景。
在业务上,需要对方法增加前置校验和后置操作,然后有多个类和多个方法需要增加这些操作,这时有三种方案可以选择:
使用硬编码,在每个方法前后各加一段代码。
抽离相同方法,在同一个类中,将相同的代码抽成同一个方法,然后进行调用。比第一种略微减少胶水代码,但还是存在多个类中会重复。
使用切面进行统一管理。使用自定义注解或者@Aspect进行管理,减少了胶水代码,同时便于管理。
通过上图对比,切面可以使代码更加整洁和管理,唯一的缺点可能就类膨胀,多了很多个类=-=,可是为了编程水平的提高,我选择了切面管理嘿嘿~
Demo思路
- 自定义一个注解,设定注解的属性
- 定义注解的Handler,在xml文件中配置该Handler为自定义注解的切入点
- 根据注解中的属性,通过工厂模式取出真正的执行者
- 在连接点(JoinPoint)前执行前置方法
- 执行连接点的方法
- 在连接点后执行后置方法
- End
Demo代码
项目结构:
先看主要的注解和XML文件如何配置:
自定义注解TypeCheck
1 | //定义了该Annotation被保留的时间长短,RUNTIME表示在运行时有效 |
定义切入点和XML配置
定义切入点有两种方式,一种是XML,还有一种是使用@Aspect注解和@PointCut(我这次使用的是XML,与@Around环绕切入类似)
TypeCheckHandler.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class TypeCheckHandler {
private TypeFactory typeFactory;
//可以看参考资料三,直接使用@Around注解实现
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取连接点的参数列表
Object[] args = joinPoint.getArgs();
// 获取连接点方法的签名
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
// 方法签名中获取注解
TypeCheck ann = method.getAnnotation(TypeCheck.class);
String content = "";
if (ann.paramIndex() < args.length) {
content = (String) args[ann.paramIndex()];
}
// 根据注解中的属性,从工厂类中取出真正的执行者
TypeProcessor processor = typeFactory.getProcessor(ann.type());
// 前置方法
processor.before();
// 执行连接点的方法
joinPoint.proceed();
// 后置方法
processor.after(Lists.newArrayList(content));
return null;
}
}
XML配置:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 切面处理器 -->
<bean id="checkHandler" class="com.example.demo.test.ann.TypeCheckHandler"/>
<!-- 拦截切面 -->
<aop:config proxy-target-class="true">
<aop:aspect ref="checkHandler">
<!-- 该expression表示拦截使用TypeCheck注解的地方 -->
<aop:pointcut id="typeCut" expression=" @annotation(com.example.demo.test.ann.TypeCheck)"/>
<!-- 设定Advice通知时机,我这边使用的是Around环绕通知 -->
<aop:around pointcut-ref="typeCut" method="doAround"/>
</aop:aspect>
</aop:config>
</beans>
接口和枚举
接口定义:1
2
3
4
5
6
7
8
9
10
11
12
13public interface TypeProcessor {
/**
* Before
*/
void before();
/**
* After
* @param content 自定义内容
*/
void after(List<String> content);
}
枚举:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public enum Type {
/**
* 客户
*/
CLIENT("c", "客户"),
/**
* 供应商
*/
SUPPLIER("s", "供应商");
private String code;
private String text;
Type(String code, String text) {
this.code = code;
this.text = text;
}
}
实际执行者ClientProcessor
1 |
|
执行者工厂类Factory
根据注解的属性判断实际取哪个执行者:
1 |
|
测试代码
1 | 0) (type = Type.CLIENT, paramIndex = |
例如我传入的content是“TEST TEST TEST”,实际输出接口为:
Before I’m a client
Hei, I’m the middle process
I’m a client processor
TEST TEST TEST,
可以看到该方法成功被切面注入了~~