Spring中Aop及日志通知

目录

一、Aop介绍

( 1 ) 是什么

( 2 ) 作用

( 3 ) 概念和机制

( 4 ) 特点 

二、日志通知

( 1 ) 前置通知 

( 2 ) 后置通知

( 3 ) 环绕通知

( 4 ) 异常通知

( 5 ) 过滤通知

给我们带来收获


一、Aop介绍

( 1 ) 是什么

AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要特性。AOP通过将与核心业务逻辑无关的横切关注点(如事务管理、日志记录、安全性检查等)独立出来,从而实现了代码的模块化和重用。

在传统的面向对象编程中,通常通过将功能逻辑嵌入到核心业务逻辑中来实现某些功能。然而,这种方式会导致代码的重复和耦合,使得应用程序的维护和扩展变得困难。

而AOP的思想是将这些横切关注点与核心业务逻辑进行解耦。在Spring框架中,AOP通过切面(Aspect)来实现。切面是一个跨越多个类的模块化单元,它定义了横切关注点的行为。

在Spring中,通过使用AOP,你可以将横切关注点抽象为一个切面,并将其应用到多个类或方法上。当程序执行到被切入的方法时,切面的代码将自动执行,完成预定义的功能。

( 2 ) 作用

AOP在Spring框架中具有以下几个作用:

  • 1. 代码解耦:AOP通过将横切关注点(如日志记录、事务管理、安全性检查等)与核心业务逻辑进行解耦,将这些功能从具体的业务代码中抽离出来。这样可以使业务代码更加清晰和简洁,减少了代码的重复和耦合。
  • 2. 横切关注点的集中处理:通过定义切面,将多个类中的相同横切关注点集中处理。例如,多个方法需要进行日志记录,可以将日志记录的代码逻辑抽取到切面中,将切面应用到需要的方法上。这样,无需在每个方法中重复编写日志记录的代码,提高了代码的重用性和可维护性。
  • 3. 代码重用与维护:AOP可以将横切关注点作为独立的模块进行开发和维护。这些模块可以在多个不同的业务场景中重用,减少了开发和维护的工作量。例如,将事务管理的逻辑抽取为切面,可以应用到不同的方法或类中,实现统一的事务管理,提高了代码的复用性和可维护性。
  • 4. 增强系统功能:通过AOP可以增强系统的功能,如安全性检查、性能监控、缓存管理等。这些非功能性需求可以通过切面的方式统一添加到系统中,而不需要修改核心的业务逻辑。这样可以更灵活地对系统进行功能的增强和定制。
  • 5. 可插拔式框架扩展:AOP可以为框架提供可插拔式的扩展能力。例如,Spring框架本身就使用AOP来实现事务管理、缓存管理等功能。开发人员可以基于Spring的AOP机制,为自己的框架增加新的功能,实现可插拔式的框架扩展。
  • 6. 代码的可测试性:AOP可以使代码更易于进行单元测试和集成测试。通过将横切关注点抽取为切面,可以在测试过程中更方便地对关注点进行模拟或验证。这样可以提高测试的覆盖率和测试代码的可读性。

总而言之,AOP在Spring框架中的作用是通过解耦、集中处理横切关注点、增强系统功能、提供框架扩展能力等方面,使得代码更具重用性、可维护性和灵活性。它使开发人员可以更好地管理非核心功能,提高开发效率和系统的可靠性。 

( 3 ) 概念和机制

在Spring框架中,AOP的主要概念和机制包括切面(Aspect)、切点(Pointcut)、通知(Advice)、织入(Weaving)和引入(Introduction)等。

  • 1. 切面(Aspect):切面是横切关注点的模块化单元,它由切点和通知组成。切面定义了在特定切点插入的代码逻辑,即在何处和何时执行通知。
  • 2. 切点(Pointcut):切点用于定义切面将会生效的具体位置。通过切点表达式,可以选择需要被切入的方法或类。例如,通过表达式"execution(* com.example.service.*.*(..))"可以选择所有com.example.service包下的方法。
  • 3. 通知(Advice):通知是切面内部的代码逻辑,它决定了在切点何处执行切面的逻辑。Spring定义了多种通知类型,包括:

  - 前置通知(Before):在切点之前的代码逻辑执行。

  - 后置通知(After):在切点之后的代码逻辑执行(无论目标方法是否发生异常)。

  - 返回通知(After-returning):在切点之后的代码逻辑执行(只有目标方法正常返回时)。

  - 异常通知(After-throwing):在切点之后的代码逻辑执行(只有目标方法发生异常时)。

  - 环绕通知(Around):在切点之前和之后的代码逻辑执行,可以完全控制目标方法的执行。

  - 过滤通知(Filtering advice)是一种特殊类型的通知,它用于过滤切点上要执行的切面逻辑。过滤通知可以控制切面是否应该在特定的条件下执行

  • 4. 织入(Weaving):织入是将切面与目标对象的代码进行整合的过程。在Spring中,有两种织入方式:

   - 编译时织入(Compile-time weaving):在编译阶段,将切面织入目标类的字节码中。
   - 运行时织入(Runtime weaving):在运行时,动态地将切面织入目标对象中。

  • 5. 引入(Introduction):引入是一种特殊的通知类型,它允许为目标对象添加新的方法或属性,而不需要修改目标对象的源码。通过引入,可以在不改变类继承关系的情况下,为目标对象添加额外的功能。
  • 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出.
  • 目标(Target):被通知(被代理)的对象
  • 代理(Proxy):将通知应用到目标对象后创建的对象(代理=目标+通知)
  • 切入点(Pointcut):多个连接点的集合,定义了通知应该应用到那些连接点。
  • 适配器(Advisor):适配器=通知(Advice)+切入点(Pointcut)

总结小知识 : 

以上这些概念和机制一起协作,通过配置来实现AOP的功能。在Spring中,通过使用XML配置、注解或Java配置来定义切面、切点、通知等,并将切面应用到目标对象上。Spring框架在运行时会自动识别切点,并按照配置的通知类型执行切面的逻辑。

总而言之,Spring框架的AOP通过切面、切点、通知等概念和机制,提供了一种机制来管理横切关注点的代码逻辑。这使得开发人员能够在不修改核心业务逻辑的情况下,实现横切关注点的复用和集中处理,提高了代码的模块化、重用性和可维护性。

  ( 4 ) 特点 

AOP(Aspect-Oriented Programming)是一种编程范式,具有以下几个特点:

  • 1. 横切关注点:AOP通过横切关注点的概念,将与核心业务逻辑无关的横切功能(如日志记录、事务管理、安全性检查等)从业务代码中分离出来,形成独立的模块。这样可以实现功能的重用和集中管理,减少了代码冗余和耦合。
  • 2. 切面模块化:AOP将横切关注点抽象为切面,切面是横切关注点的模块化单元,它包含了切点和通知。切点表示切面将会生效的具体位置,而通知表示在切点处执行的代码逻辑。通过切面的定义和组织,可以更好地管理横切关注点,并灵活地将切面应用到不同的目标对象上。
  • 3. 代码解耦和重用:AOP的一个主要目标是解耦,通过将横切关注点与核心业务逻辑进行解耦,可以减少代码间的耦合度。切面可以在多个类和方法中复用,使得横切功能可以统一管理和维护。这样可以提高代码的重用性,减少代码的冗余,同时也使得业务逻辑更加清晰和简洁。
  • 4. 面向切面编程:AOP是一种面向切面的编程范式,它允许开发人员通过定义切面来增强系统功能,而无需修改核心业务逻辑。通过切面的织入,可以在不修改原有代码的情况下,实现对系统的功能增强和定制。
  • 5. 运行时动态代理:AOP通常在运行时对目标对象进行动态代理,即在程序运行时动态地将切面的逻辑织入到目标对象中。这样可以灵活地控制切面的应用范围,并在运行时根据需要进行切换。Spring框架通过动态代理技术实现AOP,提供了对JDK动态代理和CGLIB动态代理的支持。
  • 6. 增强系统功能:AOP可以为系统增加额外的功能,如安全性检查、性能监控、缓存管理等。这些功能可以作为切面的通知部分,通过配置的方式将其应用到目标对象上。通过AOP的方式增强系统功能,不仅可以避免在每个方法中重复编写这些功能逻辑,而且还可以将其与核心业务逻辑分离,提高了代码的可维护性和可测试性。

总而言之,AOP在软件开发中具有横切关注点、切面模块化、代码解耦和重用、面向切面编程、运行时动态代理以及增强系统功能等特点。它能够提高代码的模块化程度、可维护性和灵活性,帮助开发人员更好地管理和扩展横切关注点的代码逻辑。

二、日志通知

首先根据以下项目结构创建项目的接口,类,配置文件(可以根据自己的习惯进行更改) 

Spring中Aop及日志通知_第1张图片

  IBookBiz 接口

package com.CloudJun.aop.biz.impl;

/**
 * @author CloudJun
 * @create  2023-08-17 14:20
 */

public interface IBookBiz {
    // 购书
    public boolean buy(String userName, String bookName, Double price);

    // 发表书评
    public void comment(String userName, String comments);
}

在exception包内创建一个PriceException的一次类

package com.CloudJun.aop.exception;

/**
 * @author CloudJun
 * @create  2023-08-17 14:26
 * 异常类
 */
public class PriceException extends RuntimeException {

	public PriceException() {
		super();
	}

	public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public PriceException(String message, Throwable cause) {
		super(message, cause);
	}

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

	public PriceException(Throwable cause) {
		super(cause);
	}
	
}

 BookBizImpl 接口实现类

package com.CloudJun.aop.biz.impl;

import com.CloudJun.aop.exception.PriceException;

/**
 * @author CloudJun
 * @create  2023-08-17 14:22
 */
public class BookBizImpl implements IBookBiz {

    public BookBizImpl() {
        super();
    }

    public boolean buy(String userName, String bookName, Double price) {
        // 通过控制台的输出方式模拟购书
        if (null == price || price <= 0) {
            throw new PriceException("book price exception");
        }
        System.out.println(userName + " buy " + bookName + ", spend " + price);
        return true;
    }

    public void comment(String userName, String comments) {
        // 通过控制台的输出方式模拟发表书评
        System.out.println(userName + " say:" + comments);
    }

}

 spring-aop.xml 配置文件





    

    


    


创建完以接口及类后可能还会出现方法报错及导包报错,查看了项目结构对比导包确实没有问题的情况下,进行以下操作方可恢复: 

选中 File 在 点击Settings 查询Java Compiler后选中,如图操作:

Spring中Aop及日志通知_第2张图片Spring中Aop及日志通知_第3张图片

Spring中Aop及日志通知_第4张图片

Demo 测试类 (测试类中字符串纯属为了搞笑,没有其他意思,嘿嘿嘿。。。)

package com.CloudJun.aop.demo;

import com.CloudJun.aop.biz.impl.IBookBiz;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author CloudJun
 * @create  2023-08-17 14:26
 */
public class Demo {

    public static void main(String[] args) {

        //加载spring核心配置文件(建模),获取spring上下文对象及上下文对象中可以获取任何javabean的对象
        ClassPathXmlApplicationContext context  =  new ClassPathXmlApplicationContext("/spring-aop.xml");

        IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz");
        
        bookBiz.buy("坤坤","金鸡读立图",19.9);
        bookBiz.comment("坤坤","让我看出坤拳");

    }

}

测 试 结 果  :

Spring中Aop及日志通知_第5张图片

( 1 ) 前置通知 

要进行前置通知,在advice包内创建一个 MyMethodBeforeAdvice 前置通知类

package com.CloudJun.aop.advice;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * 买书、评论前加系统日志
 * @author CloudJun
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {

	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
//		在这里,可以获取到目标类的全路径及方法及方法参数,然后就可以将他们写到日志表里去
		String target = arg2.getClass().getName();
		String methodName = arg0.getName();
		String args = Arrays.toString(arg1);
		System.out.println("【前置通知:系统日志】:"+target+"."+methodName+"("+args+")被调用了");
	}

}

 在将配置文件修改为以下代码 ( spring-aop.xml )





    

    


    

        

        
            
                com.CloudJun.aop.biz.impl.IBookBiz
            
        

        
            
                myMethodBeforeAdvice
            

        

    

在测试类(Demo)中将

IBookBiz bookBiz = (IBookBiz) context.getBean("bookBiz");

这一行代码修改为

IBookBiz bookBiz = (IBookBiz) context.getBean("proxyFactoryBean");

如图:

Spring中Aop及日志通知_第6张图片  

再进行测试,开启服务测试代码,如果如图:

Spring中Aop及日志通知_第7张图片

            由图我们可看出在方法前会先执行前置通知(相当于进行了方法调用的记录)

( 2 ) 后置通知

在advice包内创建一个后置通知的类为 MyAfterReturningAdvice 的后置通知类

package com.CloudJun.aop.advice;

import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.aop.AfterReturningAdvice;

/**
 * 列如场景:买书返利
 * @author CloudJun
 *
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		String target = arg3.getClass().getName();
		String methodName = arg1.getName();
		String args = Arrays.toString(arg2);
		System.out.println("【后置通知:买书返利】:"+target+"."+methodName+"("+args+")被调用了,"+"该方法被调用后的返回值为:"+arg0);
	

	}

}

再在 spring-aop.xml 配置文件中增加通知配置即可





    

    
    

    

        

        
            
                com.CloudJun.aop.biz.impl.IBookBiz
            
        
        
        
            
                
                myMethodBeforeAdvice
                
                myAfterReturningAdvice
            

        

    

进行测试,结果为:

Spring中Aop及日志通知_第8张图片

 

           由图可以看出在调用方法后会执行后置通知(相当于进行了方法后的调用的记录)

( 3 ) 环绕通知

advice包内创建一个环绕通知的类为 MyMethodInterceptor 的环绕通知类

package com.CloudJun.aop.advice;

import java.util.Arrays;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 环绕通知
 * 	包含了前置和后置通知
 * @author CloudJun
 *
 */
public class MyMethodInterceptor implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		String target = arg0.getThis().getClass().getName();
		String methodName = arg0.getMethod().getName();
		String args = Arrays.toString(arg0.getArguments());
		System.out.println("【环绕通知调用前:】:"+target+"."+methodName+"("+args+")被调用了");
//		arg0.proceed()就是目标对象的方法
		Object proceed = arg0.proceed();
		System.out.println("【环绕通知调用后:】:该方法被调用后的返回值为:"+proceed);
		return proceed;
	}

}

再在 spring-aop.xml 配置文件中增加通知配置即可





    

    
    
    

    

        

        
            
                com.CloudJun.aop.biz.impl.IBookBiz
            
        
        
        
            
                
                myMethodBeforeAdvice
                
                myAfterReturningAdvice
                
                methodInterceptor
            

        

    

进行测试,结果为:

Spring中Aop及日志通知_第9张图片

由图可以看出在调用方法前后(环绕)都会执行环绕通知(相当于进行了方法前后的调用的记录)

( 4 ) 异常通知

advice包内创建一个环绕通知的类为 MyThrowsAdvice 的异常通知类

package com.CloudJun.aop.advice;

import com.CloudJun.aop.exception.PriceException;
import org.springframework.aop.ThrowsAdvice;
/**
 * 出现异常执行系统提示,然后进行处理。价格异常为例
 * @author CloudJun
 *
 */
public class MyThrowsAdvice implements ThrowsAdvice {
	public void afterThrowing(PriceException ex) {
		System.out.println("【异常通知】:当价格发生异常,那么执行此处代码块!!!");
	}
}

注意 : 在改通知类中  afterThrowing 方法名称是不能修改的,必须是方法名为afterThrowing

再在 spring-aop.xml 配置文件中增加通知配置即可 





    

    
    
    
    


    

        

        
            
                com.CloudJun.aop.biz.impl.IBookBiz
            
        
        
        
            
                
                myMethodBeforeAdvice
                
                myAfterReturningAdvice
                
                methodInterceptor
                
                myThrowsAdvice
            

        

    

 为了演示错误,我们将测试类中的bookBiz.buy("坤坤","金鸡读立图",19.9);

修改为

bookBiz.buy("坤坤","金鸡读立图",-19.9);

Spring中Aop及日志通知_第10张图片 

进行测试,结果为:

Spring中Aop及日志通知_第11张图片

 

在执行代码出现异常时给予提示,说明代码的问题所在,还可以在异常类(MyThrowsAdvice )

中的afterThrowing方法里面直接进行异常的处理,让代码执行不进行报错。

( 5 ) 过滤通知

将Demo测试类中的bookBiz.buy("坤坤","金鸡读立图",-19.9);修改回原来的代码。

以下代码:

bookBiz.buy("坤坤","金鸡读立图",19.9);

要求: 后置通知必须要在买完书之后才会出现通知(买书返利)。

只需要修改spring-aop.xml 配置文件即可 





    

    
    
    
    

    
        
        
        
        
    

    

        

        
            
                com.CloudJun.aop.biz.impl.IBookBiz
            
        
        
        
            
                
                myMethodBeforeAdvice


                
                advisor
                
                methodInterceptor
                
                myThrowsAdvice
            

        

    

进行测试,结果为:

Spring中Aop及日志通知_第12张图片

 

由图中可以看出只有买书的方法有后置通知(买书返利)

而给书的评论的方法就没有后置通知(买书返利)

说明已经进行了筛选

给我们带来收获

学习Spring中的AOP(面向切面编程)可以带来以下收获:

  1. 模块化:AOP允许将横切关注点(例如日志记录、事务管理等)从核心业务逻辑中分离出来,将其作为一个独立的模块进行开发和维护。这样可以提高代码的可读性和可维护性,使系统更加模块化和灵活。

  2. 代码重用:AOP通过将通用的横切关注点抽象为切面,并将其应用到多个不同的类和模块中,可以实现代码的重用。这样可以减少重复的代码编写,提高开发效率。

  3. 面向横切关注点编程:AOP将关注点从传统的面向对象编程的纵向结构转变为横向结构,使得系统的结构更加清晰。通过将横切关注点抽象为切面,可以将其应用到系统的多个模块中,提高系统的可扩展性和可维护性。

  4. 提高系统的性能:AOP可以在运行时动态地将切面织入到目标对象中,从而实现对目标对象的增强。例如,可以通过AOP实现缓存、事务管理等功能,从而提高系统的性能和可靠性。

  5. 实现安全性控制:AOP可以用于实现安全性控制,通过在切面中定义安全性的规则和权限检查,可以对系统的访问进行控制。这样可以提高系统的安全性,防止未授权的访问和恶意操作。

  6. 日志记录和调试:AOP可以用于实现日志记录和调试功能,通过在切面中定义日志记录的规则和操作,可以自动记录系统的运行日志和调试信息。这样可以方便排查和解决问题,提高系统的可靠性和可维护性。

  7. 提供性能监控:AOP可以用于实现性能监控,通过在切面中定义性能监控的规则和操作,可以自动收集系统的性能数据和指标。这样可以及时发现和解决性能问题,提高系统的性能和响应能力。

总结来说,学习Spring中的AOP可以帮助我们解耦业务逻辑、提供声明式事务管理、实现安全性控制、日志记录和调试以及性能监控等功能。这些收获可以提高系统的可维护性、可测试性、安全性和性能,使我们的开发工作更加高效和稳定。

                                        Spring中Aop及日志通知_第13张图片

你可能感兴趣的:(spring,java,后端,tomcat,maven,intellij-idea)