基于Spring3.0实现AOP的小案例

news2024/9/29 18:07:46

前言

AOP(Aspect Oriented Programming)即面向切面编程,是一种通过预编译方式和运行期间动态代理实现程序功能统一维护的技术。针对功能增强的描述,可以理解为:“AOP允许在不修改源代码的情况下,通过定义切面(Aspect)和切入点(Pointcut),对目标对象的方法进行功能增强。”

具体来说,AOP能够将业务逻辑中的横切关注点(如日志、安全、事务等)与核心业务逻辑分离,从而降低代码间的耦合度,提高程序的可重用性和开发效率。在Spring框架中,AOP提供了多种增强类型,如前置增强、后置增强、环绕增强、异常抛出增强和引介增强等,这些增强类型可以灵活应用于不同的业务场景,以满足不同的功能需求。

 案例分析

实现手机购买和售卖的案例,利用AOP添加日志功能,显示当前时间等信息。

案例实现

 第一步:创建maven项目,导入相关依赖

pom.xml

这是spring3.0使用AOP的相关依赖,如果使用的是spring5.0以上,导包可能没有这么多,这是我的spring3.0 AOP的最佳实践

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.6.11</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>

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

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.1</version>
    </dependency>

第二步:创建service层业务,创建接口和实现类完成基本功能

 

PhoneBiz

public interface PhoneBiz {

    void buyPhone(int num);

    void salePhone(int num) throws OutOfStackException;
}

PhoneBizImpl

public class PhoneBizImpl implements PhoneBiz {
    int num;  //库存
    @Override
    public void buyPhone(int num) {
        System.out.println("手机进货,数量为"+num+"部");
        this.num+=num;
    }

    @Override
    public void salePhone(int num) throws OutOfStackException {
     
        System.out.println("手机销售,数量为"+num+"部");
    }
}

第三步:创建LogAspect切面类和spring的配置文件spring.xml

LogAspect

完成前置通知,在原始方法执行前进行日志输出

public class LogAspect {

    public String currTime(){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        return sdf.format(new Date());
    }

    /**
     * 前置通知
     * @param jp
     */
    public void before(JoinPoint jp){
        //获得请求方法的参数
        Object[] args= jp.getArgs();
        //获得请求方法名
        String methodName= jp.getSignature().getName();

        if ("buyPhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",即将开始进货操作,数量为"+args[0]+"部");
        }
        if ("salePhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",即将开始销售操作,数量为"+args[0]+"部");
        }
    }

spring.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:p="http://www.springframework.org/schema/p"
       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-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


    <bean id="phoneBiz" class="com.by.service.PhoneBizImpl"></bean>

    <bean id="logAspectBean" class="com.by.util.LogAspect"></bean>

    <aop:config proxy-target-class="true">
        <aop:pointcut id="p1" expression="execution( void *Phone(int))"/>
        <aop:aspect id="logAspect" ref="logAspectBean">
            <aop:before method="before" pointcut-ref="p1"/>
        </aop:aspect>
    </aop:config>


</beans>

 第四步:分别完成最终通知,后置通知,异常通知,环绕通知

LogAspect

 /**
     * 后置通知
     * @param jp
     */
    public void afterReturing(JoinPoint jp){
        //获得请求方法的参数
        Object[] args= jp.getArgs();
        //获得请求方法名
        String methodName= jp.getSignature().getName();

        if ("buyPhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",进货操作结束,数量为"+args[0]+"部");
        }
        if ("salePhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",销售操作结束,数量为"+args[0]+"部");
        }
    }

    /**
     * 异常通知
     * @param jp
     * @param e
     */
    public void afterThrowing(JoinPoint jp,OutOfStackException e){
        String methodName= jp.getSignature().getName();
        System.out.println(currTime()+":"+methodName+"发生异常:"+e.getMessage());

    }

    /**
     * 后置通知,发生异常也会执行
     * @param jp
     */
    public void after(JoinPoint jp){
        String methodName= jp.getSignature().getName();

        if ("buyPhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",进货操作完毕,发生异常也会执行");
        }
        if ("salePhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",销售操作完毕,发生异常也会执行");
        }
    }

    /**
     * 环绕通知
     * @param pjp
     * @return
     * @throws Throwable
     */
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        String methodName=  pjp.getSignature().getName();
        System.out.println(methodName+"方法开始执行:");
        long begin= System.currentTimeMillis();

        try {
            return pjp.proceed();
        } finally {
            long end= System.currentTimeMillis();
            System.out.println(methodName+"方法执行完毕,耗时"+(end-begin)+"毫秒");
        }

    }


    public String currTime(){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        return sdf.format(new Date());
    }

spring.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:p="http://www.springframework.org/schema/p"
       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-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


    <bean id="phoneBiz" class="com.by.service.PhoneBizImpl"></bean>

    <bean id="logAspectBean" class="com.by.util.LogAspect"></bean>

    <aop:config proxy-target-class="true">
        <aop:pointcut id="p1" expression="execution( void *Phone(int))"/>
        <aop:aspect id="logAspect" ref="logAspectBean">
            <aop:before method="before" pointcut-ref="p1"/>
            <aop:after-returning method="afterReturing" pointcut-ref="p1"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="p1" throwing="e"/>
            <aop:after method="after" pointcut-ref="p1"/>
            <aop:around method="around" pointcut-ref="p1"/>
        </aop:aspect>
    </aop:config>


</beans>

注意:如果要完成异常通知,需要预先自定义一个异常类。

package com.by.util;

/**
 * 库存异常类
 */
public class OutOfStackException extends Exception {

    public OutOfStackException(String message) {
        super(message);
    }
}

在PhoneBizImpl中新增判断语句

  @Override
    public void salePhone(int num) throws OutOfStackException {
        if (this.num<num){
            throw  new OutOfStackException("库存不足,需要"+num+"部,只有"+this.num+"部");
        }
        System.out.println("手机销售,数量为"+num+"部");
    }

 

第五步:将配置文件配置AOP的操作改为注解实现

将spring.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:p="http://www.springframework.org/schema/p"
       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-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <context:component-scan base-package="com.by"/>
    <!--启用注解aop-->
    <aop:aspectj-autoproxy/>


<!--    <bean id="phoneBiz" class="com.by.service.PhoneBizImpl"></bean>-->

<!--    <bean id="logAspectBean" class="com.by.util.LogAspect"></bean>-->

<!--    <aop:config proxy-target-class="true">-->
<!--        <aop:pointcut id="p1" expression="execution( void *Phone(int))"/>-->
<!--        <aop:aspect id="logAspect" ref="logAspectBean">-->
<!--            <aop:before method="before" pointcut-ref="p1"/>-->
<!--            <aop:after-returning method="afterReturing" pointcut-ref="p1"/>-->
<!--            <aop:after-throwing method="afterThrowing" pointcut-ref="p1" throwing="e"/>-->
<!--            <aop:after method="after" pointcut-ref="p1"/>-->
<!--            <aop:around method="around" pointcut-ref="p1"/>-->
<!--        </aop:aspect>-->
<!--    </aop:config>-->




</beans>

 在PhoneBizImpl实现类中:

@Component(value = "phoneBiz")
public class PhoneBizImpl implements PhoneBiz {
...}

 在LogAspect中使用注解:

/**
 * 通知类
 */
@Aspect
@Component
public class LogAspect {

    /**
     * 前置通知
     * @param jp
     */
    @Before("p1()")
    public void before(JoinPoint jp){
        //获得请求方法的参数
        Object[] args= jp.getArgs();
        //获得请求方法名
        String methodName= jp.getSignature().getName();

        if ("buyPhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",即将开始进货操作,数量为"+args[0]+"部");
        }
        if ("salePhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",即将开始销售操作,数量为"+args[0]+"部");
        }
    }

    /**
     * 后置通知
     * @param jp
     */
    @AfterReturning("execution( void *Phone(int))")
    public void afterReturing(JoinPoint jp){
        //获得请求方法的参数
        Object[] args= jp.getArgs();
        //获得请求方法名
        String methodName= jp.getSignature().getName();

        if ("buyPhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",进货操作结束,数量为"+args[0]+"部");
        }
        if ("salePhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",销售操作结束,数量为"+args[0]+"部");
        }
    }

    /**
     * 异常通知
     * @param jp
     * @param e
     */
    @AfterThrowing(pointcut = "execution( void *Phone(int))",throwing = "e")
    public void afterThrowing(JoinPoint jp,OutOfStackException e){
        String methodName= jp.getSignature().getName();
        System.out.println(currTime()+":"+methodName+"发生异常:"+e.getMessage());

    }

    /**
     * 后置通知,发生异常也会执行
     * @param jp
     */
    @After("execution( void *Phone(int))")
    public void after(JoinPoint jp){
        String methodName= jp.getSignature().getName();

        if ("buyPhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",进货操作完毕,发生异常也会执行");
        }
        if ("salePhone".equals(methodName)){
            System.out.println("日志:"+currTime()+",销售操作完毕,发生异常也会执行");
        }
    }

    /**
     * 环绕通知
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("execution( void *Phone(int))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        String methodName=  pjp.getSignature().getName();
        System.out.println(methodName+"方法开始执行:");
        long begin= System.currentTimeMillis();

        try {
            return pjp.proceed();
        } finally {
            long end= System.currentTimeMillis();
            System.out.println(methodName+"方法执行完毕,耗时"+(end-begin)+"毫秒");
        }

    }

    @Pointcut("execution( void *Phone(int))")
    public void p1(){}



    public String currTime(){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        return sdf.format(new Date());
    }
}

总结

以上是基于spring3.0使用AOP实现日志记录功能的小案例

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

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

相关文章

Java异步编程:从入门到精通

在现代编程实践中&#xff0c;异步编程已成为提升程序性能和用户体验的关键技术。Java&#xff0c;作为一种成熟且广泛使用的编程语言&#xff0c;提供了多种实现异步编程的方法。本文将带你从异步编程的基础知识入手&#xff0c;逐步深入到Java中的异步编程实践。 异步编程简介…

SQL常用数据过滤 - EXISTS运算符

SQL查询中的EXISTS运算符用于检查查询子句是否存在满足特定条件的记录&#xff0c;如果有一条或者多条记录存在&#xff0c;则返回True&#xff0c;否则返回False。 语法结构 SELECT column_name(s)FROM table_nameWHERE EXISTS(SELECT column_name FROM table_name WHERE co…

基于两分支卷积和 Transformer 的轻量级多尺度特征融合超分辨率网络 !

当前的单图像超分辨率&#xff08;SISR&#xff09;算法有两种主要的深度学习模型&#xff0c;一种是基于卷积神经网络&#xff08;CNN&#xff09;的模型&#xff0c;另一种是基于Transformer的模型。前者利用不同卷积核大小的卷积层堆叠来设计模型&#xff0c;使得模型能够更…

SOLID原则:现代软件架构的永恒基石

关注TechLead&#xff0c;复旦博士&#xff0c;分享云服务领域全维度开发技术。拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;复旦机器人智能实验室成员&#xff0c;国家级大学生赛事评审专家&#xff0c;发表多篇SCI核心期刊学术论文&#xff0c;阿里云认…

面积开运算bwareaopen

一个非常有用的二值图像形态学后处理算法&#xff0c;建立在连通分量分析的基础之上。 bwareaopen 从二值图像中删除小对象 语法 BW2 bwareaopen(BW,P) BW2 bwareaopen(BW,P,conn) 说明 BW2 bwareaopen(BW,P) 从二值图像 BW 中删除少于 P 个像素的所有连通分量&#x…

docker简介、安装、基础知识

基础知识 Docker简介&#xff1a; 1.Docker是一种用于构建、发布及运行应用程序的开源项目&#xff0c;他通过容器化技术简化了应用程序的部署和管理 2.Docker是一个开源的应用容器引擎&#xff0c;基于go语言开发&#xff0c;为应用打包、部署平台&#xff0c;而非单纯的虚…

计算机毕业设计 办公用品管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

C++(Qt)软件调试---内存调试器Dr.Memory(21)

C(Qt)软件调试—内存调试器Dr. Memory&#xff08;21&#xff09; 文章目录 C(Qt)软件调试---内存调试器Dr. Memory&#xff08;21&#xff09;[toc]1、概述&#x1f41c;2、安装Dr.Memory&#x1fab2;3、命令行使用Dr.Memory&#x1f997;4、Qt Creator集成使用Dr.Memory&…

预计OpenAI在新一轮融资后估值可达1500亿美元!Hugging Face平台托管模型数量破100万|AI日报

文章推荐 HuggingChat macOS版正式发布&#xff01;文章内附体验地址&#xff01;我国打造糖尿病专用AI模型&#xff5c;AI日报 今日热点 OpenAI在新一轮融资后估值可能达到1500亿美元 知情人士表示&#xff0c;Thrive Capital将在OpenAI目前的65亿美元融资轮中投资超过10亿…

如何高效管理知识产权全链条?

为了有效保护企业的创新成果&#xff0c;确保技术创意的顺利转化&#xff0c;以及高效管理知识产权案件&#xff0c;建立一套完善的知识产权管理体系至关重要。对于企业而言&#xff0c;如何有效地管理知识产权的各个环节&#xff0c;从研发项目到技术创意&#xff0c;再到提案…

排序(交换排序:快排)

快速排序&#xff1a; 写快排的注意事项 1.单趟排序hoare 2.不写优化只说优化就行 理想的情况下&#xff1a;每次排序都是二分&#xff0c;直到二分到最后&#xff0c;那就相当于递归高度次(logN)&#xff0c;每一层单趟排都是O(N)&#xff0c;时间复杂度O(NlogN) 空间复杂度就…

PHP程离禁用一段IP的写法示例

PHP程离禁用一段IP的写法示例 。 在PHP中&#xff0c;如果你想禁用一段IP地址的访问&#xff0c;你可以使用$_SERVER[REMOTE_ADDR]来获取访问者的IP地址&#xff0c;然后通过判断IP地址是否在你想要禁用的范围内来决定是否拒绝服务。 以下是一个简单的例子&#xff0c;展示了…

net Core aspx视图引擎 razor视图引擎

视图引擎 》》定义&#xff0c;什么是视图引擎 视图引擎就是&#xff0c;将服务器端模板转换为HTML标记&#xff0c;并在控制器的操作方法触发时在web浏览器中呈现 现在都推荐 Razor视图引擎了&#xff08;也是默认视图引擎&#xff09;&#xff0c;aspx引擎不推荐了。 ASPX …

AI新掌舵:智享AI直播系统:直播界的新浪潮还是真人主播的终结者?

AI新掌舵&#xff1a;智享AI直播系统&#xff1a;直播界的新浪潮还是真人主播的终结者&#xff1f; 在数字化浪潮的汹涌澎湃中&#xff0c;人工智能&#xff08;AI&#xff09;以其前所未有的速度渗透至各行各业&#xff0c;其中&#xff0c;直播领域正经历着一场前所未有的变革…

javascript:冻结对象

1 作用 冻结一个对象&#xff0c;使对象不可扩展。 2 特性 对象的属性不可再被新增、删除对象的属性的值不可再被修改对象的属性的描述符中任意配置项都不可被重新定义 3 代码示例 3.1 冻结对象 Object.freeze() 代码如下&#xff1a; use strict let initialData {a: 1…

C#案例 | 基于C#语言在Excel中进行二次开发(一):简单系统搭建:打印输出“Hello Excel C#”

基于C#语言在Excel中进行二次开发&#xff08;一&#xff09;&#xff1a;简单系统搭建&#xff1a;打印输出”Hello Excel & C#” 实现效果第一步&#xff1a;前期准备第二步&#xff1a;打开VS 2022&#xff0c;创建项目第三步&#xff1a;程序界面设计 实现效果 在Exce…

【大模型】通俗解读变分自编码器VAE

目录 写在前面 一、VAE结构 二、损失函数 三、代码实现 1.训练代码 2.推理生成图片 3.插值编辑图片 四、总结 写在前面 论文地址&#xff1a;https://arxiv.org/abs/1312.6114 大模型已经有了突破性的进展&#xff0c;图文的生成质量都越来越高&#xff0c;可控性也越来…

cesium渲染的3Dtiles的模型下载!glb模型文件下载!

前端开发测试或者学习cesium的时候最难最麻烦就是找到一个合适的模型&#xff0c;现在就直接给各位放几个可以满足我们测试使用的模型文件。 模型文件下载—香港3DTiles模型文件 某盘 通过百度网盘分享的文件&#xff1a;hk-效果图.png&#xff0c;hk.zip等2个文件 链接&…

react中的ref三种形式

1&#xff0c;字符串形式 <!-- 创建盒子 --><div id"test"></div> <script type"text/babel">class Demo extends React.Component{render(){return(<div><input type"text" refinput1 /><button onCl…

奔驰EQS450suv升级增强AR抬头显示HUD案例分享

以下是奔驰 EQS450 SUV 升级增强版 AR 抬头显示的一般改装案例步骤及相关信息&#xff1a; 配件&#xff1a;通常包括显示屏、仪表模块、饰板等。 安装步骤&#xff1a; 1. 拆下中控的仪表。 2. 在仪表上预留位置切割出合适的孔位&#xff0c;用于安装显示器。 3. 将显示器…