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 {
    
    @Override
    public void save(Person person) {
        log.info("save({})", person);

    }


    @Override
    public void update(Person person) {
        log.info("update({})", person);
    }

    @Override
    public void delete(int id) {
        log.info("delete:id={}", id);
    }

    @Override
    public List<Person> findAll() {
        log.info("findAll...");
        return Arrays.asList(new Person(1, "张益达"), new Person(2, "snake"));
    }

    @Override
    public List<Person> findByPage(int page, int size) {
        log.info("findByPage:page={},size={}", page, size);
        return Arrays.asList(new Person(1, "张益达"), new Person(2, "snake"));
    }

    @Override
    public 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 {

    @Autowired
    private PersonService personService;  //此处使用的是代理对象
    @Test
    public void test1() {
        personService.save(new Person());
    }

    @Test
    public void test2() {
        personService.update(new Person());
    }

    @Test
    public void test3() {
        System.out.println("personService的类型为:" + personService.getClass());
    }

    @Test
    public 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.mfbz.cn/a/286101.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

介绍混沌工程的主要作用是什么?

混沌工程是一种通过有序的混沌实验来提高系统稳健性和可靠性的实践。它不仅有助于发现问题&#xff0c;更能够促使团队建立起一种主动预防和处理问题的文化&#xff0c;从而为复杂系统的管理提供了全新的视角。本文将介绍混沌工程的主要作用&#xff1a; 发现系统脆弱性&#x…

中国社科院与新加坡新跃社科大联合培养博士——读博就业前景

现在国家高度重视高素质人才的培养&#xff0c;硕士和博士人数增多。很多人想要走学术道路&#xff0c;想要做自己喜欢领域的科研&#xff0c;不过也有很多人希望通过高学历获得更高的薪资待遇。根据一些数据表明我国每年的博士人数都在五万或六万左右。2018年的博士毕业生总人…

MidTool图文创作-GPT-4与DALL·E 3的结合

GPT-4与DALLE 3的结合 GPT-4是由OpenAI开发的最新一代语言预测模型&#xff0c;它在前代模型的基础上进行了大幅度的改进&#xff0c;不仅在文本生成的连贯性、准确性上有了显著提升&#xff0c;还在理解复杂语境和执行多步骤指令方面表现出了更高的能力。而DALLE 3则是一个创…

Mysql的主从复制和读写分离

一.主从复制 主从复制的作用&#xff1a; 读写分离 读写分离 具有高可用性 主从复制的工作过程&#xff1a; 主服务器master发生数据变化时&#xff0c;会记录到二进制日志中&#xff1b;从服务器slave会开启io线程请求二进制日志事件&#xff1b;主服务器会为每个io线程…

QT登录功能开发

登录功能 1选择无按钮的dialog 2登录函数 #include <QApplication> #include <QDialog> #include <QFormLayout> #include <QLineEdit> #include <QPushButton> #include <QMessageBox>class LoginDialog : public QDialog { public:Log…

SpringIOC之support模块ConversionServiceFactoryBean

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

three.js 多通道组合

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div><div style"border: 1px so…

PASCAL VOC数据集

官方页面&#xff1a; 官方网址&#xff1a; The PASCAL Visual Object Classes Challenge 2012 (VOC2012) (ox.ac.uk) 数据集下载&#xff1a; 类别&#xff1a; 数据集结构&#xff1a;voc2012 test数据集不公开 标注自己的目标检测数据集&#xff1a; Make Sensehttps:…

javafx

JavaFX JavaFX简介 JavaFX是一个用于创建富客户端应用程序的图形用户界面&#xff08;GUI&#xff09;框架。它是Java平台的一部分&#xff0c;从Java 8开始成为Java的标准库。 JavaFX提供了丰富的图形和多媒体功能&#xff0c;使开发人员能够创建具有吸引力和交互性的应用程…

【数据结构】堆的实现及TOP-K问题

文章目录 前言1. 堆的概念及结构2. 堆的实现2.1 堆向上调整算法2.2 堆向下调整算法2.3 堆的创建2.4 堆的删除2.5 堆的常用接口代码实现 3. 堆的应用TOP-K问题 前言 在正式讲堆之前&#xff0c;我们要先来讲一下二叉树的顺序结构&#xff1a; 普通的二叉树是不适合用数组来存储…

认识机器学习【woodwhales.cn】

为了更好的阅读体验&#xff0c;建议移步至笔者的博客阅读&#xff1a;认识机器学习 生活中的问题1&#xff1a;居民家庭生活用气价格 北京燃气小程序在线咨询&#xff0c;查询北京居民家庭生活用气价格 上图价格梯度&#xff0c;可以由文字转换成表格&#xff1a; 第一档用气…

【Matlab】LSTM长短期记忆神经网络时序预测算法(附代码)

资源下载&#xff1a; https://download.csdn.net/download/vvoennvv/88688439 一&#xff0c;概述 LSTM&#xff08;Long Short-Term Memory&#xff09;是一种常用的循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;结构&#xff0c;由于其对于…

UI自动化Selenium 显式和隐式等待Wait

一、问题现象 大家是否自动化执行过程中&#xff0c;出现脚本时而成功时而失败的情况&#xff1b;发现常见情况如下&#xff1a; 1、元素时而出现时而提示不存在&#xff0c;timeout 2、元素时而可以操作时而不能操作&#xff1b;提示&#xff1a;元素不可点击或不可操作 3…

Qdrant向量数据库

Qdrant 是专为扩展过滤支持而设计的向量相似度搜索引擎和向量数据库&#xff0c;这使得它适用于各种基于神经网络的语义匹配、图像搜索等应用。 Qdrant 使用 Rust &#x1f980; 编写&#xff0c;即使在高负载下也能快速、可靠地工作。 Qdrant架构 上图展示了 Qdrant 一些主要…

【排序】堆排序(C语言实现)

文章目录 前言1. 堆排序1.1 堆排序的思想1.2 堆排序的实现 2. 为什么向下调整而不是向上调整 前言 本章主要会讲堆排序的实现过程以及向上调整和向下调整的时间复杂度&#xff0c;在学习本章前&#xff0c;需要对堆、以及向上调整和向下调整有一个了解&#xff0c;如果不了解的…

【CISSP学习笔记】5. 安全架构和工程

该知识领域涉及如下考点&#xff0c;具体内容分布于如下各个子章节&#xff1a; 使用安全设计原理来研究、实施与管理工程过程理解安全模型的基本概念&#xff08;例如 Biba、Star Model、Bell-LaPadula 等模型&#xff09;基于系统安全要求选择控制措施理解信息系统 (IS) 的安…

Nginx多ip部署多站点

目录 1.修改网卡配置信息 2.修改主要配置文件nginx.conf 1.修改网卡配置信息 1)来到网卡配置文件存放目录下 cd /etc/sysconfig/network-scripts/ 2)对 ifcfg-ens33 文件进行配置修改前先进行备份 cp ifcfg-ens33 ifcfg-ens33.default 3)先修改成最小配置&#xff0c;使用 d…

QT音频编程实战项目(二)

接上一篇 我们在实现完槽函数的定义后&#xff0c;我们应该将这些槽函数和对应的信号连接起来 接着将每个控件都对应转到槽进行实现&#xff1a; 这个是对打开文件呢个控件转到槽的具体操作&#xff1a; 首先通过QDir::homePath()获得用户的主目…

CCNP课程实验-05-Comprehensive_Experiment

目录 实验条件网络拓朴 基础配置实现IGP需求&#xff1a;1. 根据拓扑所示&#xff0c;配置OSPF和EIGRP2. 在R3上增加一个网段&#xff1a;33.33.33.0/24 (用Loopback 1模拟) 宣告进EIGRP&#xff0c;并在R3上将EIGRP重分布进OSPF。要求重分布进OSPF后的路由Tag值设置为666&…

java工作流详解

什么是工作流&#xff1f; 工作流&#xff1a;两个或两个以上的人&#xff0c;为了共同的目标&#xff0c;连续的以串行或并行的方式去完成某一业务。 业务&#xff1a;工作流所指业务涵盖了与经营相关的活动。 串行或并行&#xff1a;业务中的步骤也许以一步接着一步的方式…