Spring基础学习之AOP面向切面编程

前言

小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。
这个Spring基础学习系列是用来记录我学习Spring框架基础知识的全过程 (这个系列是参照B站狂神的Spring5最新教程来写的,由于是之前整理的,但当时没有发布出来,所以有些地方可能有错误,希望大家能够及时指正!)
之后我将会以一天一更的速度更新这个系列,还没有学习Spring5框架的小伙伴可以参照我的博客学习一下;当然学习过的小伙伴,也可以顺便跟我一起复习一下基础。
最后,希望能够和大家一同进步吧!加油吧!少年们!
废话不多说,让我们开始今天的学习内容吧,今天我们来到了Spring基础学习的第十一站:AOP面向切面编程

11.AOP 面向切面编程

11.1 AOP概述

11.1.1 什么是AOP?

  • AOP(Aspect Oriented Programming):面向切面编程,通过预编译方式运行期动态代理实现程序功能的统一维护的一种技术
  • AOPOOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型

11.1.2 AOP的优点

  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务人员专心于业务逻辑的处理
  • 使业务逻辑各部分之间的耦合度降低,提高程序的可重用性
  • 提高系统的可维护性,同时提高了开发的效率

AOP面向切面编程结构图

在这里插入图片描述

11.2 AOP在Spring中的作用

11.2.1 AOP相关概念

提供声明式事务即允许用户自定义切面

  • 横切关注点跨越应用程序多个模块的方法或功能 (即与业务逻辑无关的,但是需要关注的部分),这就是横切关注点 (如日志管理,权限控制,事务处理、异常处理等)
  • 切面 (Aspect):横切关注点被模块化的特殊对象 (也就是切入点和通知的结合),即它是一个类 (例如日志类 Log)
  • 通知 (Advice):切面必须要完成的工作 (即对切入点增强的内容),它是类中的一个方法 (日志类Log中的方法,例如前置、后置和环绕通知等)
  • 切入点 (PointCut):是指要对哪些JointPoint进行拦截,即被拦截的连接点 (可以理解为对切面通知执行的"地点"的定义)
  • 连接点 (JointPoint):是指那些被拦截的点,在Spring中,可以使用动态代理拦截目标类的方法 (即与切入点匹配的执行点)
  • 目标 (Target) :是指代理的目标对象 (也就是被通知的对象)
  • 代理(Proxy):是指生成的代理对象 (即向目标对象应用通知之后创建的对象)
  • 织入 (Weaving):是指把增强代码应用到目标上,生成代理对象的过程

11.2.2 AOP实现结构图

在这里插入图片描述

11.2.3 SpringAOP中的Advice

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5中类型的Advice

通知类型作用实现接口应用
前置通知在目标方法执行前实施增强,org.springframework.aop.MethodBeforeAdvice可以应用于权限管理
后置通知在目标方法执行后实施增强org.springframework.aop.AfterReturningAdvice可以用关闭流、上传文件、删除临时文件等功能
环绕通知在目标方法执行前后实施增强org.aopalliance.intercept.MethodInterceptor可以应用于日志、事务管理等功能
异常抛出通知在方法抛出异常后执行通知org.springframework.aop.ThrowsAdvice可以应用于处理异常记录日志等功能
引介通知在目标类中添加一些新的方法和属性org.springframework.aop.IntroductionInterceptor可以应用于老版本程序(增强类)

即AOP在不改变原有代码的情况下,去增加新的功能

11.3 使用Spring实现AOP

11.3.1 使用AOP织入,需要导入一个依赖包

 <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>
  • 方式一使用Spring的API接口【主要SpringAPI接口实现】
  • 方式二自定义来实现AOP【主要是切面定义】

11.3.2 使用Spring的API接口

1.创建Log实体类
//实现前置通知接口的Log实体类
public class Log implements MethodBeforeAdvice {

    /**
     * 前置通知方法
     * @param method:要执行的目标对象的方法
     * @param args:参数
     * @param target:目标读写
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
    
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
        
    }
    
}
2.创建AfterLog实体类
// 实现后置通知接口的AfterLog实体类
public class AfterLog implements AfterReturningAdvice {

    // returnValue:返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
    }
    
}
3.创建UserService接口
// 创建用户服务接口(抽象角色)
public interface UserService {

    // 定义增删改查方法
    public void add();
    
    public void delete();
    
    public void update();
    
    public void query();
    
}
4.创建UserService接口实现类
// 用户服务接口的实现类(真实角色)
public class UserServiceImpl implements UserService {

    public void add() {
        System.out.println("增加一个用户");
    }
    
    public void delete() {
        System.out.println("删除一个用户");
    }
    
    public void update() {
        System.out.println("修改加一个用户");
    }
    
    public void query() {
        System.out.println("查询一个用户");
    }
    
}
5.applicationContext.xml配置文件
<?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: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 注册目标类和切面等bean对象 -->
    <!-- 目标类 -->
    <bean id="userService" class="com.kuang.service.UserServiceImpl"></bean>
    <!-- 切面 -->
    <bean id="log" class="com.kuang.log.Log"></bean>
    <bean id="afterLog" class="com.kuang.log.AfterLog"></bean>
    
    <!-- 方式一:使用原生Spring API接口 -->
    <!-- 配置aop:需要导入aop的约束 -->
    <aop:config>
        <!-- 配置切入点,通知最后增强哪些方法 -->
        <!-- pointcut是指被拦截的连接点,也就是切面通知的执行点 -->
        <!-- expression:使用切面表达式,增强com.kuang.service.UserServiceImpl包下的所有类型的全部方法 -->
        <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
        <!-- 关联通知Advice和切入点pointcut -->
        <!-- 前置通知 -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <!-- 后置通知 -->
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
    
</beans>
6.创建测试类
public class MyTest {
    public static void main(String[] args) {
    
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 动态代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
    
}
7.测试结果

在这里插入图片描述

11.3.3 自定义来实现AOP

1.自定义切面类
// 自定义的切面类: 可以存在多个通知Advice (增强的方法内容)
public class DiyPointCut {
    
    // 前置通知
    public void before() {
        System.out.println("======方法执行前======");
    }
    
    // 后置通知
    public void after() {
        System.out.println("======方法执行后======");
    }
    
}
2.创建UserService接口

同11.3.2的UserService接口

3.创建UserService接口实现类

同11.3.2的UserService接口实现类

4.xml配置文件
<?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: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 注册目标类、切面的bean信息 -->
    <!-- 目标类 -->
    <bean id="userService" class="com.kuang.service.UserServiceImpl"></bean>
    
    <!-- 方式二:自定义类 -->
   <!-- 切面:其实就是被模块化的特殊对象(实际上就是一个类) -->
    <bean id="diy" class="com.kuang.diy.DiyPointCut"/>
     <!-- 配置aop -->
    <aop:config>
        <!-- 引入自定义的切面,ref中是要引用的类 -->
        <aop:aspect ref="diy">
             <!-- 配置切入点,通知最后增强哪些方法 -->
            <!-- pointcut:是指被拦截的连接点,即切面通知执行的地点 -->
            <!-- expression:使用切面表达式,增强com.kuang.service下的指定类UserServiceImpl的所有类型的全部方法 -->
            <aop:pointcut id="point" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
                <!-- 增强com.kuang.service包及其子包下所有类的所有类型的全部方法 -->
<!--            <aop:pointcut id="point" expression="execution(* com.kuang.service..*.*(..))"/>-->
            <!-- 关联通知Advice和切入点pointCut -->
            <!-- Advice:是指切面必须要完成的工作,对切入点增强的内容(实际上就是类的一个方法) -->
            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="point"/>
            <!-- 后置通知 -->
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
    
</beans>
5.创建测试方法
  • 同11.3.2中的测试方法
6.测试结果

在这里插入图片描述

11.3.4 Aspectj切入点语法定义

定义切入点表达式:例如execution(* com.example.service.impl…*. *(…))

execution()是做常用的切点函数,其语法为:整个表达式可以分为五个部分

  • execution()表达式主体

  • 第一个*号表示返回类型, *号表示所有的类型

  • 包名表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.example.service.impl包、子孙包下所有类的方法

  • 第二个*号表示类名, *号表示所有的类

  • *(…)最后这个星号表示方法名, *号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数

总结

简单来说就是,该表达式的作用就是增强该包及其子包下的所有类的所有类型的全部方法

11.3.5 使用注解实现AOP

1.创建AnnotationPointCut实体类
// 方式三:使用注解方式实现AOP
@Aspect
// 标注这个类是个切面
public class AnnotationPointCut {
    
    // 前置通知(切入点表达式)
    @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("====方法执行前====");
    }
    
    // 后置通知(切入点表达式)
    @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("====方法执行后=====");
    }
    
    // 环绕通知
    @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
    // 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    public void around(ProceedingJoinPoint jp) throws Throwable { // 环绕方法中的参数为连接点
        System.out.println("=====环绕前=====");
        Signature signature = jp.getSignature(); // 获得签名
        System.out.println("签名为:"+signature);
        Object proceed = jp.proceed(); // 执行方法
        System.out.println("=====环绕后=====");
        System.out.println(proceed);
    }
    
}
2.创建UserService接口
  • 同11.3.2UserService接口
3.创建UserService接口实现类
  • 同11.3.3UserServiceImpl实现类
4.applicationContext3.xml配置文件
<?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: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- 方式三:使用注解实现AOP -->
    <!--切面:其实就是被模块化的特殊对象(实际上就是一个类)-->
    <bean id="annotationPointCut" class="com.kuang.diy.AnnotationPointCut"/>
    <!-- 开启注解支持 JDK(默认 proxy-target-class="true") cglib(proxy-target-class="true") -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    
</beans>
5.创建测试类
public class MyTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext3.xml");
        // 注意:动态代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
    
}
6.测试结果

在这里插入图片描述

好了,今天的有关AOP面向切面编程的学习就到此结束啦,欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连,我们下期见,拜拜啦!


参考视频链接:https://www.bilibili.com/video/BV1WE411d7Dv(【狂神说Java】Spring5最新教程IDEA版通俗易懂)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂奔の蜗牛rz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值