Spring - SpringAOP详解

本文深入讲解Spring AOP的概念,包括连接点、切点、增强、目标对象、引介、织入、代理和切面等核心术语,以及Spring对AOP的支持方式。并通过实例演示如何使用@AspectJ注解开发AOP,包括创建切面、定义切点、通知类型及参数传递。

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

一、什么是SpringAOP

  AOP就是在某一个类或方法执行前后打个标记,声明在执行到这里之前要先执行什么,执行完这里之后要接着执行什么。

二、AOP术语

   1)连接点(Joinpoint)

    程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。
 
    2)切点(Pointcut)
    每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
 
    3)增强(Advice)
    增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。
 
    4)目标对象(Target)
    增强逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
 
    5)引介(Introduction)
    引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。    
 
    6)织入(Weaving)
    织入是将增强添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、增强或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
    a、编译期织入,这要求使用特殊的Java编译器。
    b、类装载期织入,这要求使用特殊的类装载器。
    c、动态代理织入,在运行期为目标类添加增强生成子类的方式。
    Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
 
    7)代理(Proxy)
    一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
 
    8)切面(Aspect)
    切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
  引用自https://www.cnblogs.com/yangyquin/p/5462488.html
  注意:增强在有些地方叫做通知

通知分为

  前置通知:before       在方法执行之前执行

  后置通知:after       在方法执行之后执行(不管方法执行成功还是失败)

  返回通知:afterReturning   在方法执行成功后执行

  异常通知:afterThrowing   在方法发生异常后执行

  环绕通知:around       在方法执行前后执行

三、Spring对AOP的支持

  1.使用ProxyFactoryBean和对应的接口实现AOP

  2.使用XML配置AOP      

  3.使用@AspectJ注解驱动切面  

  4.使用AspectJ注入切面

四、使用@AspectJ注解开发AOP(优先)

  0.Spring实现的是一个方法级别的AOP框架,动态代理的方式拦截指定方法的通知

  1.创建切面

    在Spring中使用@Aspect注解一个类,SpringIOC容器就会将其当作一个切面。

    切面类中的方法可以通过加上以下注解作为通知

    @Before   前置通知

    @After   后置通知

    @Around   环绕通知

    @AfterReturning  正常返回通知

    @AfterThrowing   异常通知  
环绕通知:@Around注解,同时要加入一个参数,参数类型是ProceedingJoinPoint 此类型对象有一个方法proceed(),用于放行去执行真正的方法。
    
package club.llf.aspect;
import org.aspectj.lang.annotation.*;
@Aspect
public class XXXAspect {
	@Before("execution(* club.llf.service.impl.UserServiceImpl.printUser(..))")
	public void before(){
		System.out.println("前置通知");
	}
	@After("execution(* club.llf.service.impl.UserServiceImpl.printUser(..))")
	public void after(){
		System.out.println("后置通知");
	}
	@AfterReturning("execution(* club.llf.service.impl.UserServiceImpl.printUser(..))")
	public void afterReturning(){
		System.out.println("返回通知");
	}
	@AfterThrowing("execution(* club.llf.service.impl.UserServiceImpl.printUser(..))")
	public void afterThrowing(){
		System.out.println("异常通知");
	}
     @Around("execution(* club.llf.service.impl.UserServiceImpl.printUser(..))")
     public void around(ProccedingJoinPoint jp){ 
         System.out.println("环绕通知前");
         jp.proceed();
         System.out.println("环绕通知后")
}
}
  2.定义切点
   Spring通过上述代码中的正则表达式  execution(* club.llf.service.impl.UserServiceImpl.printUser(..)) 来判断要拦截的方法

    execution     : 执行方法时触发

    *         : 任意返回值的方法

    club.llf.service.impl.UserServiceImpl.printUser   :  拦截指定方法名

    (..)       : 方法的参数任意

  3.为了少写重复的正则表达式,可以使用@Pointcut注解来避免,修改如下 

package club.llf.aspect;
import org.aspectj.lang.annotation.*;
@Aspect
public class XXXAspect {
       @Pointcut("execution(* club.llf.service.impl.UserServiceImpl.printUser(..))")
	public void print(){
	}
	@Before("print()")
	public void before(){
		System.out.println("前置通知");
	}
	@After("print()")
	public void after(){
		System.out.println("后置通知");
	}
	@AfterReturning("print()")
	public void afterReturning(){
		System.out.println("返回通知");
	}
	@AfterThrowing("print()")
	public void afterThrowing(){
		System.out.println("异常通知");
	}
     @Around("print()")
	public void around(ProccedingJoinPoint jp){
		System.out.println("环绕通知前");
         jp.proceed();
         System.out.println("环绕通知后")
}
}

 

 五、开启AOP自动代理

  1.注解方式

     注解配置类如下(也可以给切面类加上@Component,并且扫描其所在包,那么就可以不配置下面的@Bean了)

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages={"club.llf.pojo"})
public class AOPConfig{
        @Bean
        public RoleAspect  getRoleAspect(){
                return  new XXXAspect();
        }
}        

  2.XML方式

<aop:aspectj-autoproxy/>
<bean id="xxxAspect" class="club.llf.aspect.XXXAspect"/>
....

六、参数传递和引入

  1.参数传递

    在正则表达式中书写 execution(* club.llf.service.impl.UserServiceImpl.printUser(..) && args(参数名1,参数名2))

    注解方法中加入和正则中相同的参数名即可完成传递。(参数名 一般是方法的真实参数名)

     @AfterReturning("execution(* club.llf.service.impl.UserServiceImpl.printUser(..)  && args(role,sort))")
	public void afterReturning(Role role,int sort){
		System.out.println("返回通知");
	}

  2.引入

   引入一般是对原有没有的功能进行拓展

  实现步骤

 

 

 

 

 

 

 

 

  

  

转载于:https://www.cnblogs.com/wlzg/p/10995629.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值