Spring AOP(xml配置)(注解配置)

本文详细介绍了AOP(面向切面编程)的基本概念,包括应用场景、横向切割与纵向切割、AOP中的Maven依赖(AspectJ)、XML配置中的通知类型和切入点,以及如何在Spring框架中使用AOP注解进行代理和通知配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.引入

1.应用场景

AOP的功能主要是为了拦截或者实现日志安全检查等一些检查的工作,aop也常用于事务管理,防止垃圾数据进入数据库.

2.横向切割和纵向切割

  • AOP采用的是横向切割技术—>在不修改源代码的情况下完成添加功能的技术
  • 纵向切割技术—>在修改源代码的情况下完成功能的操作

AOP名词的基础概念

AOP名词概念参考

  • 通知—>实现横向切割技术的方式(通知定义了何时,做什么.)

      @Before			前置通知--->通知方法会在目标方法调用之前执行
      @AfterReturning	后置通知--->通知方法会在目标方法返回后调用
      @AfterThrowing	异常通知--->通知方法会将目标方法封装起来
      @Around			环绕通知--->通知方法会将目标方法封装起来
      @After			最终通知--->通知方法会在目标方法返回或抛出异常后调用
    
  • 连接点(JoinPoint)

      连接点是应用执行过程中能够插入切面(Aspect)的一个点.这些点可以是调用方法时,甚至修改一个字段时.
      它是一个虚拟的概念,例如坐地铁的时候,每一个站都可以下车,那么这里每一个站都是一个接入点.
      假如一个对象中有多个方法,那么这个每一个方法就是一个连接点.
    
  • 切入点(Pointcut)

      切入点是一些特殊的连接点,是具体附加通知的地方.
      例如,坐地铁的时候,具体在某个站下车,那这个站就是切入点
    
  • 切面(Aspect)

      切面是通知和切入点的结合,通知规定了在什么时机干什么事,切入点规定了在什么地方.
      例如,"在8点钟在西站下车"就是一个切面.
      那么时间8点,动作下车就是一个通知.西站就是一个切入点.
    

二.AOP中的Maven依赖—>aspectj

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.20.1</version>
<!--    这一行要去掉,不然写代码的时候不起作用    runtime的意思是运行的时候起作用,但是会导致写代码的时候不起作用,导包导不进去-->
<!--      <scope>runtime</scope>-->
    </dependency>

三.AOP使用(xml配置)

  • applicationContext.xml文件的配置

      <aop:config>
      	<aop:ponitcut id="pointcut的id名" expression="切入点表达式"/>
      	<aop:aspect ref="引入AOP代理对象">
      		<aop:通知方法 method="AOP代理对象中的方法" pointcut-ref="pointcut的id名"/>
      	</aop:aspect>
      </aop:config>
    
  • 代理对象

      public class 类名{
      ...	//代理对象的方法体
      }
    

代码实例

  • 业务类
package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class TargetService {
//    目标方法1:查询余额
    public void selectAccount(){
        System.out.println("模拟查询到用户余额为100");
//        为了测试异常通知-->手动设置一个异常
//        throw new RuntimeException("运行时异常");
    }

//    目标方法2
    public Integer add(Integer add1,Integer add2){
        return add1+add2;
    }

}

  • AOP处理类
package com.neuedu.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Service;

//AOP代理对象
@Service
public class AOPHandler {
    public void handlerBefore(){
        System.out.println("前置通知");
    }

    public void handlerBefore1(Integer add1,Integer add2){
        System.out.println("前置通知");
        System.out.println(add1);
        System.out.println(add2);

    }

    public void handlerAfter(){
        System.out.println("返回通知");
    }


    public void handlerAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前通知");
        System.out.println("环绕通知");
//        设置为执行代码--->用proceed()方法
        Object obj = pjp.proceed();
        System.out.println(obj);
        System.out.println("环绕后通知");
    }

    public void handlerExcep(){
        System.out.println("异常通知");
    }

    public void handlerFinally(){
        System.out.println("最终通知");
    }
}

  • applicationContext.xml文件配置
 <aop:config>
<!--        设置拦截(切入点)====>用切入点标识符 -->
        <aop:pointcut id="point" expression="execution(* com.neuedu.service.*.*(..))"/>
<!--        代理对象    引入代理对象AOPHandler-->
        <aop:aspect ref="AOPHandler">
<!--            设置通知方法==>before:前置通知    pointcut-ref的值应与上面point-cut的id值对应-->
            <aop:before method="handlerBefore" pointcut-ref="point"/>
<!--            通知方法==>after-returning:返回通知-->
            <aop:after-returning method="handlerAfter" pointcut-ref="point"/>
<!--            通知方法==>round:环绕通知-->
            <aop:around method="handlerAround" pointcut-ref="point"/>
<!--            通知方法==>after-throwing:异常通知-->
            <aop:after-throwing method="handlerExcep" pointcut-ref="point"/>
<!--            通知方法==>after:最终通知-->
            <aop:after method="handlerFinally" pointcut-ref="point"/>
            
<!--            有参数的-->
            <aop:before method="handlerBefore1" pointcut="execution(* com.neuedu.service.*.*(..)) and args(add1,add2)"/>
        </aop:aspect>
    </aop:config>
  • 测试类
import com.neuedu.service.TargetService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext.xml"})

public class AOPTest {

    @Autowired
    private TargetService targetService;
    @Test
    public void testTarget(){
        targetService.selectAccount();
        targetService.add(1,3);
    }
}

在这里插入图片描述

切入点标识符

	执行表达式的格式:
	execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(parampattern)throws-pattern?)
	
	切入点表达式的格式:
	注解? 修饰符? 返回值类型 类型声明?方法名(参数列表) 异常列表?
  • 其中,返回值类型,方法名,参数列表必填,其他都为可选

      参数列表:
      	"()"表示方法没有任何参数;
      	"(..)"表示匹配接受任意个参数方法
      
      切入点表达式通配符:
      	* : 匹配所有字符
      	.. : 一般用于匹配多个包,多个参数
      	+ : 表示类及其子类
    

Proceedingjoinpoint简述

proceedingjoinpoint简述参考
proceedingjoinpoint继承了JoinPoint,在JoinPoint的基础上暴露出proceed(),这个方法是AOP代理链执行的方法.
JoinPoint仅能获取相关参数,无法执行连接点.暴露出proceed()这个方法,就能支持aop:around这种切面(而其他的几种切面只需要用到JoinPoint,这跟切面的类型有关),就能控制走代理链还是走自己拦截的其他逻辑

四.AOP注解配置

  • 启动aop注解—>applicationContext.xml页面

      <!--    AOP注解配置 (推荐使用)-->
      <!--    启动aop注解-->
      <aop:aspectj-autoproxy proxy-target-class="true"/>
    
  • 目标类

      public class 类名(){
      	...		//方法体
      }
    
  • AOP处理注解类

      /**
       * @Component   该对象由Spring容器来创建管理
       * @Aspect  标识是一个AOP对象
       */
      @Component
      @Aspect
      public class ExeTimeAop {
      	 @Pointcut("execution(* com.neuedu.service.*..*(..))")
          public void anyMethod(){}
      
      		//无参数的前置通知
              @Before("anyMethod()")
              public void before(){...}
      		
      		//有参数的前置通知
      		@Before("anyMethod()&&args(add1,add2)")
     		public void beforeParam(Object add1,Object add2){...}
     		
     		//环绕通知
     		@Around("anyMethod()")
     		
     		//返回通知
     		 @AfterReturning(pointcut = "anyMethod()",returning = "result")
     		 
     		 //异常通知
     		  @AfterThrowing(pointcut = "anyMethod()",throwing = "ex")
     		  
     		  //最终通知
     		  @After("anyMethod()")
    

代码实例

  • 启动aop注解
<aop:aspectj-autoproxy proxy-target-class="true"/>
  • 目标类
package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class AopService {
    public void print(){
        System.out.println("aop注解配置");
    }
    public Integer add(Integer add1,Integer add2){
        if (add1>0){
            throw new java.lang.ArithmeticException("零除异常");
        }
        return add1+add2;
    }
}

  • AOP处理注解类
package com.neuedu.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @Component   该对象由Spring容器来创建管理
 * @Aspect  标识是一个AOP对象
 */
@Component
@Aspect
public class ExeTimeAop {
    @Pointcut("execution(* com.neuedu.service.*..*(..))")
    public void anyMethod(){}

        @Before("anyMethod()")
        public void before(){
            System.out.println("前置通知");
        }

        //一般参数传递都会在before中--->一般参数都会在方法执行之前传进来
        //方法执行后传进来就没意义了
        @Before("anyMethod()&&args(add1,add2)")
        public void beforeParam(Object add1,Object add2){
            System.out.println("前置通知 参数传递");
            System.out.println(add1);
            System.out.println(add2);
        }

        @Around("anyMethod()")
        public void around(ProceedingJoinPoint pjt) throws Throwable {
            System.out.println("环绕前通知");
            long startTime = System.currentTimeMillis();
            //控制方法是否执行
            pjt.proceed();
            long endTime = System.currentTimeMillis();
            System.out.println("环绕后通知");
            System.out.println("方法:"+pjt.getSignature().getName()+"执行时间"+(endTime-startTime)+"ms");
        }

        @AfterReturning(pointcut = "anyMethod()",returning = "result")
        public void aroundRunning(Object result){
            System.out.println("返回通知");
            System.out.println("返回值:"+result);
        }

        @AfterThrowing(pointcut = "anyMethod()",throwing = "ex")
        public void aroundThrowing(Exception ex){
            System.out.println("异常通知");
            System.out.println(ex);
        }

        @After("anyMethod()")
        public void after(){
            System.out.println("最终通知");
        }
}

  • 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext.xml"})

public class AOPTest {

@Autowired
    private AopService aopService;
@Test
    public void testAnnAop(){
        aopService.print();
        System.out.println("-------------");
        aopService.add(1,4);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值