写点什么

师爷,翻译翻译什么叫 AOP

  • 2024-01-19
    福建
  • 本文字数:3017 字

    阅读完需:约 10 分钟

张麻子:汤师爷,翻译翻译,什么叫 AOP?

汤师爷:这还用翻译。

张麻子:我让你翻译给我听,什么叫 AOP?

汤师爷:不用翻译,切面编程就是 AOP 啊。

黄四郎:难道你听不懂什么叫 AOP?

张麻子:我就想让你翻译翻译,什么叫 AOP!

汤师爷:AOP 嘛。

张麻子:翻译出来给我听,什么他妈的叫 AOP!什么他妈的叫他妈的 AOP!

汤师爷:什么他妈的叫 AOP 啊?

黄四郎:AOP 就是 Aspect Oriented Programming,面向切面编程!明白了吗?

汤师爷:这就是 AOP 啊。

张麻子:翻译翻译。

汤师爷:...

汤师爷:AOP 就是 Aspect Oriented Programming!面向切面编程!面向!切面!横着切!切面!

张麻子:哈,大哥这是他妈的 AOP 啊,小弟我马上给个三连。


下面我们好好翻译一下 AOP 切面编程。


目标


• 动态代理搞明白,AOP 就明白了


• 学会在开发中使用 Spring 的 AOP 技术


概念


AOP 是 Aspect Oriented Programming,和 OOP(Object Oriented Programming)一词之差,OOP 强调万物皆是对象,那 AOP 呢?


要真正理解 AOP 就要理解 AOP 的核心:Aspect


WTF is Aspect?


我们把 aspect 翻译成切面,但是切面这个词对应中文语义其实很难理解到位。


我们换种解释,aspect 我们理解为事物的某个方面、某个视角


与面向对象思想相对,对象强调一个整体,一个人站在你面前,我们称之为对象。


而 aspect 强调功能化、模块化、关注点分离


天气变冷了,人要多穿衣服,上帝控制这么多人的对象,总不能一件一件穿吧?


所以 AOP 思想就是把天气变冷穿衣服这件事抽离出来,模块化,单独进行关注,然后经过编码实现后,上帝就可以进行一键穿衣,节省了大量工作。


放在实际开发中,我们以最常见的日志打印为例。


我们系统中有 200 个 Controller,都要打印请求日志,那没有 AOP 思想的实践的话,我们只能一个一个在 Controller 里编写一遍又一遍的重复代码,有了 AOP 思想,就可以考虑把日志打印这件事抽离出来做成单独的业务,实现一劳永逸。


那怎么实现呢?


那就要靠我们的动态代理了。


什么?你还没看动态代理?


上帝在吗?把这个人的棉衣扒了。


Spring 实现 AOP


我们知道框架的存在意义是用来简化开发的。


这里 Spring 简化了什么呢?


自然是我们在动态代理部分编写的一大堆要么看不懂,看懂了又不想写的代码。


AOP 作为 Spring 的左膀右臂之一,自然对这部分加以简化。


但 Spring 那一大堆 xml 也是够够的。


所以 SpringBoot 才是我们永远的家。


废话不多说直接上代码示例,在 AOP 代码的编写中提出问题,解决问题,那么最后就算是学会了。


引入依赖


<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></dependency><!--aop--><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-aop</artifactId></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope></dependency>
复制代码


编写代码


编写这部分代码的逻辑也是非常符合我们的认知逻辑的。


我们前边已经说清楚 AOP 是做什么的了。


那我们编写代码就要做 3 件事:


  • • 抽离出功能模块——定义切面


  • • 确认功能代码加在哪——定义切点


  • • 确认功能代码什么时候执行——选择通知类型


我们先把 controller 给出来:


@RestControllerpublic class AopController {
    @RequestMapping("/hello")    public String sayHello(){        System.out.println("hello");        return "hello";    }}
复制代码


定义一个切面


也就是抽离出功能模块


先随便写个类。


然后就直接一个 @Aspect 就行了,那这个类就是一个切面类。


还要再加一个 @Component 将该类纳入 Ioc 容器。


就这么简单,狗来了都会写。


@Aspect@Componentpublic class AopAdvice {}
复制代码


定义一个切点


也就是确认功能代码加在哪


先随便写一个方法。


然后就直接一个 @Pointcut 就行了,那这个方法就是一个切点。


还要再加上表达式,让系统知道代码加到什么位置。


@Aspect@Componentpublic class AopAdvice {
@Pointcut("execution (* com.example.aop.controller.*.*(..))") public void test() { }}
复制代码


这时候有同学问:


啊这个 execution 是什么?


里面那又是一坨什么?


根本看不懂。


举报了。


这个我只能说,这是固定的表达式,是规定。


规定什么?


看规定之前先记住:表达式一定从右往左匹配。


看规定之前先记住:表达式一定从右往左匹配。


看规定之前先记住:表达式一定从右往左匹配。


execution(访问修饰符(可省略) 方法返回值 包名.类名.方法名(参数))参数:..代表任何参数方法: *代表任何方法类名: *代表所有类包名: *代表所有包 ..代表子孙包返回值: *代表所有类型返回值
复制代码


具体的写法实际五花八门,而且除了 execution 还有一大堆,为了不让大脑过度疲劳,我们一次只有一个目标:


会用,但不精通。

选择通知类型


也就是确认功能代码什么时候执行


下面就是通知类型 5 种,前 3 种比较常用:


前置通知(@Before):在目标方法调用之前调用通知


后置通知(@After):在目标方法完成之后调用通知


环绕通知(@Around):在被通知的方法调用之前和调用之后执行自定义的方法


返回通知(@AfterReturning):在目标方法成功执行之后调用通知


异常通知(@AfterThrowing):在目标方法抛出异常之后调用通知


代码如下:


@Aspect@Componentpublic class AopAdvice {
@Pointcut("execution (* com.example.aop.controller.*.*(..))") public void test() {
}
@Before("test()") public void beforeAdvice() { System.out.println("beforeAdvice..."); }
@After("test()") public void afterAdvice() { System.out.println("afterAdvice..."); }
@Around("test()") public void aroundAdvice(ProceedingJoinPoint joinPoint) { System.out.println("before"); try { joinPoint.proceed(); } catch (Throwable t) { t.printStackTrace(); } System.out.println("after"); }}
复制代码


从以上代码中可以看出,@Before 和 @After 都已经简单到不能再简单了。


我们需要说一下这个 @Around 环绕通知


joinPoint.proceed()这行代码我们就理解为我们需要增强的那个方法的替身就行了。


这也不是想说的,这里我们主要讲一下 ProceedingJoinPoint joinPoint 这个参数。


ProceedingJoinPoint 是一个接口,也就是说这里实际是使用了多态,这不重要。


ProceedingJoinPoint 继承了 JoinPoint 这个接口。


这个 JoinPoint 有 2 个方法是我们需要说的。


Object getTarget();Signature getSignature();
复制代码


getTarget() 方法返回的是目标对象,即那个被增强方法所属的对象实例。


getSignature() 方法返回的是连接点的签名,即关于被调用的方法(或访问的字段等)的静态信息,如方法名、返回类型、参数类型等。


那么通过这 2 个方法,就能获取到所有的对象信息和方法信息,那么能做的事就太多了。


但是我们这里不展示更复杂的案例。


依然坚持我们本篇的策略:


懂点,但不多。


会用,但不精。


测试


启动项目,浏览器访问:


http://localhost:8080/hello
复制代码


运行结果:


beforebeforeAdvice...helloafterAdvice...after
复制代码


文章转载自:JavaCode9

原文链接:https://www.cnblogs.com/cosimo/p/17972338

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
师爷,翻译翻译什么叫AOP_Java_不在线第一只蜗牛_InfoQ写作社区