Spring-AOP基础(全在这里)

八股文部分来源于网络,例子为原创

OOP(Object-oriented programming)

也就是面向对象编程,继承,封装,多态。
局限性

  • 静态语言:类结构一旦定义,不容易被修改(并不是无法修改)。
  • 只能侵入性扩展:通过继承和组合组织新的类结构(比如我们继承一个类然后重写法,在子类里面就可以使用super关键字调用父类的方法,同时在子类拓展我们的逻辑。)

AOP 应用场景

  • 日志场景:比如log4j或logback中的MDC,比如打印方法的执行时间。
  • 统计场景:比如方法执行次数,执行异常次数,数据抽样,
  • 安防场景:比如熔断(Hystrix),限流和降级(Sentinel),认证和授权(Spring Security),监控(JMX)
  • 性能场景:比如缓存(Spring Cache),超时控制

AOP的术语

AOP主要是拦截OOP中的方法,在Spirng中只支持拦截方法。让我们关注横切面,进行拦截。
1 切面(Aspect)
各个方面都是针对横切问题的模块化单位。它们的行为有点类似于Java类,但也可能包括切入点、建议和类型间声明。也就是把我们关注的公共的东西抽象处来形成一个切面。
2 连接点(Join Point)
程序执行过程中的一个点,例如方法的执行或异常的处理。在Spring AOP中,连接点总是表示方法的执行。
3 切点 Pointcut
用于匹配Joint Point 的条件,决定了我们是不是在这些切入点进行接入。
4 Advice 通知(befroe, after, around需要手动调用)
我们找到了切入点,我们需要执行的动作,在一个特定的Joint Poiont 进行操作。一个Aspect 多应多个JointPoint,一个Join Point对应多个Advice。

AOP的设计模式

  • 代理模式 静态代理和动态代理
  • 判断模式 类,方法,注解,参数,异常
  • 拦截模式 前置,后置,返回,异常

使用方式

添加依赖
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-aop</artifactId>
     <version>5.2.2.RELEASE</version>
 </dependency>
 <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>5.2.2.RELEASE</version>
 </dependency>
 <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>1.9.6</version>
 </dependency>

​aspectjweaver包含aspectjrt,所以我们只需要引入aspectjweaver依赖包就可以了

基于注解

1 前置通知
2 后置通知
3 环绕通知
首先我们定义一个切面类

@Aspect
public class AspectDemo {
    private String templateStr = "=========================== %s ==============";
    @Pointcut("execution(public * *(..))")
    private void anyPublicPointCut() {

    }
    @Before("anyPublicPointCut()")
    public void anyPublicBefore() {
        System.out.println(String.format(templateStr, "前置拦截"));
    }
    @After("anyPublicPointCut()")
    public void anyPublicAfter() {
        System.out.println(String.format(templateStr, "后置拦截"));
    }
    @Around("anyPublicPointCut()")
    public void anyPublicAround(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println(String.format(templateStr, "环绕拦截"));
        // 特别注意这里需要我们手动去调用
        joinPoint.proceed();
    }
}

在客户端进行使用:

@EnableAspectJAutoProxy
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Client.class, AspectDemo.class);
        context.refresh();
        Client bean = context.getBean(Client.class);
        bean.execute();
    }
    public void execute() {
        System.out.println("================== 执行方法内部逻辑 ==============");
    }
}

执行结果:
在这里插入图片描述
小结:
1 关于顺序问题我们可以看到在同一切面遵循环绕通知 -> 前置通知 -> 后置通知的顺序 当然后置通知里面又有分类,后文会进行介绍。
2 前置通知,后置通知都是框架主动执行,只有环绕通知需要我们手动去调用。

基于xml

1 切面申明
2 前置通知
3 后置通知
4 环绕通知

// 切面的方法
public class InterceptConfig {
    private String templateStr = "=========================== xml %s ==============";

    public void anyPublicBefore() {
        System.out.println(String.format(templateStr, "前置拦截"));
    }

    public void anyPublicAfter() {
        System.out.println(String.format(templateStr, "后置拦截"));
    }
    public void anyPublicAround(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println(String.format(templateStr, "环绕拦截"));
        joinPoint.proceed();
    }
}

配置切面

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="aspectActionConfig" class="com.liyong.learn.aop.annotation.InterceptConfig"></bean>
    <bean id="client" class="com.liyong.learn.aop.annotation.Client"></bean>
    <aop:config>
        <aop:aspect ref="aspectActionConfig" >
            <aop:before method="anyPublicBefore" pointcut="execution(public * *(..))"></aop:before>
            <aop:after method="anyPublicAfter" pointcut="execution(public * *(..))"></aop:after>
            <aop:around method="anyPublicAround" pointcut="execution(public * *(..))"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>
public class Client {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        Client bean = context.getBean(Client.class);
        bean.execute();
    }
    public void execute() {
        System.out.println("================== 执行方法内部逻辑 ==============");
    }
}

可以看到和上面的运行结果一样:
在这里插入图片描述

基于API

1 切面申明
2 前置通知
3 后置通知
4 环绕通知
定义切面表达式:

public class ApiPointCut extends StaticMethodMatcherPointcut {
    // 要拦截的方法
    private final String methodName;
    // 要拦截的类
    private final Class targetClass;
    public ApiPointCut(String methodName, Class targetClass) {
        super();
        this.methodName = methodName;
        this.targetClass = targetClass;
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return Objects.equals(methodName, method.getName())
                && (this.targetClass.isAssignableFrom(targetClass) || this.targetClass == targetClass);
    }
}

定义拦截器:

public class CustomMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        System.out.println("==========执行拦截===" + method);
        // 这里一定要调用否则方法不会执行
        return invocation.proceed();
    }
}

使用方式:

public class Client {
   public static void main(String[] args) {
       // 定义一个切面 里面制定了我们要拦截的方法及我们要拦截的规则
       ApiPointCut pointCut = new ApiPointCut("execute", Client.class);
       // 定义一个对象
       Client client = new Client();
       // 这是SpirngAop 给我们提供的生产代理后的对象的工厂 当然这个工厂也可以通过配置的方式配置在xml
       ProxyFactory proxy = new ProxyFactory(client);
       DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointCut, new CustomMethodInterceptor());
       proxy.addAdvisor(advisor);
       // 通过代理获取对象
       Client proxyClient = (Client) proxy.getProxy();
       proxyClient.execute();
   }
   public void execute() {
       System.out.println("================== 执行方法内部逻辑 ==============");
   }
}

执行结果:
在这里插入图片描述
当然我们还可以使用其它的工厂类,通过API的方式来使用AOP:
在这里插入图片描述
我们可以看到ProxyFactoryBean,ProxyFactory,AspectJProxyFactory都是来源于几类AdviceSupport。所以我们可以通过这几个工厂去创建AOP.

// 这个例子对map的put方法进行拦截
public static void main(String[] args) {
    Map<String, Object> cache = new HashMap<>();
    AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(cache);
    aspectJProxyFactory.addAdvice(new MethodBeforeAdvice() {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            if ("put".equals(method.getName()) && args.length == 2) {
                System.out.println("========== 前置拦截 ==========");
            }
        }
    });
    Map<String, Object>  proxy = aspectJProxyFactory.getProxy();
    proxy.put("1", "1");
}

addAdvice可以有很多方式前置后置,环绕等,他们的顶级接口都是Advice:
在这里插入图片描述

自动动态代理

1 BeanNameAutoProxyCreator 通过bean名称的方式进行判断是否需要代理

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="aspectActionConfig" class="com.liyong.learn.aop.annotation.InterceptConfig"></bean>
    <bean id="client" class="com.liyong.learn.auto.Client"></bean>
    <!--定义拦截器-->
    <bean id="interceptor" class="com.liyong.learn.aop.annotation.CustomMethodInterceptor"></bean>
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*"></property>
        <property name="interceptorNames" >
            <!--指定拦截器-->
            <value>interceptor</value>
        </property>
    </bean>
</beans>
public class Client {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("auto.xml");
        Client bean = context.getBean(Client.class);
        bean.execute();
    }
    public void execute() {
        System.out.println("================== 执行方法内部逻辑 ==============");
    }
}

在这里插入图片描述

2 DefaultAdvisorAutoProxyCreator

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="defaultCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
    <bean id="client" class="com.liyong.learn.auto.Client"></bean>
    <bean id="clientOne" class="com.liyong.learn.auto.ClientOne"></bean>
    <bean id="interceptor" class="com.liyong.learn.aop.annotation.CustomMethodInterceptor"></bean>
    <bean id="pointCut" class="com.liyong.learn.aop.annotation.ApiPointCut">
        <!-- 指定方法名 -->
        <constructor-arg index="0" value="execute"></constructor-arg>
        <!-- 指定类名 -->
        <constructor-arg index="1" value="com.liyong.learn.auto.Client"></constructor-arg>
    </bean>
    <!--  配置一个通知 就需要配置pointCut 以及要执行的动作 -->
    <bean class="org.springframework.aop.support.DefaultPointcutAdvisor" name="advice">
        <constructor-arg index="0" ref="pointCut"></constructor-arg>
        <constructor-arg index="1" ref="interceptor"></constructor-arg>
    </bean>
</beans>
public class Client {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("auto.xml");
        Client bean = context.getBean(Client.class);
        bean.execute();
    }
    public void execute() {
        System.out.println("================== 执行方法内部逻辑 ==============");
    }
}

DefaultAdvisorAutoProxyCreator读取了上下文环境,发现配置的又AOP于是自动代理了,我们得到的是道理以后的对象
在这里插入图片描述

3 AnnotationAwareAspectJAutoProxyCreator @EnableAspectJAutoProxy 这是基于注解的形式来实现AOP我们只需要注解就能够成功的对我们们关心的地方进行拦截。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

before,after 与 Around 的执行顺序

1 在同一个Aspect里面Around优先于before
2 如果不在同一个Aspect里面,则是通过Order来控制的

@Aspect
public class AspectOne implements Ordered {
    @Pointcut("execution(public * *(..))")
    private void anyPublicOperation() {
    }

    @Before("anyPublicOperation()")
    public void beforeAnyPublicMethod() {
        System.out.println("====== before one ==========");
    }

    @Around("anyPublicOperation()")
    public Object aroundAnyPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("=========== around one ====== ");
        // 主动调用
        return joinPoint.proceed();
    }
    @Override
    public int getOrder() {
        return 1;
    }
}
@Aspect
public class AspectTwo implements Ordered {
    @Pointcut("execution(public * *(..))")
    private void anyPublicOperation() {
    }

    @Before("anyPublicOperation()")
    public void beforeAnyPublicMethod() {
        System.out.println("====== before two ==========");
    }
    @After("anyPublicOperation()")
    public void afterAnyPublicMethod(){
        System.out.println("====== after two ==========");
    }
    @Override
    public int getOrder() {
        return 2;
    }
}
@Configuration // 标注以后会有增强提升
@EnableAspectJAutoProxy // 激活自动代理
public class AspectDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AspectOne.class, AspectTwo.class, AspectDemo.class);
        context.refresh();
        AspectDemo bean = context.getBean(AspectDemo.class);
        bean.execute();
        context.close();
    }
    public void execute() {
        System.out.println(111);
    }
}

可以看到同一个aspect里面Around会先执行,然后就是数字越低越优先执行:
在这里插入图片描述

三种After 的关系

1 AfterReturning
2 AfterThrowing
3 After

@Aspect
public class AfterConfig {
    @Pointcut("execution(public * *(..))")
    private void anyPublicOperation() {
    }
    @After("anyPublicOperation()")
    public void after(){
        System.out.println("====== after=========");
    }
    @AfterReturning("anyPublicOperation()")
    public void afterReturning(){
        System.out.println("====== afterReturning =========");
    }
    @AfterThrowing("anyPublicOperation()")
    public void afterThrowing() {
        System.out.println("======  afterThrowing =========");
    }
}
@Configuration
@EnableAspectJAutoProxy
public class AfterDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AfterConfig.class, AfterDemo.class);
        context.refresh();
        AfterDemo bean = context.getBean(AfterDemo.class);
        bean.execute();
    }
    public void execute() {
        Random random = new Random();
        boolean flag = random.nextBoolean();
        if (flag) {
            throw new RuntimeException();
        }
        System.out.println("execute");
    }
}

可以看到我们三种After的执行顺序是先after类似于finally,然后是afterReturing,如果是有异常的话就是after和afterThrowing.通过配置的方式参考前面的配置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/445622.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

太强了!最全的大模型检索增强生成(RAG)技术概览!

本文是对检索增强生成&#xff08;Retrieval Augmented Generation, RAG&#xff09;技术和算法的全面研究&#xff0c;对各种方法进行了系统性的梳理。文章中还包含了我知识库中提到的各种实现和研究的链接集合。 鉴于本文的目标是对现有的RAG算法和技术进行概览和解释&#…

【深度学习笔记】6_5 RNN的pytorch实现

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 6.5 循环神经网络的简洁实现 本节将使用PyTorch来更简洁地实现基于循环神经网络的语言模型。首先&#xff0c;我们读取周杰伦专辑歌词…

【C++】list模拟实现list迭代器失效问题

list模拟实现&list迭代器失效问题 一&#xff0c;list模拟实现1. list的主要框架接口模拟2. list构造&拷贝构造&析构3. list迭代器3.1 普通迭代器3.2 const迭代器 4. 增删查改 二&#xff0c;迭代器失效问题1. list的迭代器失效原因2. 解决办法 一&#xff0c;list…

个推与华为深度合作,成为首批支持兼容HarmonyOS NEXT的服务商

自华为官方宣布HarmonyOS NEXT鸿蒙星河版开放申请以来&#xff0c;越来越多的头部APP宣布启动鸿蒙原生开发&#xff0c;鸿蒙生态也随之进入全新发展的第二阶段。 作为华为鸿蒙生态的重要合作伙伴&#xff0c;个推一直积极参与鸿蒙生态建设。为帮助用户在HarmonyOS NEXT上持续享…

PostGIS 中的 K-Means 聚类操作及应用

K-Means算法&#xff1a; K-means 是数据科学和商业的基本算法。让我们深入了解一下。 1. K-means是一种流行的用于聚类的无监督机器学习算法。它是用于客户细分、库存分类、市场细分甚至异常检测的核心算法。 2. 无监督&#xff1a;K-means 是一种无监督算法&#xff0c;用于…

《MySQL数据库》day2--连接查询、子查询、union、limit、DML语句

文章目录 1.把查询结果去除重复记录 -》distinct2.连接查询2.1什么是连接查询&#xff1f;2.2连接查询的分类2.3笛卡尔积现象2.4内连接2.4.1内连接之等值连接。2.4.2内连接之非等值连接2.4.3内连接之自连接 2.5外连接2.6三张表&#xff0c;四张表怎么连接&#xff1f; 3.子查询…

从0到1入门C++编程——11 函数对象及算法介绍

文章目录 函数对象1、谓词2、内建函数对象(1) 算术仿函数(2) 关系仿函数(3) 逻辑仿函数 常用算法1、常用遍历算法(1) for_each(2) transform 2、常用查找算法(1) find和find_if(2) find_if(3) adjacent_find(4) binary_search(5) count(6) count_if 3、常用排序算法(1) sort(2)…

奇舞周刊第521期:实现vue3响应式系统核心-MVP 模型

奇舞推荐 ■ ■ ■ 实现vue3响应式系统核心-MVP 模型 手把手带你实现一个 vue3 响应式系统&#xff0c;代码并没有按照源码的方式去进行组织&#xff0c;目的是学习、实现 vue3 响应式系统的核心&#xff0c;用最少的代码去实现最核心的能力&#xff0c;减少我们的学习负担&…

序列化相关知识总结

目录 一、序列化1.1 基本概念1.1.1 序列化1.1.2 反序列化1.1.3 数据结构、对象与二进制串1.1.4 序列化/反序列化的目的 1.2 几种常见的序列化和反序列化协议1.2.1 XML&SOAP1.2.2 JSON&#xff08;Javascript Object Notation&#xff09;1.2.3 Protobuf 二、安卓下的序列化…

RabbitMQ中4种交换机的Java连接代码

目录 1.直连交换机&#xff08;Direct&#xff09; 生产者代码示例 消费者代码示例 2.RabbitMQ连接工具类 3.Fanout交换机&#xff08;扇出交换机&#xff0c;广播&#xff09; 生产者 消费者 4.Topic交换机&#xff08;主题交换机&#xff09; 生产者 消费者 5.Hea…

数据库-第六/七章 关系数据理论和数据库设计【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下数据库系统概论中的重点概念&#xff0c;以供大家期末复习和考研复习的时候使用。 参考资料是王珊老师和萨师煊老师的数据库系统概论(第五版)。 数据库系统概论系列文章传送门&#xff1a; 第一章 绪论 第二/…

【Docker】容器的概念

容器技术&#xff1a;容器技术是基于虚拟化技术的&#xff0c;它使应用程序从一个计算机环境快速可靠地转移到另一个计算机环境中&#xff0c;可以说是一个新型地虚拟化技术。 一、docker容器 Docker:是一个开源地容器引擎Docker 是一种轻量级的容器化技术&#xff0c;其主要原…

阿里云服务器租用多少钱一个月?9元1个月?

阿里云服务器租用多少钱一个月&#xff1f;9元1个月&#xff1f;已经降价到5元一个月了。阿里云服务器1个月最低5元/月起&#xff0c;阿里云服务器价格可以按年、按月和按小时购买&#xff0c;本文阿里云服务器网aliyunfuwuqi.com来详细说下阿里云服务器一个月收费价格表&#…

计算机系统结构-中断例题笔记

背景&#xff1a;计算机系统结构考试中&#xff0c;中断处理程序、运行程序的过程示意图是重要考点。 中断概念&#xff1a;CPU中止正在执行的程序&#xff0c;转去处理随机提出的请求&#xff0c;待处理完后&#xff0c;再回到原先被打断的程序继续恢复执行的过程。 考点1.设…

WPF 自定义彩色控制台功能

文章目录 前言环境流内容一个简单的控制台 自动添加数据无法添加数据模板代码添加参数简单的案例添加和清空功能完善代码 额外功能添加移动到底部添加样式 总结 前言 在WPF中添加模拟控制台&#xff0c;可以试试的看到最新的日志信息。但是普通的TextBlock只是纯粹的黑色&…

分布式执行引擎ray入门--(2)Ray Data

目录 一、overview 基础代码 核心API&#xff1a; 二、核心概念 2.1 加载数据 从S3上读 从本地读&#xff1a; 其他读取方式 读取分布式数据&#xff08;spark&#xff09; 从ML libraries 库中读取&#xff08;不支持并行读取&#xff09; 从sql中读取 2.2 变换数据…

html--彩虹马

文章目录 htmljscss 效果 html <!DOCTYPE html> <html lang"en" > <head> <meta charset"UTF-8"> <title>Rainbow Space Unicorn</title> <link rel"stylesheet" href"css/style.css"> &l…

TCP/IP 七层架构模型

传输控制协议&#xff08;TCP&#xff0c;Transmission Control Protocol&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。 套接字&#xff08;socket&#xff09;是一个抽象层&#xff0c;应用程序可以通过它发送或接收数据&#xff0c;可对其进行像对文…

【Linux】常用操作命令

目录 基本命令关机和重启帮助命令 用户管理命令添加用户&#xff1a;useradd 命令修改密码&#xff1a;passwd 命令查看登录用户&#xff1a;who 命令查看登录用户详细信息 :w切换用户 目录操作命令cdpwd命令目录查看 ls [-al] 目录操作【增&#xff0c;删&#xff0c;改&#…

NUMA(Non-Uniform Memory Access)架构的介绍

1. NUMA由来 最早的CPU是以下面这种形式访问内存的&#xff1a; 在这种架构中&#xff0c;所有的CPU都是通过一条总线来访问内存&#xff0c;我们把这种架构叫做SMP架构&#xff08;Symmetric Multi-Processor&#xff09;&#xff0c;也就是对称多处理器结构。可以看出来&…