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.hqwc.cn/news/314503.html

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

相关文章

Nginx 简介和入门 - part1

虽然作为1个后端程序员&#xff0c; 终究避不开这东西 安装Nginx 本人的测试服务器是debian &#xff0c; 安装过程跟ubuntu基本一样 sudo apt-get install nginx问题是 nginx 安装后 执行文件在/usr/sbin 而不是/usr/bin 所以正常下普通用户是无法使用的。 必须切换到root…

【C语言】Ubuntu 22上用GTK写GUI程序

一、GTK介绍 GTK (GIMP Toolkit) 是一个多平台的图形用户界面工具包。它最初是为图像处理程序 GIMP 开发的&#xff0c;后来演变成为许多操作系统上开发图形界面应用程序的通用库。GTK 是用C语言编写的&#xff0c;并且是自由和开源软件&#xff0c;遵循LGPL (GNU Lesser Gene…

python多环境管理工具——pyenv-win安装与使用教程

目录 pyenv-win简介 pyenv-win安装 配置环境变量 pyenv的基本命令 pyenv安装py环境 pyenv安装遇到问题 pycharm测试 pyenv-win简介 什么是pyenv-win&#xff1a; 是一个在windows系统上管理python版本的工具。它是pyenv的windows版本&#xff0c;旨在提供类似于unix/li…

BGP路由知识点

目录 1.BGP的工作原理&#xff1a; 2.BGP路由的一般格式&#xff1a; 3.三种不同的自治系统AS 4.BGP的路由选择 5.BGP的四种报文 BGP&#xff08;Border Gateway Protocol&#xff09;是一种用于自治系统&#xff08;AS&#xff09;之间的路由选择协议。它是互联网中最常用…

CorelDRAW是什么软件?coreldraw软件可以做什么?

CorelDRAW是什么软件&#xff1f; cdr是coreldraw graphics suite的简称&#xff0c;它是corel企业的平面设计软件&#xff0c;cdr软件是corel公司出品的矢量图形制作工具软件&#xff0c;主要用于矢量图及页面设计和图像编辑。这个图形工具给设计师提供了矢量动画、页面设计、…

JDBC->SpringJDBC->Mybatis封装JDBC

一、JDBC介绍 Java数据库连接&#xff0c;&#xff08;Java Database Connectivity&#xff0c;简称JDBC&#xff09;是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口&#xff0c;提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们…

全局异常和自定义异常处理

全局异常GlobalException.java&#xff0c;basePackages&#xff1a;controller层所在的包全路径 import com.guet.score_management_system.common.domian.AjaxResult; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bi…

计算机网络复习1

概论 文章目录 概论计算机网络的组成功能分类性能指标&#xff08;搞清楚每个时延的具体定义&#xff09;分层结构协议、接口和服务服务的分类ISO/OSITCP/IP两者的不同 计算机网络的组成 组成部分&#xff1a;硬件&#xff0c;软件和协议&#xff08;协议&#xff1a;传输数据…

基于Flutter构建小型新闻App

目录 1. 概述 1.1 功能概述 1.2 技术准备 1.3 源码地址 2. App首页 2.1 pubspec依赖 2.2 热门首页组件 2.2.1 DefaultTabController 2.2.2 Swiper 2.3 新闻API数据访问 2.4 热门首页效果图 3. 新闻分类 3.1 GestureDetector 3.2 新闻分类效果图 4. 收藏功能 4…

[蓝桥杯知识学习] 树链

DFS序 什么是DFS序 怎么求DFS序 进入操作&#xff0c;将有计数 出&#xff1a;可以理解为&#xff0c;没有孩子可以去了&#xff08;不能&#xff0c;向下行动&#xff1a;对应于程序里的入栈&#xff09;&#xff0c;所以回到父结点&#xff08;向上行动&#xff0c;对应于程…

虚幻UE 材质-PDO像素深度偏移量

2024年的第一天&#xff01;&#xff01;&#xff01;大家新年快乐&#xff01;&#xff01;&#xff01; 可能是长大了才知道 当你过得一般 你的亲朋好友对你真正态度只可能是没有表露出来的冷嘲热讽了 希望大家新的一年平安、幸福、 永远活力满满地追求自己所想做的、爱做的&…

Java---网络编程

文章目录 1. 网络编程概述2. InetAddress3. 端口和协议4. Java网络API5. URL6. URLConnection类 1. 网络编程概述 1. 计算机网络&#xff1a;是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统、网络管理软…