AOP?拿来把你


什么是AOP?

Aspect Oriented Programming 面向切面编程。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可用性。

面向切面,什么是切面?

比如,把一个西瓜形容成一个程序,用刀把一个西瓜分成两瓣,切开的切口就是切面。

在实际的程序开发中,web层-网关层-服务层-数据层,每一层之间可以是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个的切面。

举例

有两个程序运行过程如上图,红色方框中的部分是重复的一样的代码。在实际开发中,有很多类似的情况,而且重复的不止两个过程。

我们需要解决的问题就是简化红色方框中重复的代码,简化程序,提高开发效率。

首先想到的方法就是把相同的代码提出来,变成公共方法的形式,这样就不需要写那么多重复的代码了。

但是,这样也有弊端,那就是每次方法过来都要主动调用这个公共方法,流程多了的话,手动设置也不得劲。

于是,再优化一下,就引入了AOP横切的概念。

AOP相关概念

  • Aspect(切面):类似于Java中的类声明,一组动作。

  • Joint point(连接点):所有可以执行切面增强(advice)方法的点。

  • Pointcut(切入点):确定要进行切面增强(advice)的方法调用点。

  • Advice(增强):切面具体要做的逻辑业务。

  • Target(目标对象):织入Advice的目标对象。

  • Weaving(织入):将切面与其它对象连接起来。

这样看过去,肯定是不怎么理解它们真正的含义的

理解示例:

让我们来假设一下, 从前有一个叫爪哇的小县城, 在一个月黑风高的晚上, 这个县城中发生了命案. 作案的凶手十分狡猾, 现场没有留下什么有价值的线索. 不过万幸的是, 刚从隔壁回来的老王恰好在这时候无意中发现了凶手行凶的过程, 但是由于天色已晚, 加上凶手蒙着面, 老王并没有看清凶手的面目, 只知道凶手是个男性, 身高约七尺五寸. 爪哇县的县令根据老王的描述, 对守门的士兵下命令说: 凡是发现有身高七尺五寸的男性, 都要抓过来审问. 士兵当然不敢违背县令的命令, 只好把进出城的所有符合条件的人都抓了起来.

来让我们看一下上面的一个小故事和 AOP 到底有什么对应关系. 首先我们知道, 在 Spring AOP 中 Joint point 指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 Joint point, 通过 point cut, 我们就可以确定哪些 Joint point 可以被织入 Advice. 对应到我们在上面举的例子, 我们可以做一个简单的类比, Joint point 就相当于 爪哇的小县城里的百姓,pointcut 就相当于 老王所做的指控, 即凶手是个男性, 身高约七尺五寸, 而 Advice 则是施加在符合老王所描述的嫌疑人的动作: 抓过来审问. 为什么可以这样类比呢?

Joint point : 爪哇的小县城里的百姓: 因为根据定义, Joint point 是所有可能被织入 Advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 Joint point. 而在我们上面的例子中, 命案发生在小县城中, 按理说在此县城中的所有人都有可能是嫌疑人.

Pointcut :男性, 身高约七尺五寸: 我们知道, 所有的方法(joint point) 都可以织入 Advice, 但是我们并不希望在所有方法上都织入 Advice, 而 Pointcut 的作用就是提供一组规则来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice. 同理, 对于县令来说, 他再昏庸, 也知道不能把县城中的所有百姓都抓起来审问, 而是根据凶手是个男性, 身高约七尺五寸, 把符合条件的人抓起来. 在这里 凶手是个男性, 身高约七尺五寸 就是一个修饰谓语, 它限定了凶手的范围, 满足此修饰规则的百姓都是嫌疑人, 都需要抓起来审问.

Advice :抓过来审问, Advice 是一个动作, 即一段 Java 代码, 这段 Java 代码是作用于 point cut 所限定的那些 Joint point 上的. 同理, 对比到我们的例子中, 抓过来审问 这个动作就是对作用于那些满足 男性, 身高约七尺五寸 的爪哇的小县城里的百姓.

Aspect::Aspect 是 point cut 与 Advice 的组合, 因此在这里我们就可以类比: “根据老王的线索, 凡是发现有身高七尺五寸的男性, 都要抓过来审问” 这一整个动作可以被认为是一个 Aspect。

AOP中的Joinpoint可以有多种类型

  • 构造方法的调用

  • 字段的设置和获取

  • 方法的调用

  • 方法的执行

  • 异常的处理执行

  • 类的初始化

即,在AOP概念中我们可以在上面这些jointpoint上织入我们自定义的Advice。但是在Spring中却没有实现上面所有的jointpoint,spring只支持方法执行类型的jointpoint。

Advice的类型

  • before advice:在join point前被执行的advice,虽然before advice是在join point 之前被执行,但是它并不能够阻止joint point 执行后面的代码,除非在里面直接抛异常。

  • after return advice:在一个join point 正常返回后执行的advice。

  • after throwing advice:当一个join point 抛出异常后执行的advice。

  • after (final) advice:无论一个join point 是正常退出还是发生了异常,都会执行的advice。

  • around advice:在join point 前和join point 退出后都执行的advice,这个是最常用的。

  • introduction:可以为原有的对象增加新的属性和方法。

代码实战Demo示例

代码结构

 pom.xml

<?xml version="1.0" encoding="UTF-8"?>

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    4.0.0

    com.test
    aop
    1.0-SNAPSHOT

     aop Demo 

    
        
        
        
            org.aspectj
            aspectjweaver
            1.9.4
        
        
            org.springframework
            spring-aop
            5.3.14
        
        
        
            junit
            junit
            4.12
            compile
        
        
        
            org.springframework
            spring-core
            5.3.14
        
        
            org.springframework
            spring-context
            5.3.14
        
    

    
    
        
            
                src/main/java
                
                    **/*.properties
                    **/*.xml
                
                false
            
            
                src/main/resources
                
                    **/*.properties
                    **/*.xml
                
                false
            
        
    


beans.xml

<?xml version="1.0" encoding="UTF-8"?>

       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">

    
    
    class="com.example.testaop.service.impl.UserServiceImpl"/>
    class="com.example.testaop.advice.BeforeLog"/>
    class="com.example.testaop.advice.AfterLog"/>

    
    
        
        
        
        
        
    


    
    
    class="com.example.testaop.advice.DiyPointcut"/>

    
    
        
            
            
            
        
    

    
    
    class="com.example.testaop.advice.AnnotationPointcut"/>
    
    


AfterLog

package com.example.testaop.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * @DESC
 * @AUTHOR lh
 * @DATE 2022/1/17 15:49
 */
public class AfterLog implements AfterReturningAdvice {

    /**
     * @param returnValue 返回值
     * @param method      被调用的方法
     * @param args        被调用对象的方法的参数
     * @param target      被调用的目标对象
     * @throws Throwable
     */
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + target.getClass().getName() + "的" + method.getName() + "方法" + "返回值" + returnValue);
    }
}

AnnotationPointcut

package com.example.testaop.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * @DESC
 * @AUTHOR lh
 * @DATE 2022/1/17 16:23
 */
@Aspect
public class AnnotationPointcut {

    @Before("execution(* com.example.testaop.service.impl.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("~~~~~~方法执行前~~~~~~");
    }

    @After("execution(* com.example.testaop.service.impl.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("~~~~~~方法执行后~~~~~~");
    }

    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        System.out.println("签名" + jp.getSignature());
        // 执行目标方法
        Object proceed = jp.proceed();
        System.out.println("环绕后");
        System.out.println(proceed);
    }

}

BeforeLog

package com.example.testaop.advice;


import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * @DESC
 * @AUTHOR lh
 * @DATE 2022/1/17 15:39
 */
public class BeforeLog implements MethodBeforeAdvice {

    /**
     *
     * @param method 要执行的目标对象的方法
     * @param args 被调用方法的参数
     * @param target 目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + target.getClass().getName() + "的" + method.getName() + "方法被执行了。");
    }
}

DiyPointcut

package com.example.testaop.advice;

/**
 * @DESC
 * @AUTHOR lh
 * @DATE 2022/1/17 16:17
 */
public class DiyPointcut {

    public void before() {
        System.out.println("======方法执行前======");
    }

    public void after() {
        System.out.println("======方法执行后======");
    }
}

UserServiceImpl

package com.example.testaop.service.impl;

import com.example.testaop.service.UserService;

/**
 * @DESC
 * @AUTHOR lh
 * @DATE 2022/1/17 15:36
 */
public class UserServiceImpl implements UserService {

    public void add() {
        System.out.println("添加成功");
    }

    public void delete() {
        System.out.println("删除成功");
    }

    public void update() {
        System.out.println("更新成功");
    }

    public void search() {
        System.out.println("查询成功");
    }
}

UserService

package com.example.testaop.service;

public interface UserService {

    // 业务添加
    void add();

    // 业务删除
    void delete();

    // 业务更新
    void update();

    // 业务查找
    void search();
}

main

package com.example.testaop;

import com.example.testaop.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @DESC
 * @AUTHOR lh
 * @DATE 2022/1/17 16:01
 */
public class main {

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.search();
    }

    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }

    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.delete();
    }

}

参考文章

https://blog.csdn.net/q982151756/article/details/80513340?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164238990716780264026805%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164238990716780264026805&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-80513340.first_rank_v2_pc_rank_v29&utm_term=aop&spm=1018.2226.3001.4187

https://blog.csdn.net/qq_33369905/article/details/105828920?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164238990716780264026805%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164238990716780264026805&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-2-105828920.first_rank_v2_pc_rank_v29&utm_term=aop&spm=1018.2226.3001.4187

 

相关