Spring04

一、AOP的概念

        AOP 为 (Aspect Oriented Programming) 的缩写,意为:面向切面编程,底层是使用动态代理的技术实现对目标方法的增强控制访问等功能。

        其中AOP中有几个重要的概念:

        1、通知:增强的逻辑,或者后期要加入的代码。

        2、目标:被增强的对象(真实类)。

        3、代理:通过SpringAOP生成的代理对象。

        4、切点:目标对象中需要被增强的方法。

        5、连接点:目标对象的方法。

        6、切面:通知+切点。

       

二、切点表达式

2.1、切点表达式的概念

        切点表达式是使用execution关键字定义的,用来指定什么类中的什么方法需要被增强。

       

2.2、切点表达式的格式 

 切点表达式的完整格式为:

execution(@注解? 访问修饰符? 返回值 包名.类名?.方法名(方法参数) throws 异常?)

 一般采用:

 其中

1、* 可以通配任意返回值类型、包名、类名、方法名、或任意类型的一个参数

2、.. 可以通配任意层级的包、或任意类型、任意个数的参数

3、注解可省略(没啥用)

4、访问修饰符可省略(没啥用,仅能匹配 public、protected、包级,private 不能增强)

5、包名.类名可省略

6、thorws 异常可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

例如:

execution(* com.itboy.service.PersonService.*(..))

        将com.itboy.service包下的PersonService类中的所有方法进行增强,其中方法的返回值为任意的,方法参数的类型和数量也是任意的

1、*表示返回值类型为任意的

 2、com.itboy.service.PersonService表示将com.itboy.service包下的PersonService这个类中的方法进行增强。

3、PersonService后面的.*表示对PersonService中的所有方法进行增强。

4、(..)表示方法参数的数量和类型任意,所以方法参数可以为0或者多个。

5、方法中参数的含义如下:

        

三、SpringAop入门 

3.1、定义一个接口

         PersonService

package com.itboy.service;import com.itboy.pojo.Person;import java.util.List;public interface PersonService {void save(Person person);void update(Person person);void delete(int id);List<Person> findAll();List<Person> findByPage(int page, int size);Person findById(int id);
}

3.2、定义一个目标对象(真实类)

        PersonServiceImpl

package com.itboy.service.impl;import com.itboy.pojo.Person;
import com.itboy.service.PersonService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import java.util.Arrays;
import java.util.List;@Service
@Slf4j
public class PersonServiceImpl implements PersonService {@Overridepublic void save(Person person) {log.info("save({})", person);}@Overridepublic void update(Person person) {log.info("update({})", person);}@Overridepublic void delete(int id) {log.info("delete:id={}", id);}@Overridepublic List<Person> findAll() {log.info("findAll...");return Arrays.asList(new Person(1, "张益达"), new Person(2, "snake"));}@Overridepublic List<Person> findByPage(int page, int size) {log.info("findByPage:page={},size={}", page, size);return Arrays.asList(new Person(1, "张益达"), new Person(2, "snake"));}@Overridepublic Person findById(int id) {log.info("findById:id={}", id);return new Person(1, "张益达");}
}

3.3、定义一个切面类

        Aspect1

package com.itboy.aspect;import com.itboy.anno.MyAnno;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;/*** 切面类:定义增强逻辑,也就是通知+切点*/
@Component  //让Spring管理Bean对象,创建Bean对象。
@Aspect //表示该类是一个切面类,将来需要定义增强逻辑
@Slf4j
public class Aspect1 {/*** 定通知:使用@Around注解表示该方法是一个通知方法,将来在通知方法中写通知内容* 定切点:通过@Around注解中的表达式找到要增强的方法* 通配符:*      * :表示任意返回值类型、任意方法*      .. :表示任意多个参数占位符* @param pjp 封装了要增强的方法,用来执行目标对象方法* @return 哪里调用代理对象的方法就返回到哪里*/@Around("execution(void com.itboy.service.impl.PersonServiceImpl.save(..))")  //可以public Object around(ProceedingJoinPoint pjp) throws Throwable {//System.out.println(("anno的值为"+anno.value()));//log.info("Aspect1...");//定义增强逻辑//1 记录开始时间long start = System.nanoTime();System.out.println("Aspect1提供的增强方法:开始时间为"+start+".....");//2 调用目标对象方法,获取返回值进行返回Object result=pjp.proceed();//3 记录结束时间,计算耗时并打印long end = System.nanoTime();System.out.println("Aspect1提供的增强方法:结束时间为"+end+".....");System.out.println("共耗时:"+(end-start));return result;}
}

3.4、定义一个测试类

Spring01ApplicationTests
package com.itboy;import com.itboy.pojo.Person;
import com.itboy.service.PersonService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@SpringBootTest
@Slf4j
class Spring01ApplicationTests {@Autowiredprivate PersonService personService;  //此处使用的是代理对象@Testpublic void test1() {personService.save(new Person());}@Testpublic void test2() {personService.update(new Person());}@Testpublic void test3() {System.out.println("personService的类型为:" + personService.getClass());}@Testpublic void test4() {personService.delete(1);}}

3.5、得到最终的结果

分别执行test1test2,得到最终的结果。

test1的执行结果:

test2的执行结果:

发现只有save方法被增强了,update方法没有被增强。 

因为切面表达式为:

execution(void com.itboy.service.impl.PersonServiceImpl.save(..))

        表示对com.itboy.service.impl包下的PersonServiceImpl的save方法进行增强,其中方法的返回值类型为void,方法参数类型和个数是任意的。

3.6、分析

        对切面类Aspect1进行分析,切面类要放入Spring容器中,所以Aspect1类上方要加上@Component注解,并且要加上@Aspect注解,表示该类是一个切面类,将来需要定义增强逻辑

        切面表达式由@Around 注解包裹,使用@Around注解表示该方法是一个通知方法,将来在通知方法中写增强逻辑

Object result=pjp.proceed();表示调用目标对象的方法,并获取返回值。并可以在该方法的前后

 写增强逻辑 

3.7、整个调用的执行流程 

        我们在Spring01ApplicationTests这个类上方加了@SpringBootTest注解,那么当我们启动test1时,Spring容器也会启动,因为@SpringBootTest注解表示该测试类是基于spring容器实现的。然后Spring就会扫描Aspect1类上的@Component注解和@Aspect注解,再通过切面表达式中指定的类,就会创建PersonServiceImpl对象的代理对象并放入Spring的容器中。

        此时我们依赖注入的不是目标对象PersonServiceImpl,而是它的代理对象。

        我们执行test3,也能看到它的类型为代理对象,并且是通过cglib实现的

        生成的代理对象是PersonServiceImpl目标对象的子类对象,继承了PersonServiceImpl中的所有方法,调用的是save方法,就会去Aspect1中的切面表达式进行匹配,如果能匹配上,就会进入@Around注解标注的方法中。

         

图形总结:

3.8、调用流程总结

 1、生成目标对象的代理对象并放入Spring的容器中(生成的代理对象是目标对象的子类对象)

2、

@Autowired

private PersonService personService;

这里的personService已经被代理对象所替换

3、

        personService.save(new Person());就会去切面表达式中进入匹配,如果能匹配上就进入到切面表达式定义的方法中,如果不能匹配上,就会进入到目标对象的方法中

四、SpringAop进阶

4.1、提取切面表达式

        可以使用pointcut注解对切点表达式进行抽取,增强它的复用性:类名.方法名即可。这里的方法的方法体为空,只是为了承接pointcut注解的。

 4.2、代码

定义一个提取类:MyPointcut

package com.itboy.aspect;import org.aspectj.lang.annotation.Pointcut;/*** @Author zhouxiangyang* @Date 2022/5/12 14:47*/
public class MyPointcut {/*** @Pointcut注解:定义切点表达式* 方法名就是切点表达式的名称,将来通过类名.方法名()获取切点表达式*/@Pointcut("execution(* com.itboy.service.impl.PersonServiceImpl.save*(..))")public void pt1(){}@Pointcut("execution(String com.itboy.service.impl.PersonServiceImpl.update*(..))")public void pt2(){}@Pointcut("execution(* com.itboy.service.impl.PersonServiceImpl.delete*(..))")public void pt3(){}}

定义一个切面类:Aspect1

package com.itboy.aspect;import com.itboy.anno.MyAnno;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;/*** 切面类:定义增强逻辑,也就是通知+切点*/
@Component  //让Spring管理Bean对象,创建Bean对象。
@Aspect //表示该类是一个切面类,将来需要定义增强逻辑
@Slf4j
public class Aspect1 {/*** 定通知:使用@Around注解表示该方法是一个通知方法,将来在通知方法中写通知内容* 定切点:通过@Around注解中的表达式找到要增强的方法* 通配符:*      * :表示任意返回值类型、任意方法*      .. :表示任意多个参数占位符* @param pjp 封装了要增强的方法,用来执行目标对象方法* @return 哪里调用代理对象的方法就返回到哪里*/@Around("MyPointcut.pt1()|| MyPointcut.pt2() || MyPointcut.pt3()")public Object around(ProceedingJoinPoint pjp) throws Throwable {//System.out.println(("anno的值为"+anno.value()));//log.info("Aspect1...");//定义增强逻辑//1 记录开始时间long start = System.nanoTime();System.out.println("Aspect1提供的增强方法:开始时间为"+start+".....");//2 调用目标对象方法,获取返回值进行返回Object result=pjp.proceed();//3 记录结束时间,计算耗时并打印long end = System.nanoTime();System.out.println("Aspect1提供的增强方法:结束时间为"+end+".....");System.out.println("共耗时:"+(end-start));return result;}
}

MyPointcut.pt1()|| MyPointcut.pt2() || MyPointcut.pt3()

        只要其中一个表达式满足要求即可,并且只会增强满足要求的,例如Update方法不满足切面表达式,所以只有save方法和delete方法会被增强。

4.3、通知类型

之前介绍的是环绕通知,它是功能最为强大的通知,但除此以外还有四种通知类型

@Before - 此注解标注的通知方法在目标方法前被执行

@AfterReturning - 此注解标注的通知方法在目标方法后被执行,有异常不会执行

@AfterThrowing - 此注解标注的通知方法发生异常后执行

@After - 此注解标注的通知方法在目标方法后被执行,无论是否有异常

@Before注解为例:此注解标注的通知方法在目标方法前被执行。

定义一个切面类:

        Aspect2

        

package com.itboy.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Arrays;/*** @Author zhouxiangyang* @Date 2022/5/12 15:01*/
@Component
@Aspect
@Slf4j
public class Aspect2 {//1 前置通知:被@Before注解标注的方法为前置通知方法,在目标方法执行之前执行@Before("execution(* com.itboy.service.impl.PersonServiceImpl.*(..))")public void before(JoinPoint jp){System.out.println("前置通知:before...");//1 获取方法签名 返回值类型 包名....类名.方法名(参数类型)MethodSignature signature = (MethodSignature) jp.getSignature();System.out.println("signature = " + signature);//2 获取完整的方法信息 修饰符 返回值类型 包名....类名.方法名(参数类型)Method method = signature.getMethod();System.out.println("method = " + method);//3 获取返回值类型Class returnType = signature.getReturnType();System.out.println("returnType = " + returnType);//4 获取参数值Object[] args = jp.getArgs();System.out.println("args = " + Arrays.toString(args));}}

再去Spring01ApplicationTests类中执行test1方法获得结果

4.4、SpringAop中动态代理的方式

        Spring中既支持jdk动态代理,也支持cglib动态代理,默认采用的是cglib动态代理。

        Springboot 默认配置 spring.aop.proxy-target-class=true 此时无论目标是否实现接口,都是采用cglib 技术,生成的都是子类代理

        

        如果设置了 spring.aop.proxy-target-class=false,那么又分两种情况 如果目标实现了接口,Spring 会采用jdk 动态代理技术生成代理 如果目标没有实现接口,Spring 会采用cglib 技术生成代理

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

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

相关文章

【js】js解析Token:

一、效果&#xff1a; 二、实现&#xff1a; export function getTokenObject(token) {//通过split()方法将token转为字符串数组,数组中的第二个字符进行解析return token ? JSON.parse(decodeURIComponent(escape(window.atob(token.split(".")[1].replace(/-/g &…

docker Mysql-udf-http

1.Mysql-udf-http镜像已上传到dockerhub中 docker pull heidaodageshiwo/mysql-udf-http:v1 2.启动镜像(默认密码root1234) docker run -tid -p 3306:3306 --namemysql-udf-http --privilegedtrue heidaodageshiwo/mysql-udf-http:v1 3.命令 [rootlocalhost ~]# docker im…

odoo17 | 开发环境设置

前言 开始odoo17开发之前&#xff0c;请先掌握python的基本语法和工具包的使用&#xff0c;以及postgres数据库的安装&#xff0c;和简单的sql使用。以及一些前端的html、css、javascript等前端知识&#xff0c;以及xml、json等数据传输的使用。 本教程同时适用于odoo15-17 …

go语言语法基础

文章目录 前言一、输入和输出常用的字符串格式化符号 二、注释三、Go常用基本语言数据类型数字类型布尔类型字符类型变量与常量数组和切片数组切片 map类型创建map增删改查特别提醒 指针 四、运算符五、条件判断语句if系列switch六、循环语句for循环标准写法死循环while循环do …

Primavera Unifier 项目控制延伸:Phase Gate理论:2/3

阶段Gate的具体内容&#xff1a; 阶段0 根据公司需要和资源现状&#xff0c;决定开展哪些项目。在这个阶段&#xff0c;公司一般需要开展一些脑力风暴或者团队集思广益的活动以获得足够多的点子。一旦团队决定采用某个想法&#xff0c;必须从各个维度去完善它&#xff0c;并使…

【FileZilla的安装与使用(主动与被动模式详解,以及如何利用FileZilla搭建FTP服务器并且进行访问)】

目录 一、FileZilla介绍 1.1 简介 1.2 重要信息和功能 二、FileZilla的安装与使用 2.1 FileZilla服务端安装与配置 2.1.1 安装步骤 2.1.2 新建组 2.1.3 新建用户 2.1.4 新建目录 2.1.5 权限分配 &#xff08;1&#xff09;用户Milk权限分配 &#xff08;2&#xff…

使用 Hyper-V 创建虚拟机

使用 Hyper-V 创建虚拟机 官网教程修改存储目录Hyper-V管理器创建虚拟机启动虚拟机Win10安装教程Press any key to boot from CD or DVD...... 如何使用Windows自带的虚拟机工具来创建虚拟机&#xff0c; 快速创建虚拟机进行学习探讨&#xff0c;如果有环境问题可以立即创建一个…

元旦小礼品:想去面试的时候如何快速打造自己的面试亮点

新年快乐&#xff01; 对于毕业生&#xff0c;或者工作时间不太长的同学来说&#xff0c;面试实习之前写简历是一件很痛苦的事情&#xff0c;特别是简历上没什么亮点&#xff0c;总感觉很心虚。这时候就会发现很多人凡是知道的&#xff0c;叫得上名字的统统写上去。可惜即使通…

VMware之FTP的简介以及搭建使用计算机端口的介绍

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《产品经理如何画泳道图&流程图》 ⛺️ 越努力 &#xff0c;越幸运 目录 一、FTP介绍 1、什么是FTP&#xff1a; 2、FTP适用于以下情况和应用场景&#xff1a; 3、winServer2012搭…

软件测试/测试开发丨Linux 三剑客与管道使用

1、 程序运行环境输入与输出 标准输入 0 read a;echo $a标准输出 1 echo ceshiren.com错误输出 ls not_exist_dir 2、 管道重定向 管道与管道之间可以重定向管道与文件之间可以重定向 echo 11 > /tmp/1 read var </tmp/1错误输出&#xff1a; ls not_exist_dir > /…

ubuntu:beyond compare 4 This license key has been revoked 解决办法

https://www.cnblogs.com/zhibei/p/12095431.html 错误如图所示&#xff1a; 解决办法&#xff1a; &#xff08;1&#xff09;先用find命令找到bcompare所在位置&#xff1a;sudo find /home/ -name *bcompare &#xff08;2&#xff09;进入 /home/whf/.config,删除/bco…

【瞎折腾/3D】无父物体下物体的旋转与移动

目录 说在前面移动World SpaceLocal Space 旋转World SpaceLocal Space 代码 说在前面 测试环境&#xff1a;Microsoft Edge 120.0.2210.91three.js版本&#xff1a;0.160.0其他&#xff1a;本篇文章中只探讨了无父对象下的移动与旋转&#xff0c;有父对象的情况将在下篇文章中…

springboot 接收appsflyer 参数

1、官网登陆&#xff1a;hq1.appsflyer.com a、配置接收请求的地址和需要的事件 配置详情 2、Controller配置接收 RequestMapping(value "/req", method POST)ResponseBodypublic ResData req(RequestBody Map<String, String> map) {String jsonObject J…

一文搞懂什么是缓存穿透、缓存雪崩、缓存击穿三个概念,以及解决方案

先理解概念&#xff1a;【注&#xff1a;我们这里说的是分布式、高并发环境】 一、缓存穿透是什么&#xff1f; 缓存穿透是指&#xff1a;请求【可以有很多】的数据在缓存、关系型数据库中都不存在&#xff0c;每次来查询都会查询到关系型数据库中。 解决方案&#xff1a; 1、将…

CUMT--Java复习--核心类

目录 一、装箱与拆箱 二、“”与equals 三、字符串类 1、String、StringBuffer、StringBuilder的区别 2、String类 3、StringBuffer类 4、StringBuilder类 四、类与类之间关系 一、装箱与拆箱 基本类型与对应封装类之间能够自动进行转换&#xff0c;本质就是Java的自…

强烈推荐 25个 前端开源中后台管理系统

作为程序员&#xff0c;构建一套个人专属的后台管理系统非常重要。这不仅是为了打造自己独有的开发生态&#xff0c;更是因为我们正处于个人开发和AI模型泛滥的时代。利用AI增强自己的系统变得尤为关键。然而&#xff0c;在UI界面设计方面&#xff0c;我们可能需要参考开源项目…

mysql原理--Explain详解

1.概述 一条查询语句在经过 MySQL 查询优化器的各种基于成本和规则的优化会后生成一个所谓的 执行计划 &#xff0c;这个执行计划展示了接下来具体执行查询的方式&#xff0c;比如多表连接的顺序是什么&#xff0c;对于每个表采用什么访问方法来具体执行查询等等。设计 MySQL 的…

实时交通标志检测和分类(代码)

交通标志检测和分类技术是一种基于计算机视觉和深度学习的先进技术&#xff0c;能够识别道路上的各种交通标志&#xff0c;并对其进行分类和识别。这项技术在智能交通系统、自动驾驶汽车和交通安全管理领域具有重要的应用前景。下面我将结合实时交通标志检测和分类的重要性、技…

第9章 继承和派生习题(详解)

一、选择题 1&#xff0e;下列表示引用的方法中&#xff0c; &#xff08;&#xff09; 是正确的。已知&#xff1a;int m10&#xff1a; A&#xff0e;int &xm&#xff1b; B&#xff0e;int &y10&#xff1b; C&#xff0e;int &z&#xff1b; D&#xff0e;fl…

Java中利用Redis,ZooKeeper,数据库等实现分布式锁(遥遥领先)

1. 分布式锁 1.1 什么是分布式锁 在我们进行单机应用开发涉及并发同步的时候&#xff0c;我们往往采用synchronized或者ReentrantLock的方式来解决多线程间的代码同步问题。但是当我们的应用是在分布式集群工作的情况下&#xff0c;那么就需要一种更加高级的锁机制&#xff0…