spring系列 SpringAOP

news/2024/11/20 2:29:24/

概念

原来的方法写法

public void save(){        

        Long startTime = System.currentTimeMillis();        

        System.out.println(“book dao save ...”);    

        Long endTime = System.currentTimeMillis();        

        Long totalTime = endTime-startTime;        

        System.out.println("方法耗时:" + totalTime + "ms");    

}

通知

把多个方法耦合的部分抽取出来就叫做通知,设置通知的类就叫做通知类:

public void method(){        

        Long startTime = System.currentTimeMillis();        

        //调用原始操作     

        Long endTime = System.currentTimeMillis();        

        Long totalTime = endTime-startTime;        

        System.out.println("方法耗时:" + totalTime + "ms");    

}

@Component
@Aspect
public class MyAdvice {


    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    @Pointcut("execution(int com.itheima.dao.BookDao.select())")
    private void pt2(){}

    //@Before:前置通知,在原始方法运行之前执行
//    @Before("pt()")
    public void before() {
        System.out.println("before advice ...");
    }

    //@After:后置通知,在原始方法运行之后执行,原方法就算抛异常也会执行
//    @After("pt2()")
    public void after() {
        System.out.println("after advice ...");
    }

    //@Around:环绕通知,在原始方法运行的前后执行

    //借助ProceedingJoinPoint pjp可以调用原来的方法,从而达到对原来方法返回值的处理,如果原来方法有返回值而这里没有,虽然也会执行,但是如果我们想通过代码获取返回值的话就会报错。

        //这里如果ProceedingJoinPoint pjp没有,那就会跳过原始方法执行相应通知,可以用来做校验。

        //这里的规范是用Object,这样即使是个null也能接。

        //调用原始方法的时候会提示我们要抛异常throws Throwable
//    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        //获取原方法的参数,实际值

        //Object[] args = pjp.getArgs();    

        //Object ret = pjp.proceed(args);//在拿到原方法参数值后可以加一些校验逻辑,不需要操作的话不用传参数也可以

        //System.out.println(Arrays.toString(args));

        //获取执行的签名对象
        Signature signature = pjp.getSignature();
        String className = signature.getDeclaringTypeName();        //类路径和类名
        String methodName = signature.getName();        //方法名


        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");

        System.out.println("类方法:"+ className+"."+methodName+"执行");
        return ret;
    }

//    @Around("pt2()")
    public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Integer ret = (Integer) pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

       //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中没有抛异常现象才会执行,执行时机在@after之前

        //如果要操作原始方法的返回值。注解值要改成这样:(value = "pt()",returning = "ret"),然后下面方法写一个形参,形参名要和上面的去接returning 值一样 ,具体类型可以自己根据情况选择。当ProceedingJoinPoint或JoinPoint和我们的返回参数一起被定义的为形参的时候,JoinPoint必须在最前面,不然会报illegalArgumentException。
//    @AfterReturning("pt2()")
    public void afterReturning() {
        System.out.println("afterReturning advice ...");
    }

    //@AfterThrowing:抛出异常后通知,在原始方法执行过程中如果出现异常后就会运行

    //要获取异常对象的话,(value = "pt()",throwing = "t"),下面的参数改成Throwable t
    @AfterThrowing("pt2()")
    public void afterThrowing() {
        System.out.println("afterThrowing advice ...");
    }
}

连接点

这种实际的方法就是一个个的连接点:

public void update(){    

        System.out.println("book dao update ...");

}

public void delete(){    

        System.out.println("book dao delete ...");

}

public void select(){    

        System.out.println("book dao select ...");

}

切入点

指定要具体操作连接点的就是切入点。

代码中指要进行增强的方法。

切入点表达式

切入点表达式标准格式:

动作关键字(访问修饰符  返回值  包名.类/接口名.方法名(参数)异常名)

动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点 访问

修饰符:public,private等,可以省略

异常名:方法定义中抛出指定异常,可以省略

下面两种都行:

execution(void com.xxz.dao.BookDao.update())

execution(void com.xxz.dao.impl.BookDaoImpl.update())

表达式使用通配符:

* :指代文件目录的一层,或一个元素,也可以用来匹配前后缀。

需要注意的是方法参数中要是写了*,那就是匹配的任意参数的,没参数不行

execution(public * com.xx.*.UserService.find*(*))

.. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

execution(public User com..UserService.findById(..))

+:用来匹配子类类型

execution(* *..*Service+.*(..))


切入点通常描述接口,而不描述实现类,因为实现类更可能会随需求改变,耦合度更小。

返回值类型对于增删改类使用精准类型匹配,而查询方法的返回值往往有多种数据结构的,对于查询类使用*通配快速描述。

包名书写使用..匹配时,效率过低,尽量不使用,常用*做单个包描述匹配,或精准匹配。

接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名。

方法名书写以动词进行精准匹配,具体名词可以采用*匹配,例如getById书写成getBy*,而selectAll可以直接书写成selectAll。

通常不使用异常作为匹配规则,异常有相应的处理手段。。

切面

就是将通知和切入点联系起来的部分。

案例

导包

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>

配置类:

@EnableAspectJAutoProxy,开启注解开发AOP功能

@Configuration
@ComponentScan("com.xxz")
@EnableAspectJAutoProxy
public class SpringConfig {
}

要被代理的方法save和update:

@Repository
public class BookDaoImpl implements BookDao {

    public void save() {
        System.out.println("book dao save ...");
    }

    public void update(){
        System.out.println("book dao update ...");
    }
}

通知类:

//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类
@Aspect
public class MyAdvice {
    //设置切入点,要求配置在方法上方,这个方法要求无参无返回值无逻辑
    @Pointcut("execution(void com.xxz.dao.BookDao.update())")
    private void pt(){}

    //设置在切入点pt()的前面运行当前操作(前置通知)
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

启动类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
//        bookDao.update();
        System.out.println(bookDao);
        System.out.println(bookDao.getClass());//当方法名不对应目标时,比如void com.xxz.dao.BookDao.update()写成void com.xxz.dao.BookDao.updateD(),对象还是自身。而经过代理的由于重写了toString方法会打出一个proxy对象地址。
    }
}


http://www.ppmy.cn/news/12571.html

相关文章

解决Vue3中使用setup如何定义组件的name属性

vue2的书写name: <template><div></div> </template><script> export default {name: "nameoption", }; </script><style lang"scss" scoped></style>解决&#xff1a;Vue3中使用setup如何定义组件的nam…

基于贝叶斯算法的邮件过滤管理系统的设计和实现(Vue+SpringBoot)

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

2个大厂 100亿级 超大流量 红包 架构方案

2个大厂 100亿级 超大流量 红包 架构方案 文章目录2个大厂 100亿级 超大流量 红包 架构方案100亿级 红包 应用 场景概述百亿级 微信红包技术架构架构**南北分布****拆红包入账异步化****发拆落地&#xff0c;其他操作双层cache**高并发**红包算法****柔性降级方案**360w QPS 10…

Java9的新特性模块化(Module)

一、 模块化是什么&#xff1f; Java 9引入了模块化系统&#xff0c;称为"Java Platform Module System"&#xff08;JPMS&#xff09; 这个系统允许将Java程序分成模块&#xff0c;每个模块都有自己的规范&#xff0c;可以明确地声明它依赖于哪些其他模块&#xff…

Python NumPy 数组索引

前言NumPy&#xff08;Numerical Python的缩写&#xff09;是一个开源的Python科学计算库。使用NumPy&#xff0c;就可以很自然地使用数组和矩阵。NumPy包含很多实用的数学函数&#xff0c;涵盖线性代数运算、傅里叶变换和随机数生成等功能。本文主要介绍Python NumPy 数组索引…

介绍Java中的常/变量.各种数据类型以及类型转换和提升的用法

本文简单描述了什么是常量和变量,介绍了Java各种数据类型:基本数据类型(四类八种,大小和范围)和引用数据类型(种类),简单介绍了包装类字符串类型,以及不同数据类型之间的常量和变量,数据类型之间的转换和提升… Java常/变量和数据类型一.什么是常量?二.什么是变量?三.数据类型…

TryHackMe-Minotaur‘s_Labyrinth

Minotaur’s Labyrinth 嗨&#xff0c;是我&#xff0c;代达罗斯&#xff0c;迷宫的创造者。我能够 实现一些后门&#xff0c;但牛头怪能够&#xff08;部分&#xff09;修复它们 &#xff08;这是一个秘密&#xff0c;所以不要告诉任何人&#xff09;。但是让我们回到你的任务…

电路方案分析(十六)高效备用电源设计方案

高效备用电源设计方案 备用电源&#xff1a; 备用电源是一种在主电源发生故障时为负载提供紧急电源的电气系统。适当的备用电源通过提供存储在备用电容器或电池中的能量&#xff0c;提供即时保护&#xff0c;防止主电源中断而产生故障。这种备份电源通常用于保护硬件&#xf…