Spring基础 SpringAOP

2024-04-22 23:12
文章标签 java 基础 spring springaop

本文主要是介绍Spring基础 SpringAOP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

我们都知道Spring中最经典的两个功能就是IOC和AOP

我们之前也谈过SpringIOC的思想 容器编程思想了

今天我们来谈谈SpringAOP的思想

首先AOP被称之为面向切面编程

实际上面向切面编程是面向对象的编程的补充和完善

重点就是对某一类问题的集中处理

前面我们写的统一异常管理和统一结果返回以及拦截器都是基于这个思想来创建的

我们发现这里的共性就是这些操作都有一个特点,他们都是统一操作的接口...

但是拦截器作用的是接口,AOP这类提供的操作的是方法

概念

切点(Printcut):这里的切点可以理解为切入的点,这里指的是一组规则,告诉程序对哪些方法来进行增强

连接点(Join Point):指的是满足上述切点规则的方法

通知(Advice): 对方法在前面/后面/周围加上一些处理(共性功能)

切面(Aspect): 切点 + 通知  (一类问题的解决方案)

SpringAOP

Spring 提供了一个通用的接口,可以帮助我们实现AOP的功能

比如对应用程序的监控,我们可以对每个接口之间加上计算实际运行时间

从计算的瓶颈解决问题

我们可以记录开始时间和节数时间然后作差,但是如果每个接口都一个一个写就没意思了

如果这里调用链非常长,我们还需要一个一个写,就太难受了呀

于是我们来使用AOP来解决问题

这里我们要先导入aop的依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

这里虽然文件是Aspect包中的,但是却是Spring实现的

简单的实现

@Aspect
@Component
@Slf4j
public class TimeRecordAspect {@Around("execution(* com.example.bookmanager.controller.*.*(..))")public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {//记录开始时间long startTime = System.currentTimeMillis();//目标方法Object proceed = joinPoint.proceed();//结束时间long endTime = System.currentTimeMillis();//打印日志log.info("方法执行时间"+(endTime-startTime)+"ms");return proceed;}
}

这样我们就可以在调用接口的时候打印其执行的时间了

以上的@Around注解表示就是环绕

在目标函数执行的前后都会执行

通知类型有如下几种

@Around: 环绕通知, 此注解标注的通知⽅法在⽬标⽅法前, 后都被执⾏
@Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏
@After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏
@AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, 有异常不会执⾏
@AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏
1.切点:这里的规则就是上面的Around里面的切点表达式
2.与切点表达式匹配的都是他描述的方法,也称之为连接点
3.通知 目标方法前后要做的操作
那么多包两层会怎么样呢????
比如先使用@Around再使用@Before和@After.....
这里就像一个栈一样,先进后出
注意:Around一定是要有返回值的,因为其有目标方法的执行
        Around的优先级高于其他

接口正常时

接口异常时

我们发现before和after在接口正常和异常的情况下都是会执行的

切点

注意需要一个空参方法
我们可以使用@Pointcut注解来代替大量重复的注解,后面的切点表达式就可以实现复用了
注意,如果切点是private的,在其他的类还是不能使用,声明为public并且使用全限定名即可
注:默认执行顺序为字典序,不可取,我们可以使用一个@Order来改变其优先级
数字越大优先级越低
下面我们来介绍一下切点表达式的内容
execution(* com.example.bookmanager.controller.*.*(..))
execution + 访问修饰符(可省略) + 返回类型 + 全类名 + 参数 + 异常(可省略)
这里的*表示任意单词可以替换返回类型,包名,类名方法等
..表示匹配多个任意符号
假设我们想对不同的类中的不同的方法来执行对应的连接
这个时候使用切点表达式就不能很好的完成问题了 我们就需要使用自定义注解对需要进行通知的方法进行修饰了

自定义注解

首先我们需要创建一个注解

我们需要声明他的作用域和生命周期

然后我们需要去做他的配置类/实现类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyAspect {}

我们暂时只实现一个Around功能

这里的@annotation就表示对什么注解生效

@Aspect
@Component
@Slf4j
public class MyAspectDemo {@Around("@annotation(com.example.springaop.config.MyAspect)")public Object doAround(ProceedingJoinPoint joinPoint) {log.info("do around before");Object o = null;try {o = joinPoint.proceed();} catch (Throwable e) {throw new RuntimeException(e);}log.info("do around after");return  o;}
}

后面我们可以用@MyAspect注解来修饰对应的方法

这样在调用的时候就可以实现其Around功能

SpringAOP的实现方式??

1.使用Aspect注解来实现

2.使用自定义注解来实现

3.基于XML文件使用api来实现  config:aop

4.基于代理来实现(比较久远)

代理模式(重点)

定义:

为其他对象提供⼀种代理以控制对这个对象的访问. 它的作⽤就是通过提供⼀个代理类, 让我们在调⽤⽬标⽅法的时候, 不再是直接对⽬标⽅法进⾏调⽤, ⽽是通过代理类间接调⽤.
代理前
代理后
生活中的代理也有很多
比如中介,经纪人,经销商,秘书........
主要角色:
Subject:业务接口类
RealSubject:业务实现类 具体的业务执行,也可以是被代理对象
Proxy:代理类,为RealSubject的代理
适用场景:
1.无法直接调用目标对象
2.目标对象给我们提供的功能不够,我们希望对目标对象已提供的方法进行功能增强
代理模式分为静态代理和动态代理
我们以生活中的中介来举例
静态代理就是我选好想看的房源之后,房源已经绑定了对应的中介了
动态代理就是我们选好想看的房源之后,平台自动分配中介
这种提前分配就称之为静态代理
运行时分配的就是动态代理

静态代理代码实现

接口
public interface Subject {void rentHouse();
}

房东

public class RealHouseSubject implements  Subject{@Overridepublic void rentHouse() {System.out.println("我要卖房子");}
}

中介

public class HouseProxy implements  Subject{private RealHouseSubject target;public HouseProxy(RealHouseSubject target) {this.target = target;}@Overridepublic void rentHouse() {System.out.println("开始代理");target.rentHouse();System.out.println("结束代理");}
}

类似于适配器模式,这里也是持有了第三方的房东授权

缺点是代码都写死了

动态代理实现

//JDK代理
public class JDKInvocationHandler implements InvocationHandler {private RealHouseSubject target;public JDKInvocationHandler(RealHouseSubject target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("开始代理");Object o = method.invoke(target, args);System.out.println("结束代理");return o;}
}//main方法RealHouseSubject subject = new RealHouseSubject();Subject proxy= (Subject)Proxy.newProxyInstance(subject.getClass().getClassLoader(),new Class[]{Subject.class},new JDKInvocationHandler(subject));proxy.rentHouse();

这里有两种  CGLib和 JDK动态代理

这里就不做要求了

注意代理是基于反射实现的

主要记得这里的两个区别

JDK可以代理接口 不能代理类

CGLib可以代理接口也可以代理类

但是性能我不太确认,网上说两个性能谁高的都有,可能有硬件的影响  

博主可以后面测测看

注意Spring和SpringBoot在这里AOP的实现也是有差异的

代理工厂中有一个参数 proxyTargetClass  

默认Spring是false   默认实现接口使用JDK代理

SpringBoot从2.x之后设置为true     默认全部使用CGLib实现代理

proxyTargetClass⽬标对象代理⽅式
false实现了接⼝jdk代理
false未实现接⼝(只有实现类)cglib代理
true实现了接⼝cglib代理
true未实现接⼝(只有实现类)cglib代理

小总结

 AOP是⼀种思想,是对某⼀类事情的集中处理.Spring框架实现了AOP,称之为SpringAOP
2. SpringAOP常⻅实现⽅式有两种:

1.基于注解@Aspect来实现

2.基于⾃定义注解来实现,还有⼀些更原始的⽅式,⽐如基于代理,基于xml配置的⽅式,但⽬标⽐较少⻅
3. SpringAOP是基于动态代理实现的,有两种⽅式:

1.基本JDK动态代理实现

2.基于CGLIB动态代理
实现.运⾏时使⽤哪种⽅式与项⽬配置和代理的对象有关

这篇关于Spring基础 SpringAOP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/927127

相关文章

Java如何用乘号来重复字符串的功能

《Java如何用乘号来重复字符串的功能》:本文主要介绍Java使用乘号来重复字符串的功能,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java乘号来重复字符串的功能1、利用循环2、使用StringBuilder3、采用 Java 11 引入的String.rep

SpringBoot中HTTP连接池的配置与优化

《SpringBoot中HTTP连接池的配置与优化》这篇文章主要为大家详细介绍了SpringBoot中HTTP连接池的配置与优化的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、HTTP连接池的核心价值二、Spring Boot集成方案方案1:Apache HttpCl

Spring Boot项目打包和运行的操作方法

《SpringBoot项目打包和运行的操作方法》SpringBoot应用内嵌了Web服务器,所以基于SpringBoot开发的web应用也可以独立运行,无须部署到其他Web服务器中,下面以打包dem... 目录一、打包为JAR包并运行1.打包为可执行的 JAR 包2.运行 JAR 包二、打包为WAR包并运行

Java进行日期解析与格式化的实现代码

《Java进行日期解析与格式化的实现代码》使用Java搭配ApacheCommonsLang3和Natty库,可以实现灵活高效的日期解析与格式化,本文将通过相关示例为大家讲讲具体的实践操作,需要的可以... 目录一、背景二、依赖介绍1. Apache Commons Lang32. Natty三、核心实现代

Spring Boot 常用注解整理(最全收藏版)

《SpringBoot常用注解整理(最全收藏版)》本文系统整理了常用的Spring/SpringBoot注解,按照功能分类进行介绍,每个注解都会涵盖其含义、提供来源、应用场景以及代码示例,帮助开发... 目录Spring & Spring Boot 常用注解整理一、Spring Boot 核心注解二、Spr

SpringBoot实现接口数据加解密的三种实战方案

《SpringBoot实现接口数据加解密的三种实战方案》在金融支付、用户隐私信息传输等场景中,接口数据若以明文传输,极易被中间人攻击窃取,SpringBoot提供了多种优雅的加解密实现方案,本文将从原... 目录一、为什么需要接口数据加解密?二、核心加解密算法选择1. 对称加密(AES)2. 非对称加密(R

详解如何在SpringBoot控制器中处理用户数据

《详解如何在SpringBoot控制器中处理用户数据》在SpringBoot应用开发中,控制器(Controller)扮演着至关重要的角色,它负责接收用户请求、处理数据并返回响应,本文将深入浅出地讲解... 目录一、获取请求参数1.1 获取查询参数1.2 获取路径参数二、处理表单提交2.1 处理表单数据三、

java变量内存中存储的使用方式

《java变量内存中存储的使用方式》:本文主要介绍java变量内存中存储的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、变量的定义3、 变量的类型4、 变量的作用域5、 内存中的存储方式总结1、介绍在 Java 中,变量是用于存储程序中数据

如何合理管控Java语言的异常

《如何合理管控Java语言的异常》:本文主要介绍如何合理管控Java语言的异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、Thorwable类3、Error4、Exception类4.1、检查异常4.2、运行时异常5、处理方式5.1. 捕获异常

Spring Boot集成SLF4j从基础到高级实践(最新推荐)

《SpringBoot集成SLF4j从基础到高级实践(最新推荐)》SLF4j(SimpleLoggingFacadeforJava)是一个日志门面(Facade),不是具体的日志实现,这篇文章主要介... 目录一、日志框架概述与SLF4j简介1.1 为什么需要日志框架1.2 主流日志框架对比1.3 SLF4