浅谈Spring-AOP

2023-11-16

HI,大家好,我是Lee

这篇文章我们主要说一下关于Spring-AOP,什么是AOP?什么叫做面向切面编程?为什么要使用AOP?接下来让我们往下看

什么是AOP?

AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP的作用:

不修改源码的情况下,程序运行期间对方法进行功能增强

好处:

1、减少代码的重复,提高开发效率,便于维护。

2、专注核心业务的开发。

核心业务和服务性代码混合在一起

开发中:各自做自己擅长的事情,运行的时候将服务性代码织入到核心业务中。通过spring工厂自动实现将服务性代码以切面的方式加入到核心业务代码中。

代理模式

一、代理模式

AOP底层是动态代理 。

代理模式分为静态代理与动态代理 /

代理模式作为23种经典设计模式之一,其比较官方的定义为“为其他对象提供一种代理以控制对这个对象的访问”, 简单点说就是,之前A类自己做一件事,在使用代理之后,A类不直接去做,而是由A类的代理类B来去做。代理类其实是在之前类的基础上做了一层封装。java中有静态代理、JDK动态代理、CGLib动态代理的方式。静态代理指的是代理类是在编译期就存在的,相反动态代理则是在程序运行期动态生成的

二、静态代理

1、原有方式:核心业务与服务方法编写在一起

public class TeamService {
    public void add(){
        try {
            System.out.println("开始事务");
            System.out.println("TeamService---- add----");// 核心业务
            System.out.println("提交事务");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("回滚事务");
            }
     }
}

2、基于类的静态代理 

缺点:代理类只能代理一个类

public class ProxyTest {
    @Test
    public void test(){
        UserService userService = new UserService();
        Proxy proxy = new Proxy(userService);
        proxy.show();
    }
}

class UserService{
    public void show(){
        System.out.println("Output information -->");
    }
}

class Proxy extends UserService{
    private UserService userService;
    public Proxy(UserService userService) {
        this.userService = userService;
    }
    @Override
    public void show() {
        try {
            System.out.println("开始事务");
            super.show();
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("异常事务-->回滚");
        }finally {
            System.out.println("结束事务");
        }
    }
}

3、基于接口的静态代理 

要求:代理类和被代理类都实现了同一个接口

public class ProxyTest {
    @Test
    public void test(){
        UserService userService1 = new UserServiceImpl();
        UserService userService2 = new StudentServiceImpl();

        Proxy proxy1 = new Proxy(userService1);
        proxy1.show();
        Proxy proxy2 = new Proxy(userService2);
        proxy2.show();
    }
}

interface UserService{
    void show();
}

class UserServiceImpl implements UserService{

    public void show(){
        System.out.println("UserServiceImpl Output information -->");
    }
}

class StudentServiceImpl implements UserService{

    public void show() {
        System.out.println("StudentServiceImpl Output information -->");
    }
}

class Proxy implements UserService{
    private UserService userService;

    public Proxy(UserService userService) {
        this.userService = userService;
    }


    public void show() {
        try{
            System.out.println("开始事务");
            userService.show();
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("异常-->回滚事务");
        }finally {
            System.out.println("结束事务");
        }
    }
}

4、提取出切面代码,作为AOP接口

public class ProxyTest {
    @Test
    public void test(){
        UserService userService1 = new UserServiceImpl();
        UserService userService2 = new StudentServiceImpl();
        AOP tranAOP = new TranAOP();

        ProxyService proxy1 = new ProxyService(userService1,tranAOP);
        proxy1.show();
        ProxyService proxy2 = new ProxyService(userService2,tranAOP);
        proxy2.show();
    }
}

interface UserService{
    void show();
}

class UserServiceImpl implements UserService{

    public void show(){
        System.out.println("UserServiceImpl Output information -->");
        int a = 1/0;
    }
}

class StudentServiceImpl implements UserService{

    public void show() {
        System.out.println("StudentServiceImpl Output information -->");
    }
}

interface AOP{
    void before();
    void after();
    void exception();
    void myFinally();
}

class TranAOP implements AOP{

    public void before() {
        System.out.println("开始事务-->");
    }

    public void after() {
        System.out.println("提交事务-->");
    }

    public void exception() {
        System.out.println("异常--回滚事务-->");
    }

    public void myFinally() {
        System.out.println("结束事务-->");
    }
}

class ProxyService implements UserService{
    private UserService userService;
    private AOP aop;

    public ProxyService(UserService userService, AOP aop) {
        this.userService = userService;
        this.aop = aop;
    }

    public void show() {
        try{
            aop.before();
            userService.show();
            aop.after();
        }catch (Exception e){
            aop.exception();
        }finally {
            aop.myFinally();
        }
    }
}

三、动态代理

基于动态代理有两种方式:     

                    基于JDK的动态代理    

                    基于CGLIB的动态代理

1、基于jdk的动态代理

//定义一个接口
interface IService {
    void show();
}
//定义一个类
class UserService implements IService{

    @Override
    public void show() {
        System.out.println("Output information -->");
    }
}
//使用JDK的动态代理添加事务
public class ProxyTest {
    public static void main(String[] args) {
        //目标对象(被代理的对象)
        UserService userService = new UserService();
        //返回代理的对象 基于JDK的代理
        IService proxyService = (IService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new InvocationHandler() { //句柄(回调函数)
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            System.out.println("开始事务");
                            Object invoke = method.invoke(userService, args);//核心方法
                            System.out.println("提交事务");
                            return invoke;
                        } catch (Exception e) {
                            System.out.println("回滚事务");
                            e.printStackTrace();
                            throw e;
                        }
                    }
                }
        );
        proxyService.show();
    }
}

代理对象不需要实现接口,但是目标对象一定要实现接口,否则无法使用JDK的动态代理。

2、基于CGLIB的动态代理

CFLIB代理,也叫子类代理,在内存中构建一个子类对象从而实现对目标对象功能的扩展

//创建被代理对象
class UserService {
    public void show() {
        System.out.println("Output information -->");
    }
}
//使用CGLIB代理事务
public class ProxyTest {
    public static void main(String[] args) {
        //目标对象(被代理对象) 无实现接口
        UserService userService = new UserService();
        //创建代理对象,使用CGLIB代理
        UserService proxyService = (UserService) Enhancer.create(
                userService.getClass(),
                userService.getClass().getInterfaces(),
                new MethodInterceptor() { //回调函数编写代理规则
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        try {
                            System.out.println("开始事务");
                            Object invoke = methodProxy.invokeSuper(o, objects);
                            System.out.println("提交事务");
                            return invoke;
                        }catch (Exception e){
                            System.out.println("回滚事务");
                            e.printStackTrace();
                            throw e;
                        }
                    }
                });

        proxyService.show();
    }
}

Spring-AOP

一、Spring-AOP

Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。 

二、AOP的相关术语:

Target(目标对象) 要被增强的对象,一般是业务逻辑类的对象。 

Proxy(代理) 一个类被 AOP 织入增强后,就产生一个结果代理类。 

Aspect(切面) 表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合。 

Joinpoint(连接点) 所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法(一般是类中的业务方法),因为 Spring只支持方法类型的连接点。 

Pointcut(切入点) 切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。 

Advice(通知/增强) 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时 间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。

通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。 

切入点定义切入的位置,通知定义切入的时间。 

Weaving(织入). 是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编 译期织入和类装载期织入。 

三、切面的三个关键因素

1、切面的功能--切面能干啥 

2、切面的执行位置--使用Pointcut表示切面执行的位置 

3、切面的执行时间--使用Advice表示时间,在目标方法之前还是之后执行。

四、AspectJ 对 AOP 的实现 

对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。 

AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。 

在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式 AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。

五、AspectJ的通知类型 AspectJ 中常用的通知有5种类型

  1. 前置通知 

  2. 后置通知

  3. 环绕通知

  4. 异常通知

  5. 最终通知

六、AspectJ的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。 

表达式的原型如下: 

execution(modifiers-pattern? ret-type-pattern 

declaring-type-pattern?name-pattern(param-pattern) 

throws-pattern?) 

说明: 

modifiers-pattern] 访问权限类型 

ret-type-pattern 返回值类型 

declaring-type-pattern 包名类名 

name-pattern(param-pattern) 方法名(参数类型和参数个数) 

throws-pattern 抛出异常类型 

?表示可选的部分 

以上表达式共 4 个部分。 

execution(访问权限 方法返回值    方法声明(参数) 异常类型) 

切入点表达式要匹配的对象就是目标方法的方法名。

所以,execution 表达式中就是方法的签名。 

PS:表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号: 

符号  意义 

*    0-多个任意字符

..   用在方法参数中,表示任意个参数;用在包名后,表示当前及其子包路径 

+    用在类名后,表示当前及其子类;用在接口后,表示当前接口及其实现类 示例:execution(* com.kkb.service.*.*(..)) 

指定切入点为:定义在 service 包里的任意类的任意方法。 

execution(* com.kkb.service..*.*(..)) 

指定切入点为:定义在 service 包或者子包里的任意类的任意方法。

“..”出现在类名中时,后面必须跟 “*”,表示包、子包下的所有类。 

execution(* com.kkb.service.IUserService+.*(..)) 

指定切入点为:IUserService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类, 则为该类及其子类中的任意方法。

关于Spring-AOP就说到这里啦。

大家喜欢的可以关注公众号:测试开发Lee

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

浅谈Spring-AOP 的相关文章

随机推荐

  • Git——Day3(Github Pages搭建个人网站)

    1 个人站点访问 https github用户名 github io 2 搭建步骤 1 创建个人站点 gt 新建仓库 注 仓库名必须是 用户名 github io 2 在仓库下新建index html的文件即可 注意 1 github pa
  • Python报错socket.gaierror: [Errno 11001] getaddrinfo failed

    1 报错 from scapy all import sr IP ICMP target 192 168 142 129 pkt IP dst target ICMP ans unans sr pkt timeout 1 for s r i
  • GitHub Desktop客户端下载安装,以及上传到服务端

    下载安装地址 https desktop github com 使用教程 https blog csdn net qqw666666 article details 125652869 操作流程 就是不同应用端的交互 做好相关验证即可
  • 应用中间件二、Tomcat单机多实例部署

    Tomcat 常见的几种部署场景 通常 我们在同一台服务器上对 Tomcat 部署需求可以分为以下几种 单实例单应用 单实例多应用 多实例单应用 多实例多应用 实例的概念可以理解为上面说的一个 Tomcat 目录 单实例单应用 比较常用的一
  • Python3.x opencv操作中文文件

    我用的是python3 5 本身用file打开中文文件是没有问题的 但是用opencv就不行 网上看到很多解决版本 可能都是针对python2 x的 没有效果 后来在知乎上看到一个解决方法 测试有效 引用在这里 冯卡门 由于python3字
  • Redis底层数据结构.md

    1 Redis 概述 Redis 数据库里面的每个键值对 key value 都是由对象 object 组成的 数据库键总是一个字符串对象 string object 数据库的值则可以是字符串对象 列表对象 list 哈希对象 hash 集
  • Jmeter对图片验证码的处理

    jmeter对图片验证码的处理 在web端的登录接口经常会有图片验证码的输入 而且每次登录时图片验证码都是随机的 当通过jmeter做接口登录的时候要对图片验证码进行识别出图片中的字段 然后再登录接口中使用 通过jmeter对图片验证码的识
  • ctfshow—萌新—web1

    0x00 前言 CTF 加解密合集 CTF Web合集 0x01 题目 0x02 Write Up 解法1 标准的数字型注入 查列名 http cc3ecc3f 8c42 4624 979e 277a51ea85d2 challenge c
  • 【面经】外企德科-华为精英研发项目-笔试编程题

    微信搜索 编程笔记本 获取更多干货 微信搜索 编程笔记本 获取更多干货 点击上方蓝字关注我 我们一起学编程 欢迎小伙伴们分享 转载 私信 赞赏 今天来看一道 外企德科 华为精英研发项目 的一道笔试编程题 求满足条件的最长字串的长度 题目描述
  • 一次 Young GC 的优化实践

    这个 GC 案例比较有意思 排查问题有点像侦探断案 先分析各种可能性 再按照获得的一个个证据 去排除各种可能性 然后定位原因 最终解决问题 问题 某同学在微信上问我 有没有办法排查 YoungGC 效率低的问题 听到这话 我也是不知从何说起
  • Linux网络编程:Socket套接字编程(Server服务器 Client客户端)

    文章目录 一 定义和流程分析 1 定义 2 流程分析 3 网络字节序 二 相关函数 IP地址转换函数inet pton inet ntop 本地字节序 网络字节序 socket函数 创建一个套接字 bind函数 给socket绑定一个服务器
  • 引领AI数据标注行业,景联文科技提供高质量图像和文本标注服务

    近年来 我国的数据要素市场呈现出高速增长的趋势 根据国家工信安全中心的统计数据 截至2022年 我国数据要素市场规模已达到815亿元 同比增长49 51 数据要素作为数字经济时代的关键要素 是构建新发展格局的重要支撑 其重要性日益凸显 党中
  • Android开发学习之路-基本事件的使用

    1 事件的响应方法 setOnClickListener view OnClickListener l setOnFocusChangeListener view OnFocusChangeListener l setOnLongClick
  • [从零开始学习FPGA编程-37]:进阶篇 - 基本时序电路-有限状态机实现(Verilog)

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 目录 第1章 状态机概述 1 1 UML描述状态机 1 2 数字电路描述状态机
  • VScode的代码截图插件CodeSnap

    CodeSnap 在 VS Code 中为您的代码截取漂亮的屏幕截图 插件名 CodeSnap 官方地址 CodeSnap Visual Studio Marketplace 特征 快速保存代码的屏幕截图 将屏幕截图复制到剪贴板 显示行号
  • user-select不可被用户选中

    目录 背景 字段属性 注意 案例 背景 项目中有这种奇葩需求 就是特定字段不让用户复制 不可被选中状态 这种应用场景最多的就是考试系统啥的吧 不让考生复制题目搜题 真恶心 字段属性 注意 浏览器实现之间的区别之一是继承 案例
  • 我用Python做副业,月赚1W+:千万别让死工资拖垮了自己。。

    被压垮的打工人 你还好吗 房贷车贷 上老下小 日常开销节省不了 但你的收入有多少 所以不敢生病 甚至不敢回家 就为了每个月那么点死工资 还得天天加班 然而忙忙忙 却变成了 穷忙族 成为了职场废人 其实很多人都想改变现状 想学点什么的 但就是
  • Proxy error: Could not proxy request错误解决

    vue 项目 错误原因 跨域 解决办法 package json文件中的scripts调试添加 start node index js server nodemon index js ignore client 这篇文章解释的很清楚http
  • 二叉排序树转化成双向链表

    题目描述 输入一棵二叉搜索树 将该二叉搜索树转换成一个排序的双向链表 要求不能创建任何新的结点 只能调整树中结点指针的指向 输入 输入可能包含多个测试样例 对于每个测试案例 输入的第一行为一个数n 0
  • 浅谈Spring-AOP

    HI 大家好 我是Lee 这篇文章我们主要说一下关于Spring AOP 什么是AOP 什么叫做面向切面编程 为什么要使用AOP 接下来让我们往下看 什么是AOP AOP为Aspect Oriented Programming的缩写 意思为