当前位置: 首页 > article >正文

Spring AOP - 配置文件方式实现

目录

AOP基础概念

示例1:模拟在com.text包及子包项下所有类名称以ServiceImpl结尾的类的所有方法执行前、执行后、执行正常后返回值、执行过程中出异常的情况

示例2:统计com.text包及子包项下所有类名称以DaoImpl结尾的类的所有方法执行时长情况


AOP基础概念

AOP:Aspect Oriented Programming 面向切面编程

使用场景:将一些通用的功能封装成切面类,切面类作用在目标类方法的前后,并通过自动插拔实现目标类方法的前后逻辑,示意图如下:

上述的示意图显示,Aspect切面类横向贯穿了3个目标类方法的执行逻辑之前,由此可以看出,AOP实现需要如下组件:

  1. 切面类(Aspect类)
  2. 切点(Pointcut),即上图的各个目标方法,通过切点表达式(execution)实现
  3. 连接点(JoinPoint) ,切面和切点之间的连接信息,可以理解为横切面和切点的交汇处
  4. 通知(Advice) ,在目标类方法的之前、之后还是环绕执行切面逻辑

Spring AOP实现需要引入aspectjweaver依赖

<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.6</version>
</dependency>

示例1:模拟在com.text包及子包项下所有类名称以ServiceImpl结尾的类的所有方法执行前、执行后、执行正常后返回值、执行过程中出异常的情况

1、配置文件:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.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">
    <!--<context:component-scan base-package="com.text"/>-->
    <bean id="studentService" class="com.text.service.impl.StudentServiceImpl"/>
    <!-- 切面类交给IOC容器实例 -->
    <bean id="methodAspect" class="com.text.aspect.MethodAspect"/>
    <!-- aop配置-->
    <aop:config>
        <!-- 切点配置,expression表示com.text所有包及子包项下所有服务层(以ServiceImpl结束的类)的所有方法-->
        <aop:pointcut id="pointcut" expression="execution(public * com.text..*ServiceImpl.*(..))"/>
        <!-- 切面配置-->
        <aop:aspect ref="methodAspect">
            <!-- 切点方法执行之前执行-->
            <aop:before method="methodInvokeBefore" pointcut-ref="pointcut"/>
            <!-- 切点方法执行之后执行-->
            <aop:after method="methodInvokeAfter" pointcut-ref="pointcut"/>
            <!-- 切点方法执行之后带返回值-->
            <aop:after-returning method="methodInvokeAfterReturn" returning="ret" pointcut-ref="pointcut"/>
            <!-- 切点方法执行时出现异常-->
            <aop:after-throwing method="methodInvokeException" throwing="thr" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>

2、服务类及方法(pointcut)

package com.text.service.impl;

import com.text.entity.Course;
import com.text.entity.Student;
import com.text.service.StudentService;

public class StudentServiceImpl implements StudentService {

    @Override
    public void save(Student student) {
        System.out.println(student + "正在被保存...");
    }

    @Override
    public void deleteById(String id) {
        System.out.println("学生id=" + id + "的记录已被删除...");
    }

    @Override
    public void updateById(String id) throws Exception{
        System.out.println("学生id=" + id + "的记录正在被修改...");
        throw new Exception("修改学生信息出异常");
    }

    @Override
    public Student searchById(String id) {
        System.out.println("已查询到学生id=" + id + "的记录...");
        return new Student("张三",20,new Course("计算机"));
    }
}

3、切面类(aspect) 

package com.text.aspect;

import org.aspectj.lang.JoinPoint;

/**
 * 定义方法切面类
 */
public class MethodAspect {
    public void methodInvokeBefore(JoinPoint joinPoint) {
        String targetClassName = joinPoint.getTarget().getClass().getName();//获取切面执行的目标类
        String methodName = joinPoint.getSignature().getName();
        System.out.println(targetClassName + "." + methodName + "方法执行之前的处理逻辑...");
    }

    public void methodInvokeAfter(JoinPoint joinPoint) {
        String targetClassName = joinPoint.getTarget().getClass().getName();//获取切面执行的目标类
        String methodName = joinPoint.getSignature().getName();
        System.out.println(targetClassName + "." + methodName + "方法执行之后的处理逻辑...");
    }

    public void methodInvokeAfterReturn(JoinPoint joinPoint,Object ret) {
        String targetClassName = joinPoint.getTarget().getClass().getName();//获取切面执行的目标类
        String methodName = joinPoint.getSignature().getName();
        System.out.println(targetClassName + "." + methodName + "方法执行之后返回的结果为:" + ret);
    }

    public void methodInvokeException(JoinPoint joinPoint,Throwable thr) throws Throwable {
        String targetClassName = joinPoint.getTarget().getClass().getName();//获取切面执行的目标类
        String methodName = joinPoint.getSignature().getName();
        System.out.println(targetClassName + "." + methodName + "方法执行中的异常信息为:" + thr.getMessage());
        throw thr;
    }
}

4、测试类 Application

package com.text;
import com.text.entity.Course;
import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        StudentService studentService = context.getBean("studentService", StudentService.class);
        studentService.save(new Student("张三",20,new Course("计算机")));
        System.out.println("======save end========");
        studentService.deleteById("1");
        System.out.println("======delete end ========");
        Student student = studentService.searchById("1");
        System.out.println("======search end ========");
        studentService.updateById("1");
        System.out.println("======update end ========");
    }
}

5、输出结果:

示例2:统计com.text包及子包项下所有类名称以DaoImpl结尾的类的所有方法执行时长情况

此需求如果按照之前的advice至少需要写2个,一个是aop:before,一个是aop:after,方法的总执行时间需要after的当前时间减去before的当前时间,这2个时间如何联通也存在困难,AOP 提供一种自定义的通知执行时机-Around Advice(环绕通知)可以轻松解决此需求

1、配置文件:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.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">
    <!--<context:component-scan base-package="com.text"/>-->
    <bean id="studentDao" class="com.text.dao.impl.StudentDaoImpl"/>
    <!-- 切面类交给IOC容器实例 -->
    <bean id="methodAspect" class="com.text.aspect.MethodAspect"/>
    <!-- aop配置-->
    <aop:config>
        <!-- 切点配置,expression表示com.text所有包及子包项下所有服务层(以DaoImpl结束的类)的所有方法-->
        <aop:pointcut id="pointcut" expression="execution(public * com.text..*DaoImpl.*(..))"/>
        <!-- 切面配置-->
        <aop:aspect ref="methodAspect">
            <!-- 自定义的环绕通知-->
            <aop:around method="countMethodInvokeTime" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>

2、服务类及方法(pointcut)

package com.text.dao.impl;
import com.text.dao.StudentDao;

public class StudentDaoImpl implements StudentDao {

    @Override
    public void getById(String id) throws Exception {
        Thread.sleep(1000);
        System.out.println("查询学生id=" + id + "的信息");
    }
}

3、切面类(aspect) 

package com.text.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

import java.util.Date;

/**
 * 定义方法切面类
 */
public class MethodAspect {
    public void countMethodInvokeTime(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("目标方法执行之前记录初始时间...");
        Date startTime = new Date();
        try {
            proceedingJoinPoint.proceed();//执行目标方法 即:StudentDaoImpl.getById方法
            System.out.println("目标方法执行之后记录结束时间...");
            String methodName = proceedingJoinPoint.getTarget().getClass().getName() + "." +
                    proceedingJoinPoint.getSignature().getName();
            Date endTime = new Date();
            System.out.println(methodName + "方法执行总时长为:" + (endTime.getTime() - startTime.getTime()) + "毫秒");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

4、测试类 Application

package com.text;

import com.text.dao.StudentDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        StudentDao studentDao = context.getBean("studentDao", StudentDao.class);
        studentDao.getById("1");
        System.out.println("======getById end========");
    }
}

5、输出结果:


http://www.kler.cn/a/321236.html

相关文章:

  • 华为数字化转型的本质为何是管理变革
  • 基于 PyTorch 从零手搓一个GPT Transformer 对话大模型
  • 如何修改npm包
  • 使用--log-file保存pytest的运行日志
  • redis linux 安装
  • ‘视’不可挡:OAK相机助力无人机智控飞行!
  • 【IEEE 独立出版,快速EI检索】第四届人工智能、虚拟现实与可视化国际学术会议(AIVRV 2024)
  • 【编程基础知识】Cookie、Session和JWT(JSON Web Token)
  • Linux 学习 awk 和sed 命令使用
  • 欧洲欧盟药品数据库:EMA、HMA、EDQM-一键查询
  • WEB 编程:富文本编辑器 Quill 配合 Pico.css 样式被影响的问题之Shadow DOM
  • PostgreSQL 向量数据存储指南
  • 即梦PixelDance:从追赶到领跑,一跃成为全球AI竞赛的领航者!
  • 付费计量系统的标准化框架(下)
  • PCIe扫盲(14)
  • 树莓派基础命令
  • Keil5安装arm和C51共存环境
  • SSC338D/SSC338Q CA7*2+IPU5M/Multi-sensorISP: HDR/3DNR
  • 一键转换:Python如何轻松将PPT转换为PDF
  • Spring(三)Spring事件+计划定时任务+条件注解+SpringAware
  • 详细解释在Android开发中如何实现自定义View
  • Vue.js入门
  • “AI+Security”系列第3期(二):AI赋能自动化渗透测试
  • GPT实现联网,NextChat插件的配置说明
  • MySQL高阶1853-转换日期格式
  • 手机也可以更换任意IP地址吗?