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

相关文章

Python基础文件操作方法超详细讲解(详解版)

《Python基础文件操作方法超详细讲解(详解版)》文件就是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位,文件的核心操作就是读和写,:本文主要介绍Python基础文件操作方法超详细讲解的相... 目录一、文件操作1. 文件打开与关闭1.1 打开文件1.2 关闭文件2. 访问模式及说明二、文件读写1.

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain