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依赖:

1         <dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-aop</artifactId>
4 </dependency>
5 <dependency>
6 <groupId>org.aspectj</groupId>
7 <artifactId>aspectjweaver</artifactId>
8 <version>1.9.7</version>
9 </dependency>

接着我们按照3个基本操作来添加aop能力:

1、设置切入面
设置切面的常用方式有两种,我们依次来看
(1)使用注解的形式

 1 package com.example.demo.learnaop;
2
3
4 import java.lang.annotation.ElementType;
5 import java.lang.annotation.Retention;
6 import java.lang.annotation.RetentionPolicy;
7 import java.lang.annotation.Target;
8
9 @Target(ElementType.METHOD)
10 @Retention(RetentionPolicy.RUNTIME)
11 public @interface LogAop {
12 }

如上先定义一个注解:@Target,我们设置为method,@Retention,我们设置为runtime,该注解可被标记到方法中,同时运行时期要使用该注解。(关于java的注解,属于java的基础知识,但是在新兴的框架中,他的作用越来越大,我抽时间会写一篇相关的文章)

定义如下的切面类:
随意定义一个切面方法,方法的注解@PointCut,标记好要增加的注解的全限定类名。
然后我们就可以在我们想要设置的切面出设置切入点了,如下

1 public class AopAdvice {
2
3 @Pointcut("execution(* com.example.demo.learnaop.DoService.learnMinus(..))")
4 public void logAopCut() {
5 int a=1;
6 System.out.println("point cut 123 " );
7 // log.warn("ex advice1");
8 }
9 }

业务代码像这样添加定义好的注解,如红色字体:

1     @LogAop //像这样
2 @Override
3 public String learnMinus(String para1, String para2) {
4 // log.warn("start Minus");
5 System.out.println("service learn minus "+para1 +para2 );
6 return para1 + "-" + para2;
7 }

这种方式比较符合目前的编程思路,(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )尽可能的使用各种注解来代替原有的各种配置,降低配置的维护难度。

(2)使用execution 表达式
我们可以不定义注解,直接在切面方法上设置,要切入的点,如下:

1 public class AopAdvice {
2
3 @Pointcut("execution(* com.example.demo.learnaop.DoService.learnMinus(..))")
4 public void logAopCut() {
5 int a=1;
6 System.out.println("point cut 123 " );
7 // log.warn("ex advice1");
8 }
9 }

execution后边的部分,我们使用的表达式称之为 execution表达式

这是一种类似于正则的表达式,总体的结构如下图

问号部分我们可填也可以不填,同时我们可以使用*,..来实现模糊匹配,

* 可以模糊匹配,某一个层级的选项,或者某一层级一部分的选项,比如我们想省略某一层级包名,也可以省略方法名的某一部分。
.. 可以用来省略多级选项。
限于篇幅有限,这里就不过多的介绍execution表达式了。
这样我们就可以直接根据全限定路径,直接指定某一层级方法作为切入点了。
这里有两点需要注意的是:
如果使用的注解表达式,则注解加入到接口中,是不能在实现类中添加切入点的,换句话说不会直接生效。
注意:使用execution表达式时,如果表达式匹配的是父类或接口,则对应子类的切入点是会生效的。这里也和java中注解不会直接继承,继承类和接口实现类,却可以替代类和接口中的方法是一个效果。

2、编写增强能力
我们继续在OPTAopAdvice类中添加如下方法:

方法的注解可以依次使用
@Before 切入面执行执行
@After 切入面返回之后
@Around 切入面环绕
@AfterReturning 切入面正常返回后
@AfterThrowing 切入面异常返回后
@After 是包含@AfterReturning @AfterThrowing两种场景的。
像下面这样,我们就可以定义几个增强能力

 1 public class AopAdvice {
2
3
4 @Before("logAopCut()")
5 public Object logBefore() {
6 System.out.println("log before !!!" );
7 return "123456654rjdkkgjlkjg";
8 }
9
10 @Before("optAopCut()")
11 public Object optBefore() {
12 System.out.println("opt before !!!" );
13 return "123456654rjdkkgjlkjg";
14 }
15
16 @After("optAopCut()")
17 public void optAfter() {
18 System.out.println("opt after !!!" );
19
20 }
21
22 @After("logAopCut()")
23 public void logAfter() throws Throwable {
24 System.out.println("log after !!!" );
25
26 }
27
28 @Around("optAopCut()")
29 public Object optAround1(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
30 System.out.println("around start1 " );
31 Object proceed = proceedingJoinPoint.proceed();
32 System.out.println("around end1 " );
33 return proceed;
34 }
35
36 @Around("optAopCut()")
37 public Object optAround2(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
38 System.out.println("around start2 " );
39 Object proceed = proceedingJoinPoint.proceed();
40 System.out.println("around end 2" );
41 return proceed;
42 }
43 }

3、织入
这一步理论上来说最复杂,但是和具体业务逻辑又距离最远,(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )所以spring早已替我们封装好了
我们只需要在OPTAopAdvice类上添加@Aspect @Component,分别表示要进行切入处理,和进行springboot的bean管理。

整体的代码如下:

controller层

 1 package com.example.demo.learnaop;
2
3 import lombok.extern.slf4j.Slf4j;
4 import org.springframework.beans.factory.annotation.Autowired;
5 import org.springframework.web.bind.annotation.GetMapping;
6 import org.springframework.web.bind.annotation.RequestParam;
7 import org.springframework.web.bind.annotation.RestController;
8
9 import java.util.Date;
10
11 /**
12 * @discription
13 */
14 @Slf4j
15 @RestController
16 public class Controller {
17
18 @Autowired
19 private DoService doService;
20
21 @Autowired
22 private DoServiceImpl doServiceImpl;
23
24 @Deprecated
25 @GetMapping("/learn/add")
26 public String learnAdd(@RequestParam("para1") String para1, @RequestParam("para2") String para2) {
27 // log.debug("show plugin Profile {} ,{}", para1, para2);
28 System.out.println("controller learn add " + para1 + para2);
29 return doService.learnMinus(para1, para2) + doService.learnAdd(para1, para2);
30 }
31
32 @Deprecated
33 @GetMapping("/learn/minus")
34 public String learnMinus(@RequestParam("para1") String para1, @RequestParam("para2") String para2) {
35 // log.debug("show plugin Profile {} ,{}", para1, para2);
36 System.out.println("controller learn Minus " + para1 + para2);
37 Date date = new Date();
38 return doService.learnMinus(para1, para2) + date.getTime() + date.getSeconds() + "";
39 }
40
41 }

service层

 1 package com.example.demo.learnaop;
2
3 /**
4 * @discription
5 */
6 public interface DoService {
7
8 String learnAdd(String para1, String para2);
9
10
11 String learnMinus(String para1, String para2);
12
13 }
 1 package com.example.demo.learnaop;
2
3
4 import lombok.extern.slf4j.Slf4j;
5 import org.springframework.stereotype.Service;
6
7 /**
8 * @discription
9 */
10 @Slf4j
11 @Service
12 public class DoServiceImpl implements DoService {
13 @Override
14 public String learnAdd(String para1, String para2) {
15 System.out.println("service learn add "+para1 +para2 );
16 return para1 + "+" + para2;
17 }
18
19 @OPTAop
20 @Override
21 public String learnMinus(String para1, String para2) {
22 // log.warn("start Minus");
23 System.out.println("service learn minus "+para1 +para2 );
24 return para1 + "-" + para2;
25 }
26
27 @Override
28 public void learnNothing() {
29 System.out.println("service learn do nothing " );
30
31 }
32 }

服务端口我们设置为8081,

请求如下url

http://127.0.0.1:8081/learn/minus?para1=1a&para2=2b

控制台输出如下

 1 controller learn Minus 1a2b
2 around start1
3 around start2
4 log before !!!
5 opt before !!!
6 service learn minus 1a2b
7 opt after !!!
8 log after !!!
9 around end 2
10 around end1

注意看这里有两个细节

1、执行顺序,

around先执行,然后才会执行 before、after 接着又跳转回around,也就是说before 和after 更接近切面点,这一点我们在处理诸如分布式锁的场景要考虑到。

2、可以在一个切入点加入多个方法,

切入顺序一般是按照代码的加载顺序(书写顺写)来加入的,尽管是在一个切入点加入了多个增强方法,但是只执行一遍切入面的代码。(他的原理是什么呢?如何模仿或者实现呢,我后文会详细介绍)

以上说的比较多,这里我们总结一下,

1、aop是面向对象的补充,是针对多个对象的共同特性,我们统一增强能力的一个途径。

2、自定义aop编程只要实现3部分:设置切入点,编写增强能力,织入

什么是AOP,以及在Springboot中自定义AOP的更多相关文章

  1. Springboot中使用AOP统一处理Web请求日志

    title: Springboot中使用AOP统一处理Web请求日志 date: 2017-04-26 16:30:48 tags: ['Spring Boot','AOP'] categories: ...

  2. 在SpringBoot中配置aop

    前言 aop作为spring的一个强大的功能经常被使用,aop的应用场景有很多,但是实际的应用还是需要根据实际的业务来进行实现.这里就以打印日志作为例子,在SpringBoot中配置aop 已经加入我 ...

  3. SpringBoot图文教程5—SpringBoot 中使用Aop

    有天上飞的概念,就要有落地的实现 概念+代码实现是本文的特点,教程将涵盖完整的图文教程,代码案例 文章结尾配套自测面试题,学完技术自我测试更扎实 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例 ...

  4. 详解Springboot中自定义SpringMVC配置

    详解Springboot中自定义SpringMVC配置 WebMvcConfigurer接口 ​ 这个接口可以自定义拦截器,例如跨域设置.类型转化器等等.可以说此接口为开发者提前想到了很多拦截层面的需 ...

  5. 编写SpringBoot 中的AOP

    编写SpringBoot 中的AOP 在程序开发的过程中会使用到AOP的思想,面向切面进行开发,比如登录的验证,记录日志等等-频繁需要操作的步骤,在遇到这种情况时就要使用Spring 的AOP了 Sp ...

  6. SpringBoot中搭配AOP实现自定义注解

    1 springBoot的依赖 确定项目中包含可以注解的依赖 <dependency> <groupId>org.springframework.boot</groupI ...

  7. SpringBoot中使用AOP打印接口日志的方法(转载)

    前言 AOP 是 Aspect Oriented Program (面向切面)的编程的缩写.他是和面向对象编程相对的一个概念.在面向对象的编程中,我们倾向于采用封装.继承.多态等概念,将一个个的功能在 ...

  8. springboot中使用aop技术

    aop是面向切面编程的意思,它可以需要先选择一些切入点,然后对这些切入点进行拦截,注入统一的代码逻辑,这也是解耦的一种方式,也是为了避免重复的代码,让开发人员把关注点放在业务上. 引用包 'org.s ...

  9. spring-boot中的AOP

    public class User { private Integer id; private String username; private String note; public User(In ...

  10. springboot中自定义根路径的配置

    Spring boot默认是/ ,这样直接通过http://ip:port/就可以访问到index页面,如果要修改为http://ip:port/path/ 访问的话,那么需要在Application ...

随机推荐

  1. Linux开机启动三种方式

    有的时候,我们开机启动一些命令或者是一段脚本,又或者是开机启动自定义的服务. 下面归纳了2种实现的方式. 方式1-开机启动命令 vim /etc/rc.local #添加你想执行的命令 chmod + ...

  2. 推荐一款基于业务行为驱动开发(BDD)测试框架:Cucumber!

    大家好,我是狂师. 今天给大家介绍一款行为驱动开发测试框架:Cucumber. 1.介绍 Cucumber是一个行为驱动开发(BDD)工具,它结合了文本描述和自动化测试脚本.它使用一种名为Gherki ...

  3. 高通平台UEFI有关介绍

    高通平台UEFI有关介绍 背景 我需要在高通平台上学习点亮LCD,目前通过同事在别的平台的配置代码,我已经将kernel部分的屏幕点亮了:剩余的工作量就在BP侧,也就是系统刚开机的那一段时间.在开发过 ...

  4. 基于RK3588的8K视频解码显示案例分享!引领超高清工业视频时代

    8K.4K.2K显示对比 2K分辨率:也称为全高清(FULL HD),它具有1920 x 1080像素的分辨率.这是目前大多数消费者电视和电脑显示器的标准分辨率,可以提供良好的图像质量. 4K分辨率: ...

  5. 从零开始带你上手体验Sermant自定义插件开发

    本文分享自华为云社区<Sermant自定义插件开发上手体验>,作者:华为云开源. 一.研究缘由 由于目前我们所处的行业是汽车行业,项目上进行云服务的迁移时使用到了Sermant中的相关插件 ...

  6. element-plus如何隐藏el-row

    在 Element Plus 中,el-row 是用于布局的组件,如果你想要隐藏 el-row,你可以使用 CSS 的 display 属性将其设置为 none.以下是一个简单的示例: <tem ...

  7. Modbus转Profinet网关模块连PLC与流量计通讯案例

    一.案例背景 在饮品加工厂中,会涉及到流量计的使用,然而达到对流量计的精准控制和数据采集需要用到PLC,由于PLC和流量计可能使用不同的通信协议(如Profinet和Modbus),造成两者不能自接进 ...

  8. oeasy教您玩转vim - 35 - # 正则表达

    ​ 查找进阶 回忆上节课内容 实时搜索 :set incsearch 大写小写 ignorecase 查找当前单词 * 正向按单词 # 反向按单词 g* 正向不按单词 g# 反向不按单词 继续查找 n ...

  9. 第九节 JMeter基础-高级登录【接口关联-鉴权】

    声明:本文所记录的仅本次操作学习到的知识点,其中商城IP错误,请自行更改. 背景:电商的功能:登录.加入购物车.提交订单.问题:谁把什么商品加入了购物车?这时需要把上一个接口的响应数据(登录成功后返回 ...

  10. [rCore学习笔记 06]运行Lib-OS

    QEMU运行第一章代码 切换分支 git checkout ch1 detail git checkout ch1 命令是用来切换到名为 ch1 的分支或者恢复工作目录中的文件到 ch1 提交的状态 ...