再识Spring——AOP(面向切面编程)

194 阅读3分钟

一.Aop底层原理

1.AOP 底层使用动态代理

(1)有两种情况动态代理

第一种 有接口情况,使用 JDK 动态代理

  • 创建接口实现类代理对象,增强类的方法

1.png

第二种 没有接口情况,使用 CGLIB 动态代理

  • 创建子类的代理对象,增强类的方法

2.png

二.Aop术语

  1. 连接点 类里面哪些方法可以被增强,这些方法称为连接点

  2. 切入点 实际被真正增强的方法,称为切入点

3.通知(增强) (1)实际增强的逻辑部分称为通知(增强)

(2)通知有多种类型

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知

4.切面

切面是指把通知应用到切入点过程

三.实现Aop操作

1. 切入点表达式

(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强

(2)语法结构: execution(权限修饰符+返回类型+类全路径+方法名称(参数列表) )

例1:对 com.spring5.dao.BookDao 类里面的 add 进行增强

execution(* com.spring5.dao.BookDao.add(..))

例2:对 com.spring5.dao.BookDao 类里面的所有的方法进行增强

execution(* com.spring5.dao.BookDao.* (..))

例3:对 com.spring5.dao 包里面所有类,类里面所有方法进行增强

execution(* com.spring5.dao.*.* (..))
2.基于AspectJ注解实现Aop操作

(1)创建类,并在类里面定义方法

public class User {
   public void add() {
     System.out.println("add.......");
   }
}

(2)创建增强类,编写增强逻辑

//增强的类

public class UserProxy {
        public void before() {//前置通知
           System.out.println("before......");
    }
}

(3)在 spring 配置文件中,开启注解扫描

<?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">
<!--开启注解扫描-->
    <context:component-scan base-package="com.spring5.aopanno"></context:component-scan>

(4)使用注解创建User和UserProxy

(5)在增强类上添加注解 @Aspect


@Component
@Aspect
public class UserProxy {
//类中代码已省略
}

(6)在 spring 配置文件中开启生成代理对象

<!--    开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

(7)配置不同类型的通知.在增强类的里面的通知方法上面添加通知类型注解,使用切入点表达式配置

@Component
@Aspect
public class UserProxy {
//    前置通知
    @Before(value = "execution(* com.spring5.aopanno.User.add(..))")
    public void before(){
         System.out.println("before.....");
     }
     
    @AfterReturning(value = "execution(* com.spring5.aopanno.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }
    
    @After(value = "execution(* com.spring5.aopanno.User.add(..))")
    public void after(){
        System.out.println("after...");
    }

    @AfterThrowing (value= "execution(* com.spring5.aopanno.User.add(..))")
    public void afterThrowing(){
        System.out.println("after...");
    }
    
    @Around (value= "execution(* com.spring5.aopanno.User.add(..))")
    public void  around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前......");
        
//        被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后......");
    }
}

其中@After为最终通知,是在被增强的方法执行结束后执行的。而@AfterReturning为后置通知,在被增强的方法正常结束后执行的。@AfterThrowing魏异常通知,在被增强方法有异常时才执行

补充:可以使用@Pointcut对相同的切入点抽取

//相同切入点抽取
@Pointcut(value = "execution(* com.spring5.aopanno.User.add(..))")public void pointdemo() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.........");
}

有多个增强类多同一个方法进行增强,还可以在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高

@Component
@Aspect
@Order(1)
public class PersonProxy{}
3.基于AspectJ 配置文件AOP 操作

(1)创建两个类,增强类和被增强类,创建方法(同上) (2)在 spring 配置文件中创建两个类对象

<!--创建对象-->
<bean id="book" class="com.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.spring5.aopxml.BookProxy"></bean>

(3)在 spring 配置文件中配置切入点

<!--配置 aop 增强-->
<aop:config>

<!--切入点-->
<aop:pointcut id="p" expression="execution(*com.spring5.aopxml.Book.buy(..))"/>

<!--配置切面-->
<aop:aspect ref="bookProxy">

<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"/>

</aop:aspect>
</aop:config>

总之,Aop是在程序运行期间,在不修改源码的基础上对方法进行功能增强。使用Aop可以减少重复代码,提高开发效率,并且便于维护。