Aop切面编程原理和Spring实现

news2024/9/22 5:29:11

Aop切面编程概念

在这里插入图片描述
AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现 日志处理,权限控制,性能检测,事务控制等
AOP实现的原理就是动态代理,在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理
在这里插入图片描述
为Dao层所有的add方法添加一个性能记录功能(抽象化横切面)

AOP中的术语辨析

1.连接点 Joint point:
类里面那些可以被增强的方法,这些方法称之为连接点
表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point

2.切入点 Pointcut:
实际被增强的方法,称之为切入点
表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方

3.通知 Advice:
实际增强的逻辑部分称为通知 (增加的功能)
Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
通知类型: 1 前置通知 2 后置通知 3 环绕通知 4 异常通知 5 最终通知

4.目标对象 Target:
被增强功能的对象(被代理的对象)织入 Advice 的目标对象

5.切面Aspect:
表现为功能相关的一些advice方法放在一起声明成的一个Java类
Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

6.织入 Weaving
创建代理对象并实现功能增强的声明并运行过程
将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

Spring Aop实现

AspectJ本身并不是spring框架中的组成部分, 是一个独立的AOP框架,一般把AspectJ和Spring框架的AOP依赖一起使用,所以要导入一个独立的依赖

实现的两种方式
1.基于注解方式实现 (熟练)
2.基于XML配置方式 (了解)

准备工作
导入依赖

<dependencies>
        <!--spring核心容器包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!--spring切面包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!--织入包  spring-aspects 已经导入该包,这里可以不导入-->
        <!--<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>-->
        <!--aop联盟包-->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!--Apache Commons日志包-->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <!--德鲁伊连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <!--Junit单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
        <!--lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

切入点表达式

切入点表达式: 通过一个表达式来确定AOP要增强的是哪个或者那些方法
语法结构:execution([权限修饰符][返回值类型][类的全路径名][方法名](参数 列表) )
例子1
execution(* com.msb.dao.UserDaoImpl.add(..))  //指定切点为UserDaoImpl.add方法 
execution(* com.msb.dao.UserDaoImpl.*(..))      //指定切点为UserDaoImpl.所有的方法 
execution(* com.msb.dao.*.*(..))                         //指定切点为dao包下所有的类中的所有的方法 
execution(* com.msb.dao.*.add(..))                     // 指定切点为dao包下所有的类中的add的方法 
execution(* com.msb.dao.*.add*(..))                   // 指定切点为dao包下所有的类中的add开头的方法  		

在这里插入图片描述
项目结构
在这里插入图片描述

基于注解方式实现

开启注解扫描和AOP切面编程自动生成代理对象配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <!--spring 包扫描  -->
    <context:component-scan base-package="com.msb"/>
    <!--aop autoProxy 自动生成代理对象 -->
    <aop:aspectj-autoproxy/>
</beans>

准备接口
UserDao和EmpDao

package com.msb.dao;
/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
public interface EmpDao {
    int addEmp(Integer empno,String ename,String job);
}

package com.msb.dao;
/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
public interface UserDao {
    int addUser(Integer userid,String username);
}

接口实现类

package com.msb.dao.impl;
import com.msb.dao.UserDao;
import org.springframework.stereotype.Repository;
/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
@Repository
public class UserDaoImpl implements UserDao {
    public int addUser(Integer userid,String username){
        System.out.println("userdao add ... ...");
        //int i =1/0;
        return 1;
    }
}
package com.msb.dao.impl;
import com.msb.dao.EmpDao;
import com.msb.dao.UserDao;
import org.springframework.stereotype.Repository;
/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
@Repository
public class EmpDaoImpl implements EmpDao {
    public int addEmp(Integer empno,String ename,String job){
        System.out.println("empDao add ... ...");
        return 1;
    }
}

准备切面

package com.msb.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
@Component
@Aspect
public class DaoAspect {
    //定义公共切点
    @Pointcut("execution(* com.msb.dao.*.add*(..))")
    public void addPointCut(){}
    /*
    * 前置通知: 切点方法执行之前先执行的功能
    * 参数列表可以用JoinPoint接收切点对象
    * 可以获取方法执行的参数
    * */
    @Before("addPointCut()")
    public void methodBefore(JoinPoint joinPoint){
        System.out.println("Before invoked");
    }
    /*
    * 后置通知:方法执行之后要增强的功能
    * 无论切点方法是否出现异常都会执行的方法
    * 参数列表可以用JoinPoint接收切点对象
    * */
    @After("addPointCut()")
    public void methodAfter(JoinPoint joinPoint){
        System.out.println("After invoked");
    }
    /*
     * 返回通知:切点方法正常运行结束后增强的功能
     * 如果方法运行过程中出现异常,则该功能不运行
     * 参数列表可以用 JoinPoint joinPoint接收切点对象
     * 可以用Object res接收方法返回值,需要用returning指定返回值名称
     * */
    @AfterReturning( value = "addPointCut()",returning = "res")
    public void methodAfterReturning(JoinPoint joinPoint,Object res){
        System.out.println("AfterReturning invoked");
    }
    /*
     * 异常通知:切点方法出现异常时运行的增强功能
     * 如果方法运行没有出现异常,则该功能不运行
     * 参数列表可以用Exception ex接收异常对象 需要通过throwing指定异常名称
     * */
    @AfterThrowing( value = "addPointCut()",throwing = "ex")
    public void methodAfterThrowing(Exception ex){
        System.out.println("AfterThrowing invoked");
    }
    /*环绕通知:在切点方法之前和之后都进行功能的增强
    * 需要在通知中定义方法执行的位置,并在执行位置之前和之后自定义增强的功能
    * 方法列表可以通过ProceedingJoinPoint获取执行的切点
    * 通过proceedingJoinPoint.proceed()方法控制切点方法的执行位置
    * proceedingJoinPoint.proceed()方法会将切点方法的返回值获取到,并交给我们,可以做后续处理
    * 我们在环绕通知的最后需要将切点方法的返回值继续向上返回,否则切点方法在执行时接收不到返回值
    * */
    @Around("addPointCut()")
    public Object methodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("aroundA invoked");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("aroundB invoked");
        return proceed;
    }
}

测试代码

 @Test
    public void test1(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = context.getBean( UserDao.class);
        int add = userDao.addUser(10,"晓明");
    }

JoinPoint对象和ProceedingJoinPoint对象
在这里插入图片描述
在这里插入图片描述
有多个增强类对同一个方法进行增强,通过@Order注解设置增强类优先级
数字越小,优先级越高
数字越大,其代理位置越靠近注入位置
在这里插入图片描述
在这里插入图片描述
完全使用注解开发
创建配置类替代applicationContext.xml

package com.msb.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
@Configuration
@ComponentScan(basePackages = "com.msb")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}

测试代码

@Test
    public void test2(){
        ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
        UserDao userDao = context.getBean( UserDao.class);
        int add = userDao.addUser(10,"晓明");
    }

基于XML配置方式

取消上面配置的切面类注解

package com.msb.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

//@Aspect
//@Component
//@Order(2)
public class DaoAspect {
    //定义一个公共切点,通过切点表达式来动态控制需要增强的方法
//    @Pointcut("execution(* com.msb.dao.*.add*(..))")
    public void addPointCut(){};

//    @Before("addPointCut()")
    public void methodBefore(JoinPoint joinPoint) {
        System.out.println("前置通知");
//        Object[] args = joinPoint.getArgs();
//        System.out.println("args = " + Arrays.toString(args));
    }

//    @After("addPointCut()")
    public void methodAfter(JoinPoint joinPoint) {
        System.out.println("后置通知");
    }

//    @AfterReturning(value = "addPointCut()", returning = "res")
    public void methodAfterReturning(JoinPoint joinPoint, Object res) {
        System.out.println("返回通知" + res);
    }

//    @AfterThrowing(value = "addPointCut()", throwing = "exception")
    public void methodAfterThrowing(Exception exception) {
        System.out.println("异常通知" + exception);
    }

//    @Around("addPointCut()")
    public Object methodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知A");
        Object res = proceedingJoinPoint.proceed();
        System.out.println("环绕通知B");
        return res;
    }
}

配置文件配置如下内容

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--    &lt;!&ndash;    spring 包扫描&ndash;&gt;-->
<!--    <context:component-scan base-package="com.msb"/>-->
<!--    &lt;!&ndash;    aop autoProxy 自动生成代理对象&ndash;&gt;-->
<!--    <aop:aspectj-autoproxy/>-->

    <!--创建对象-->
    <bean id="userDao" class="com.msb.dao.impl.UserDaoImplA"></bean>
    <bean id="daoAspect" class="com.msb.aspect.DaoAspect"></bean>

    <!--配置aop增强-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="pointCutAdd" expression="execution(* com.msb.dao.*.add*(..))"/>
        <!--配置切面-->
        <aop:aspect ref="daoAspect">
            <!--增强作用在具体的方法上-->
            <aop:before method="methodBefore" pointcut-ref="pointCutAdd"/>
            <aop:after method="methodAfter" pointcut-ref="pointCutAdd"/>
            <aop:around method="methodAround" pointcut-ref="pointCutAdd"/>
            <aop:after-returning method="methodAfterReturning"  pointcut-ref="pointCutAdd" returning="res"/>
            <aop:after-throwing method="methodAfterThrowing"  pointcut-ref="pointCutAdd" throwing="exception"/>
        </aop:aspect>
    </aop:config>
</beans>

测试代码

   @Test
    public void getBean() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = context.getBean(UserDao.class);
        int res = userDao.addUser("aaa", "张三");
        System.out.println("res = " + res);
    }

配置成功,打印如下

前置通知
环绕通知A
UserDaoImplA add ...
返回通知1
环绕通知B
后置通知
res = 1

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

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

相关文章

字体反爬,一种来自字体设计师的跨行反爬案例 | 案例 28

本篇博客涉及的内容非常有价值&#xff0c;尤其是在反爬领域。 核心内容为自定义字体文件反爬。 文章目录准备工作在 Web 页面中使用字体文件整理文字编码Flask 中随机一串数字&#xff0c;渲染到前台总结准备工作 在正式编写代码前&#xff0c;需要先安装 FontCreator &#…

GeoServer学习笔记-01GeoSever运行编译

一、运行1. 下载GeoServerGitHub仓库地址&#xff1a;https://github.com/geoserver/geoserver2.本地代码工具打开项目在idea里&#xff0c;文件->新建->来自现有的源代码项目&#xff0c;选择项目的pom文件加载项目。3.idea编译环境设置&#xff08;1&#xff09;设置jd…

图论(5)最小生成树简单应用

活动 - AcWing 参考&#xff1a;《算法竞赛进阶指南》-lyd 目录 一、基础算法 二、 1.最短网络&#xff08;prim板子&#xff09; 2.局域网&#xff08;kruskal板子&#xff09; 3.繁忙的都市 4.1143. 联络员 5.连接格点&#xff08;预处理&#xff09; 一、基础算法…

影响电商发展的重要因素及电商未来的发展可能

易观分析&#xff1a;自从互联网传入中国以后&#xff0c;特别是2000年以后&#xff0c;一直保持着非常快的演变速度&#xff0c;而以互联网为基础的电商则更是发展成了中国互联网的代表性行业。中国电商的发展不仅在初期有着非常高的增速&#xff0c;有着多样化的演进路线&…

Numpy(3)—切片和索引、高级索引、Broadcast、迭代数组、广播迭代

1.切片和索引 eg1&#xff1a;我们首先通过 arange() 函数创建 ndarray对象。 然后&#xff0c;分别设置起始&#xff0c;终止和步长的参数为 2&#xff0c;7 和 2。 import numpy as npa np.arange(10) s slice(2, 7, 2) # 从索引 2 开始到索引 7 停止&#xff0c;间隔为…

【笔记】行人属性识别

【论文】https://arxiv.org/pdf/1901.07474.pdf &#xff08;以下序号与论文序列不对应&#xff09; 属性可以看作是高层语义信息&#xff0c;对视点变化和观察条件多样性具有更强的鲁棒性 本文试图解决以下几个重要问题: (1)传统和基于深度学习的行人属性识别算法之间的联系…

Java_Git:4. 分支管理

目录 1 创建合并分支 2 使用TortoiseGit实现分支管理 2.1 创建分支 2.2 合并分支 3 解决冲突 1 创建合并分支 Git会把每次的提交&#xff0c;串成一条时间线&#xff0c;即一个分支。 初始状态&#xff0c;Git只有一条时间线&#xff08;分支&#xff09;&#xff0c;这个分…

高级Spring之BeanFactory 后处理器

老样子&#xff0c;直接上代码演示&#xff0c;准备一个干净的容器&#xff1a; // ⬇️GenericApplicationContext 是一个【干净】的容器GenericApplicationContext context new GenericApplicationContext();//注册configBeancontext.registerBean("config", Conf…

【C语言学习笔记】39. 错误处理、递归

前言 C 语言不提供对错误处理的直接支持&#xff0c;但是作为一种系统编程语言&#xff0c;它以返回值的形式允许您访问底层数据。 错误处理 C 语言不提供对错误处理的直接支持&#xff0c;但是作为一种系统编程语言&#xff0c;它以返回值的形式允许您访问底层数据。在发生…

振弦采集模块配置工具VMTool的MODBUS 工具模块

振弦采集模块配置工具VMTool的MODBUS 工具模块 &#xff08; 1&#xff09; 寄存器查看 此功能模块提供标准的 MODBUS 协议寄存器显示及单个寄存器修改功能&#xff0c;通过点击扩展功能区的【 MODBUS】 标签切换到此模块&#xff0c;如下图所示。 此模块将 VMXXX 所有寄存器以…

Codeforces Round #847 (Div. 3) A~E

比赛链接&#xff1a;Dashboard - Codeforces Round #847 (Div. 3) - Codeforces 目录 A. Polycarp and the Day of Pi B. Taisia and Dice C. Premutation D. Matryoshkas E. Vlad and a Pair of Numbers A. Polycarp and the Day of Pi 题意&#xff1a;求出一个数字…

一步创建 AI 图像网站,即刻生成 AI 图像解决方案 #Graydient

过去一年当中&#xff0c;AI 画图工具非常火爆&#xff0c;一条简单的指令&#xff0c;就能快速得出超高品质的图形&#xff0c;这对于游戏开发者来说无疑是令人振奋的消息&#xff0c;尤其是没有预算和人手打造美术资源的中小团队。从网络上看到的结果来看&#xff0c;AI 的绘…

JDBC(powernode 文档)(内含源代码)

源代码下载地址链接&#xff1a;https://download.csdn.net/download/weixin_46411355/87400304 目录 JDBC概述 1.1 前言 1.2 什么是JDBC 1.3 JDBC的原理 1.4 程序员&#xff0c;JDBC&#xff0c;JDBC驱动的关系及说明 1.4.1 JDBC API 1.4.2 JDBC 驱动 1.4.3 Java程序员…

并发编程-多线程并发设计原理

并发编程-多线程&并发设计原理并发编程简介多线程&并发设计原理1 多线程回顾1.1 Thread和Runnable1.1.1 Java中的线程1.1.2 Java中的线程&#xff1a;特征和状态1.1.3 Thread和Runnable接口1.1.4 Callable1.2 synchronized关键字1.2.1 锁的对象1.2.2 锁的本质1.2.3 实现…

k8s实现controller如何远程调式?

背景&#xff1a; 使用kubebuilder和code-generate生成自定义资源代码后&#xff0c;实现管理自定义资源的controller逻辑。此时&#xff0c;需要调试controller代码逻辑&#xff0c;有2种思路。方法1&#xff1a;对该代码打包成镜像文件&#xff0c;直接部署进入k8s集群中&…

Springboot+vue中小企业合同管理系统

编写企业合同管理系统&#xff0c;让其能创建合同、修改合同、删除合同、合同变更标识、合同收款提醒、合同时间管理、合同废止标识、结束合同、合同统计、合同查询等几大功能。 (1) 创建合同 管理人员将签订后的合同的各项信息存入数据库中&#xff0c;使合同进入开始执行的…

网络编程(2)

封装和分用 1)封装:就是在数据中添加一些辅助传输的信息&#xff1b; 2)分用:就是解析这些信息 3)发送数据的时候&#xff0c;上层协议要把数据交给下层协议&#xff0c;由下层协议来添加一些信息 4)接收数据的时候&#xff0c;下层协议要把数据交给上层协议&#xff0c;有上层…

分割pdf的办法?看这里就明白了!

对于大多数办公党来说&#xff0c;困难的或许不是制作一些办公文件、文档&#xff0c;重要的是如何将这些文档以合适的形式发送给需要的人。不管是客户还是同事、上级&#xff0c;他们对文档格式、内容的要求都是有不一样的标准的。这时候我们就面临一个重要的问题了&#xff0…

Linux驱动开发:块设备驱动

这里写自定义目录标题一、块设备的简介二、块设备驱动框架1、block_device 结构体2、gendisk 结构体3、block_device_operations 结构体4、块设备 I/O 请求过程5、bio 结构体三、使用请求队列方式的块设备驱动程序1、经过第“二”部分的讲解总结&#xff0c;可以得出驱动程序的…

Java基础10:常用API(上)

Java基础10&#xff1a;常用API&#xff08;上&#xff09;一、Math二、System1. currentTimeMillis2. arraycopy三、Runtime四、Object1. toString2. equals3. clone五、Objects六、BigInteger1. 构造方法&#xff08;获取BigInteger&#xff09;2. 常用方法七、BigDecimal1. …