spring AOP 整理
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 AOP是动态织入,即在运行时织入
AspectJ是静态织入,即在编译期额类加载时织入,在编译出来class文件时,字节码就已经被织入。
-
编译时织入:AspectJ编译器同时加载我们切面的源代码和我们的应用程序,并生成一个织入后的类文件作为输出。
-
编译后织入:这就是所熟悉的二进制织入。它被用来编织现有的类文件和JAR文件与我们的切面。
-
加载时织入:这和之前的二进制编织完全一样,所不同的是织入会被延后,直到类加载器将类加载到JVM。
2. 使用的对象不同
-
spring AOP的通知是基于该对象是SpringBean对象才可以
-
AspectJ可以在任何对象上应用通知
6 AspectJ注解实现AOP操作
- 导入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>
-
配置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>
-
创建切入点类,创建通知类,并用注解创建对象,在增强类上添加增强注解@Aspect
//切入点类 @Component public class User { public void add() { System.out.println("add......."); } } //增强类 @Component @Aspect public class UserProxy { public void before() {//前置通知 System.out.println("before......"); } }
-
在 xml配置文件中开启生成代理对象
<!-- 开启 Aspect 生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- 配置不同类型的通知
在增强类的通知方法上面添加通知类型的注解,要加切入点表达式
- 前置通知:@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
环绕通知的优先级大于前置通知&后置通知
- 抽取相同的切入点
将相同的切入点提取出来,在增强注解中使用提取的表达式
//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//使用pointdemo代替切入点表达式
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.........");
}
- 有多个增强类多同一个方法进行增强,设置增强类优先级
- 在增强类上面添加@Order(数字),数字越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy
8.完全使用注解开发(不需要配置文件)
- 创建一个配置类
//表明配置类
@Configuration
//开启注解扫描
@ComponentScan(basePackages = {"com.atguigu"})
//开启AspectJ生成代理对象
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
https://juejin.cn/post/6844903555531276296
https://snailclimb.gitee.io/javaguide-interview/#/./docs/e-1spring?id=_514-spring-ioc-amp-aop