AOP (Aspect Oriented Programming)一般译为面向切面编程
Aspect [ˈæspekt] n.方面;层面;(动词的)体
那么AOP 面相切面编程具体是指什么,它和之前的OOP 面相对象编程又有什么区别和联系。
先说OOP,面相对象编程简单来说,万物皆可视为对象,我们要做的就是将万物(业务逻辑中的虚拟物体),抽象为一个个对象,进而为这些抽象的物体丰富各种能力和特性(方法和属性)。从而抽象出一整段的业务逻辑,作为我们的系统。
但是在OOP的开发过程中,我们发现尽管我们已经抽象出很多对象了,但是对象之间的某些方法是有一些共性的,如果进一步抽象,则整体的抽象粒度过于小,抽象粒度过于复杂。在这种情况下,我们需要换一个角度,将这些共性的点,作为一个切入点,将我们的业务逻辑注入到里边去,直接去增强这些切入点。而这种增强的对象某个同性点的编程方式,我们就称之为AOP,即面相切面编程。(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )
举个例子:
学校的老师,每天都需要统计上课工时,政府的公务员,每天都需要统计办公工时,同时办公室的电脑也要统计每天的开机时长。
他们本质上都是对象,如果统计每天的运行时间,我们现在有两个办法来实现:
1,各自实现各自的统计办法,实现简单,但是修改复杂,而且有大量重复逻辑。
2,定义统一的接口,老师、公务员、电脑实现同样的接口,这样减少了重复逻辑,但是又实现复杂,整个抽象粒度太细了。
此时我们就可以通过AOP编程的方式,将老师、公务员、机器的办公,作为一个切入点,在这个切入点作一些对象之外的处理工作。这就是所谓的面向切面编程。这看着有点像作弊,所以很多人将aop视为面向对象编程的一个补充。是从第三方的视角,来看待面向对象编程的,如下图:
下面我们来看看,如何在当下最流行的java框架springboot框架中,实现面向切面编程:
面相切面编程主要实现三个基本操作:
1、设置切入面 Aspect (放到哪里)
2、编写增加能力,即注入到业务逻辑中的新特性 (放什么)
3、织入,即将新特性注入到原有的业务逻辑中。(怎么放进去)
假设我们现在已经有一个简易的Springboot工程,实现两个字符串的连接:
首先我们添加相关的pom依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
接着我们按照3个基本操作来添加aop能力:
1、设置切入面
设置切面的常用方式有两种,我们依次来看
(1)使用注解的形式
package com.example.demo.learnaop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAop {
}
如上先定义一个注解:@Target,我们设置为method,@Retention,我们设置为runtime,该注解可被标记到方法中,同时运行时期要使用该注解。(关于java的注解,属于java的基础知识,但是在新兴的框架中,他的作用越来越大,我抽时间会写一篇相关的文章)
定义如下的切面类:
随意定义一个切面方法,方法的注解@PointCut,标记好要增加的注解的全限定类名。
然后我们就可以在我们想要设置的切面出设置切入点了,如下
public class AopAdvice {
@Pointcut("execution(* com.example.demo.learnaop.DoService.learnMinus(..))")
public void logAopCut() {
int a=1;
System.out.println("point cut 123 " );
// log.warn("ex advice1");
}
}
业务代码像这样添加定义好的注解,如红色字体:
@LogAop //像这样
@Override
public String learnMinus(String para1, String para2) {
// log.warn("start Minus");
System.out.println("service learn minus "+para1 +para2 );
return para1 + "-" + para2;
}
这种方式比较符合目前的编程思路,(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )尽可能的使用各种注解来代替原有的各种配置,降低配置的维护难度。
(2)使用execution 表达式
我们可以不定义注解,直接在切面方法上设置,要切入的点,如下:
public class AopAdvice {
@Pointcut("execution(* com.example.demo.learnaop.DoService.learnMinus(..))")
public void logAopCut() {
int a=1;
System.out.println("point cut 123 " );
// log.warn("ex advice1");
}
}
execution后边的部分,我们使用的表达式称之为 execution表达式
这是一种类似于正则的表达式,总体的结构如下图