Spring-AOP基础(全在这里)

news2024/10/1 21:32:03

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

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.通过配置的方式参考前面的配置。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言 --- 指针(5)

目录 一.sizeof和strlen对比 1.sizeof 2.strlen 3.strlen 和sizeof的对比 二.数组和指针笔试题目详解 回顾&#xff1a;数组名的理解 1.一维数组 2.字符数组 代码1&#xff1a; 代码2&#xff1a; 代码3&#xff1a; 代码4&#xff1a; 代码5&#xff1a; 代码6&am…

《JAVA与模式》之门面模式

系列文章目录 文章目录 系列文章目录前言一、医院的例子二、门面模式的结构三、门面模式的实现四、门面模式的优点五、门面模式在Tomcat中的使用前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,…

【论文阅读】OpsEval

粗糙翻译&#xff0c;如有兴趣请看原文&#xff0c;链接&#xff1a;https://arxiv.org/abs/2310.07637 摘要 信息技术&#xff08;IT&#xff09;运营&#xff08;Ops&#xff09;&#xff0c;特别是用于IT运营的人工智能&#xff08;AlOps&#xff09;&#xff0c;是保持现…

改进沙猫群优化的BP神经网络ISCSO-BP(时序预测)的Matlab实现

改进沙猫群优化的BP神经网络&#xff08;ISCSO-BP&#xff09;是一种结合了改进的沙猫群优化算法&#xff08;Improved Sand Cat Swarm Optimization, ISCSO&#xff09;和反向传播&#xff08;Back Propagation, BP&#xff09;神经网络的模型&#xff0c;旨在提高时序预测的准…

LLM 推理优化探微 (2) :Transformer 模型 KV 缓存技术详解

编者按&#xff1a;随着 LLM 赋能越来越多需要实时决策和响应的应用场景&#xff0c;以及用户体验不佳、成本过高、资源受限等问题的出现&#xff0c;大模型高效推理已成为一个重要的研究课题。为此&#xff0c;Baihai IDP 推出 Pierre Lienhart 的系列文章&#xff0c;从多个维…

王道机试C++第 5 章 数据结构一:向量vector和蓝桥杯13年两个程序 Day31

5.1 向量 有限个类型相同的变量的线性集合&#xff0c;组成数组的各个变量称为数组的元素。给每个元素分配唯一的一个下标&#xff0c;就能用这个下标指代该元素。还可通过下标直接访问数组中的任何一个元素&#xff0c;并且这些访问能在常数时间内完成。 1&#xff0e;STL-v…

算法练习:同向双指针(滑动窗口)

目录 1. 长度最小的数组2. 无重复字符的最长字符串3. 最大连续1的个数4. 将x减到0的最小操作数5. 水果成篮6. 找到字符串中所有字母异位词7. 串联所有单词的子串8. 最小覆盖子串 1. 长度最小的数组 题目信息&#xff1a; 题目链接&#xff1a; 长度最小的数组思路&#xff1a; …

观察主线程退出对子线程的影响

✨主进程以pthread_exit(0)方式退出——线程退出 ✨子线程pthread_cancel方式杀死父线程 ✨主线程以return&#xff0c;exit(0)方式退出——进程退出  &#x1f531;return   &#x1f531;return在主函数   &#x1f536;return在子函数  &#x1f536;exit(0)   &…

数据结构——堆的应用 Topk问题

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

谷歌开源的LLM大模型 Gemma 简介

相关链接&#xff1a; Hugging face模型下载地址&#xff1a;https://huggingface.co/google/gemma-7bGithub地址&#xff1a;https://github.com/google/gemma_pytorch论文地址&#xff1a;https://storage.googleapis.com/deepmind-media/gemma/gemma-report.pdf官方博客&…

多址接入技术 ---FDMA、TDMA、CDMA

1、多址接入技术 所谓多址技术就是把同一个无线信道按照时间、 频率等进行分割, 使不同的用户都能够在不同的分割段中使用这一信道, 而又不会明显地感觉到他人的存在, 就好像自己在专用这一信道一样。 占用不同的分割段就像是拥有了不同的地址, 使用同一信道的多个用户就…

Vue.js过滤器:让数据展示更灵活

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Unity的相机跟随和第三人称视角二

Unity的相机跟随和第三人称视角二 展示介绍第二种相机模式远离代码总结 展示 我录的视频上可能看不太出来&#xff0c;但是其实今天的这个方法比原来写的那个方法更简便并且死角更少一些。 这里我也做了两个人物偏移的视角&#xff0c;按Q是原来的两个相机模式切换&#xff0c…

问题 | 程序员求职宝典

程序员的金三银四求职宝典 随着春天的脚步渐近,对于许多程序员来说,一年中最繁忙、最重要的面试季节也随之而来。金三银四,即三月和四月,被广大程序员视为求职的黄金时期。在这两个月里,各大公司纷纷开放招聘,求职者们则通过一轮又一轮的面试,力争心仪的职位。而如何在…

从零学习Linux操作系统 第三十二部分 Ansible中的变量及加密

一、变量的基本设定及使用方式 变量命名&#xff08;与shell相同&#xff09;&#xff1a; 只能包含数字&#xff0c;下划线&#xff0c;字母 只能用下划线或字母开头 .变量级别&#xff1a; 全局从命令行或配置文件中设定的paly:在play和相关结构中设定的主机:由清单&…

NeRF模型NeRF模型

参考视频&#xff1a;https://www.youtube.com/watch?vHfJpQCBTqZs&ab_channelVision%26GraphicsSeminaratMIT NeRF模型的输入输出: 输入: (x, y, z): 一个三维空间坐标,代表场景中的一个位置点(θ, φ): 视线方向,θ表示与y轴的夹角,φ表示与x轴的夹角,用两个角度可以…

(C语言)strcpy与strcpy详解,与模拟实现

目录 1. strcpy strcpy模拟实现&#xff1a; 实现方法1&#xff1a; 实现方法2&#xff1a; 2. strcat strcat模拟实现&#xff1a; 1. strcpy 作用&#xff1a;完成字符串的复制。 头文件&#xff1a;<string.h> destination是字符串要复制到的地点&#xff0c;s…

图片在div完全显示

效果图&#xff1a; html代码&#xff1a; <div class"container" style" display: flex;width: 550px;height: 180px;"><div class"box" style" color: red; background-color:blue; width: 50%;"></div><div …

FreeRTOS操作系统学习——同步互斥与通信

同步&#xff08;Synchronization&#xff09; 同步是一种机制&#xff0c;用于确保多个任务能够按照特定的顺序协调执行或共享数据。当一个任务需要等待其他任务完成某个操作或满足某个条件时&#xff0c;同步机制可以帮助任务进行协调和等待。 在FreeRTOS中&#xff0c;常见…

【布局:1688,阿里海外的新筹码?】1688重新布局跨境海外市场:第一步开放1688API数据采集接口

2023年底&#xff0c;阿里巴巴“古早”业务1688突然成为“重头戏”&#xff0c;尤其宣布正式布局跨境业务的消息&#xff0c;一度引发电商圈讨论。1688重新布局跨境海外市场&#xff1a;第一步开放1688API数据采集接口 2023年11月中旬&#xff0c;阿里财报分析师电话会上&…