欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring AOP

程序员文章站 2022-07-12 14:19:19
...

欢迎访问:我的个人网站

AOP

AOP:面向切面编程,扩展一个方法的功能,不通过修改源代码的层面实现,它采用横向抽取机制,取消了传统的纵向继承体系,少了很多重复性的代码,为一些列的方法提供一个统一的增强(事务,安全,缓存,性能监测等),在AOP之前,如果要对方法进行增强,有以下两种方式,分别是:直接在方法内部进行实现以及采用纵向抽取机制。这两种方法都各有弊端,第一种方法会修改源代码并且会增加大量的重复性代码,而第二种方式虽然减少了代码的重复,但是如果父类中方法发生了变化,所有进行增强的子类全部需要更改方法名。

Spring AOP

Spring的AOP实现是在上面第二种方式的基础进行的,它采用的横向抽取机制,针对有接口的情况,AOP的底层实现为动态代理。而对没有接口的类,AOP在实现的时候,会先创建被增强类的一个子类作为代理类,在子类中调用父类的方法进行增强。这种代理叫做cjlib动态代理

AOP中的相关名称

  • JoinPoint(连接点): 类中可以被增强的方法。 每个方法可以理解为一个点,Spring可以选择拦截这些点,并在执行到这些点之前或之后对其进行增强。另外在Spring中,只支持方法类型的连接点。
  • PointCut(切入点): 表示Spring实际拦截的点,意味着Spring要对该方法进行增强。
  • Advice(通知/增强): 表示在拦截到方法之后,对这个方法所添加的逻辑,比如拦截方法,为其增加日志功能,那么日志功能就是就是一个增强。增强有几种不同的类型:
    • 前置增强: 在切入点执行之前进行的增强操作。
    • 后置增强: 在切入点执行之后进行的操作。
    • 异常增强: 在切入点执行时发生异常时进行的操作。
    • 环绕增强: 在切入点执行之前执行(如果同时存在前置增强,在前置增强执行之后在执行)在切入点执行之后也执行。
    • 最终增强: 在后置增强执行之后执行。
  • Aspect(切面): 将增强与切入点联合起来的过程就是切面。
  • Introduction(引介): 在运行期动态的为类添加属性或方法。
  • Target(目标对象): 被增强的对象。
  • Proxy(代理): 在正常完成一次切面操作之后,就会产生一个结果代理类,此时这个类中的方法就是增强过的。

Spring进行AOP操作

在使用Spring进行AOP操作之前,需要明白,Spring建议使用一个专门的切面编程框架AspectJ 来进行AOP操作。所以我们需要导入Spring自身的spring-aop.jar, spring-aspects.jar 以及AspectJ相关的包。AspectJ本身并不是Spring的一部分。使用Spring+AspectJ进行AOP操作主要可以使用以下两种形式实现:

  • 基于AspectJ+配置文件实现。
  • 基于AspectJ+注解实现。

配置文件实现AOP操作

针对配置文件而言,因为要进行AOP操作,约束也还是要进行一定的更新,加入AOP的约束:

<?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: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.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">

</bean>

在配置文件中,同样需要使用一种表达式execution来选择切入点进行增强,使用execution函数进行切入点的配置:

execution(访问权限修饰符 被增强方法的全路径);
//对位于com.bestbigkk.domain包下的Person类中的run()方法进行增强,run(..)表示如果内部存在参数也将匹配
execution(* com.bestbigkk.domain.Person.run(..));

//对所有public修饰的方法进行增强
execution(public *.*(..));

//对所有方法增强
execution(* *.*(..));

//对所有以save开头的方法进行增强
execution(* save*(..));

进行AOP操作

(1)配置被增强的类Person以及增强类PersonEnhance

@Component  
@Scope(value = "prototype")  
public class Person {  
    public void run(){  
        System.out.println("running...");  
  }  
    public void eat(){  
        System.out.println("eating...");  
  }  
    public void sleep(){  
        System.out.println("sleeping...");  
  }  
    public void study(){  
        System.out.println("studying....");  
  }  
}

@Component
@Scope(value = "prototype" )
public class PersonEnhance {
    public void beforeEnhance(){
        System.out.println("前置增强!");
    }
    public void afterEnhance(){
        System.out.println("后置增强!");
    }
    public void roundEnhance(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕增强,之前执行!");
        proceedingJoinPoint.proceed();
        System.out.println("环绕增强,之后执行!");
    }
    public void exceptionEnhance(){
        System.out.println("异常增强!");
    }
    public void finalEnhance(){
        System.out.println("最终增强!");
    }
}

(2)在配置文件中配置AOP操作,

  1. 首先配置切入点,使用execution表达式指明对哪个类的那个方法进行增强,并对这个切入点明明,后续会使用。
  2. 配置切面,需要使用属性ref指定增强类,值为注册的Bean的名称
  3. 在切面中使用不同标签指定要进行何种类型的增强,指定增强类中的方法与切入点。
<aop:config>  
 <!--1.配置切入点, id为切入点的名称-->
 <aop:pointcut id="adviceA" expression="execution(* com.bestbigkk.domain.Person.*(..))"> </aop:pointcut>  
 
 <!--2. 配置切面-->
 <aop:aspect ref="personEnhance">
      <!--3. 指定增强类型-->
      <aop:before method="beforeEnhance" pointcut-ref="adviceA"/>
      <aop:around method="roundEnhance" pointcut-ref="adviceA"/>
 </aop:aspect>
</aop:config>

3.调用:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");  
Person person = (Person) context.getBean("person");  
person.run();

//输出, 在执行被增强类的run方法之前,执行了增强类中的beforEnhance方法。
前置增强!
环绕增强,之前执行!
running...
环绕增强,之后执行!

注解实现AOP操作

1.首先创建增强类以及被增强类,并进行配置。
2.在配置文件中开启AOP自动代理:

<aop:aspectj-autoproxy/>

3.使用注解:
在被增强的类上面使用**@Aspect** 注解。 指定这个类是一个增强类,然后再具体的方法上面再次使用注解,确定要使用的增强方式:

  • @Before:前置增强
  • @AfterReturning : 后置增强
  • @Around: 环绕增强
  • @AfterThrowing: 异常增强
  • @After: 最终增强,不管是否异常都会执行

每个注解都需要为一个属性value配置值,值为execution表达式,指定被增强的类。

示例,建立增强类:

@Aspect
@Component
@Scope(value = "prototype" )
public class PersonEnhance {
    @Before(value = "execution(* com.bestbigkk.domain.Person.eat())")
    public void beforeEnhance(){
        System.out.println("前置增强!");
    }

    @Around(value = "execution(* com.bestbigkk.domain.Person.run(..))")
    public void roundEnhance(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕增强,之前执行!");
        proceedingJoinPoint.proceed();
        System.out.println("环绕增强,之后执行!");
    }
}

建立被增强类:

@Component
@Scope(value = "prototype")
public class Person {
    public void run(){
        System.out.println("running...");
    }
    public void eat(){
        System.out.println("eating...");
    }
}

调用:

   public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
        Person person = (Person) context.getBean("person");
        person.run();
        person.eat();
    }

//输出
环绕增强,之前执行!
running...
环绕增强,之后执行!
前置增强!
eating...