Spring AOP

1 描述Spring AOP

AOP是面向切面编程。通俗的讲就是可以不修改源代码的方式,在主干功能里面添加新功能;将与业务无关,但和业务模块共同调用的东西(例如事务处理,日志管理,权限控制等)封装起来。从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2 Spring AOP的原理

Spring AOP是基于动态代理。动态代理有两种情况。

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

JDK动态代理:调用newProxyInstance方法。

newProxyInstance(ClassLoader loader, 类 interfaces, InvocationHandler h)。

newProxyInstance方法的三个参数:

1 类加载器

2 切入点的接口

3 实现接口InvocationHandler,创建代理对象,写增强的部分

第二种是没有接口,只有类,使用CGLIB动态代理

3 Spring AOP的术语

  • 连接点:类中的那些方法可以被增强
  • 切入点: 实际上真正被增强的方法
  • 通知(增强)
    增强逻辑代码部分
    通知的类型有5种:前置,后置,环绕,异常,最终通知。
  • 切面:是动作,将通知应用放到切入点的过程

4 切入点表达式

作用:表达那个类中的那个方法进行增强

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

举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强 execution(* com.atguigu.dao.BookDao.add(..))

举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强 execution(* com.atguigu.dao.BookDao.* (..))

举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强 execution(* com.atguigu.dao.. (..))

5 Spring AOP和AspectJ AOP的区别

spring request获取post内容_AOP

  1. 织入时期不同

Spring AOP是动态织入,即在运行时织入

AspectJ是静态织入,即在编译期额类加载时织入,在编译出来class文件时,字节码就已经被织入。

  • 编译时织入:AspectJ编译器同时加载我们切面的源代码和我们的应用程序,并生成一个织入后的类文件作为输出。
  • 编译后织入:这就是所熟悉的二进制织入。它被用来编织现有的类文件和JAR文件与我们的切面。
  • 加载时织入:这和之前的二进制编织完全一样,所不同的是织入会被延后,直到类加载器将类加载到JVM。
2. 使用的对象不同
  • spring AOP的通知是基于该对象是SpringBean对象才可以
  • AspectJ可以在任何对象上应用通知

6 AspectJ注解实现AOP操作

  1. 导入maven依赖
<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>
    </dependencies>
  1. 配置xml文件,开启注解扫描
<?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 basepackage="com.atguigu.spring5.aopanno"></context:component-scan>
  1. 创建切入点类,创建通知类,并用注解创建对象,在增强类上添加增强注解@Aspect
//切入点类
@Component
public class User {
 public void add() {
 System.out.println("add.......");
 }
}

//增强类
@Component
@Aspect
public class UserProxy {
 public void before() {//前置通知
 System.out.println("before......");
 }
}
  1. 在 xml配置文件中开启生成代理对象
<!-- 开启 Aspect 生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  1. 配置不同类型的通知

在增强类的通知方法上面添加通知类型的注解,要加切入点表达式

  • 前置通知:@Before
@Component
@Aspect //生成代理对象
public class UserProxy {
 //前置通知
 //@Before 注解表示作为前置通知
 @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
 public void before() {
 System.out.println("before.........");
 }
  • 后置通知:@AfterReturning
  • 最终通知: @AfterTrowing
  • 环绕通知:@Around

环绕通知的优先级大于前置通知&后置通知

  1. 抽取相同的切入点

将相同的切入点提取出来,在增强注解中使用提取的表达式

//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")

public void pointdemo() {
}
//使用pointdemo代替切入点表达式

@Before(value = "pointdemo()")
public void before() {
 System.out.println("before.........");
}

 

  1. 有多个增强类多同一个方法进行增强,设置增强类优先级
  • 在增强类上面添加@Order(数字),数字越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy

8.完全使用注解开发(不需要配置文件)

  • 创建一个配置类
//表明配置类
@Configuration
//开启注解扫描
@ComponentScan(basePackages = {"com.atguigu"})
//开启AspectJ生成代理对象
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}