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.rhkb.cn/news/228822.html

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

相关文章

2024校招测试工程师笔试——经典错题记录和解析

大家好&#xff0c;这篇文章记录几个测开方向经典的例题&#xff0c;并给出相应解析&#xff0c;欢迎给出你的看法 下列关于软件性能测试的说法中&#xff0c;正确的是&#xff1a;&#xff08; &#xff09; A 性能测试的目的不是为了发现软件缺陷 B 压力测试与负载测试的目的…

【并发设计模式】聊聊Thread-Per-Message与Worker-Thread模式

在并发编程中&#xff0c;核心就是同步、互斥、分工。 同步是多个线程之间按照一定的顺序进行执行&#xff0c;比如A执行完&#xff0c;B在执行。而互斥是多个线程之间对于共享资源的互斥。两个侧重点不一样&#xff0c;同步关注的是执行顺序&#xff0c;互斥关注的是资源的排…

CGAL的空间排序

1、介绍 许多在CGAL中实现的几何算法都是增量的&#xff0c;因此它们的速度取决于插入顺序。此软件包提供了排序算法&#xff0c;可以大大提高此类算法的运行时间。 其基本原理是沿着空间填充曲线对对象进行排序&#xff0c;这样在插入顺序上&#xff0c;几何上接近的两个对象将…

.net8 AOT编绎-跨平台调用C#类库的新方法-函数导出

VB.NET AOT无法编绎DLL,微软的无能&#xff0c;正是你的机会 .net8 AOT编绎-跨平台调用C#类库的新方法-函数导出 1&#xff0c;C#命令行创建工程&#xff1a;dotnet new classlib -o CSharpDllExport 2&#xff0c;编写一个静态方法&#xff0c;并且为它打上UnmanagedCallersO…

Linux - 设置虚拟机和主机IP在同一网段(桥接)

1.查看主机ip地址等相关信息。 ipconfig -all 2.设置虚拟网络编辑器 打开虚拟网络编辑器 设置虚拟网络编辑器&#xff0c;设置为桥接模式。&#xff08;记得以管理员方式打开VMware&#xff09;。 3.修改虚拟机网卡文件 查看虚拟机ip,我们的目标是将其修改为与主机同一网段…

postman使用-04响应

文章目录 响应响应界面说明Pretty&#xff1a;格式化显示&#xff0c;以便查看Raw&#xff1a;不进行任何处理&#xff0c;显示响应数据的原始格式Preview&#xff1a;预览响应体&#xff0c;会自动换行&#xff0c;不会格式化&#xff08;有时候是数据&#xff0c;有时候是页面…

什么是缓存、为什么要用缓存、缓存分类、缓存测试、缓存更新、缓存设计考虑点、缓存测试点

一、缓存 缓存是一种将数据存储在高速缓存中的技术&#xff0c;它可以提高应用程序的性能和响应速度。 二、 为什么要用缓存 1. 高性能(主要目的) 查询耗时&#xff0c;但变化少&#xff0c;又有很多读请求情况下&#xff0c;可以将查询结果放到缓存中。减少对数据库的压力&…

Java位运算及移位运算

java中能表示整数数据类型的有byte、short、char、int、long&#xff0c;在计算机中占用的空间使用字节描述&#xff0c;1个字节使用8位二进制表示。 数据类型字节数二进制位数表示范围默认值byte18-27 – 27-10char2160 – 216-1\u0000 (代表字符为空 转成int就是0)short216-…

Word 将页面方向更改为横向或纵向

文章目录 更改整个文档的方向更改部分页面的方向方法1&#xff1a;方法2&#xff1a; 参考链接 更改整个文档的方向 选择“布局”>“方向”&#xff0c;选择“纵向”或“横向”。 更改部分页面的方向 需要达到下图结果&#xff1a; 方法1&#xff1a; 选:中你要在横向页面…

帆软报表中定时调度中的最后一步如何增加新的处理方式

在定时调度中,到调度执行完之后,我们可能想做一些别的事情,当自带的处理方式不满足时,可以自定义自己的处理方式。 产品的处理方式一共有如下这些类型: 我们想在除了上面的处理方式之外增加自己的处理方式应该怎么做呢? 先看下效果: 涉及到两方面的改造,前端与后端。…

C++日期类的实现

前言&#xff1a;在类和对象比较熟悉的情况下&#xff0c;我们我们就可以开始制作日期表了&#xff0c;实现日期类所包含的知识点有构造函数&#xff0c;析构函数&#xff0c;函数重载&#xff0c;拷贝构造函数&#xff0c;运算符重载&#xff0c;const成员函数 1.日期类的加减…

【产品设计】信息建设三驾马车:PLM系统拆解

本篇文章将介绍PLM的基础信息、发展及模块功能等内容&#xff0c;让大家对PLM有一个全面、完整地了解&#xff0c;方便在后期的工作中能快速地使用其解决方案&#xff0c;希望本篇文章能对你有所帮助。 PLM系统主要实现产品模块业务&#xff0c;既包含产品的创意设计、样品打样…

【源码】-MyBatis-如何系统地看源码

写在前面 前段时间做过一个项目&#xff0c;期间用到了动态数据源dynamic-datasource&#xff0c;经历了dbcp2的数据库连接池没有生效到排查定位、MyBatis多种数据库产品兼容、手写MyBatis拦截器等事情。 花费了好久&#xff0c;一直在打磨这篇文章&#xff08;不知道花费这么长…

thinkphp6.0升级到8.0

目录 一&#xff1a;升级过程 二&#xff1a;报错处理 最近写的项目需要使用thinkphp8.0&#xff0c;之前的老项目需要从php6.0升级到8.0&#xff0c;特此记录下升级过程。 一&#xff1a;升级过程 查看版本&#xff1a; php think version,我目前的版本是6.1.4 生成thin…

WPF+Halcon 培训项目实战(8-9):WPF+Halcon初次开发

文章目录 前言相关链接项目专栏运行环境匹配图片WPF Halcon组件HSmartWindowControlWPF绑定读取图片运行代码运行结果 抖动问题解决运行结果 绘制矩形绘制图像会消失 绘制对象绑定事件拖动事件 前言 为了更好地去学习WPFHalcon&#xff0c;我决定去报个班学一下。原因无非是想…

C/C++面向对象(OOP)编程-回调函数详解(回调函数、C/C++异步回调、函数指针)

本文主要介绍回调函数的使用&#xff0c;包括函数指针、异步回调编程、主要通过详细的例子来指导在异步编程和事件编程中如何使用回调函数来实现。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;C/C精进之路 &…

Spark魔力:招聘网站数据深度分析系统

Spark魔力&#xff1a;招聘网站数据深度分析系统 简介数据集技术栈功能特点创新点 简介 在本文中&#xff0c;我们将介绍一款基于Spark的招聘网站数据分析系统&#xff0c;该系统使用爬取的前程无忧招聘数据。通过结合Flask、Pandas、PySpark、以及MySQL等技术&#xff0c;实现…

【汇编笔记】初识汇编-内存读写

汇编语言的由来&#xff1a; CPU是计算机的核心&#xff0c;由于计算机只认识二进制&#xff0c;所以CPU执行的指令是二进制。 我们要想让CPU工作&#xff0c;就得给他提供它认识的指令&#xff0c;这一系列的指令的集合&#xff0c;称之为指令集。 指令集&#xff1a; 不同的体…

单片机键盘程序设计举例

1、键盘与的连接 图3键盘连接 图4单片机与键盘接口图 2、通过1/0口连接。将每个按钮的一端接到单片机的I/O口&#xff0c;另一端接地&#xff0c;这是最简单的办法&#xff0c;如图3所示是实验板上按钮的接法&#xff0c;四个按钮分别接到P3.2 、P3.3、P3.4和P3.5。对于这种键…

css 设置鼠标覆盖显示菜单

鼠标覆盖到“全部分类”效果如下 鼠标放到“精品推荐”效果如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"&g…