Spring的AOP
aop概述
Aspect Oriented Programing 面向切面(方面)编程,
aop:扩展功能不修改源代码实现
aop采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
aop原理
-
横向抽取机制
扩展功能:
添加数据之后,添加日志功能,添加之后,记录在什么时间添加哪个用户
传统方式:
public class User{ //添加新用户的方法 public void add(){ //添加逻辑 //传统方式:在此修改源代码,添加日志逻辑 } }
------》解决方案:纵向抽取机制
缺陷:父类方法名字发生变化,在子类调用的方法也需要变化
public class BaseUser{ //创建添加日志的方法 public void weitelog(){ //添加日志逻辑 } } public class User extends BaseUser{ //添加新用户的方法 public void add(){ //添加逻辑 //功能扩展,添加日志的功能 //调用父类方法实现日志功能 super.writelog(); } }
——》横向抽取机制
aop:横向抽取机制
底层使用 动态代理方式实现
第一种情况
使用动态代理方式,创建接口实现类代理对象
创建一个和DaoImpl平级对象
这个对象不是真的对象,代理对象
实现DaoImpl相同的功能
public interface Dao{ public void add(); } public class DaoImpl implements Dao{ public void add(){ //添加逻辑 } }
第二种情况 没有接口的情况
使用cglib代理,没有借口情况
public class User{ public void add(){ } } /*动态代理实现 *创建Uesr类的子类的对象 *在子类里面调用父类方法完成增强 */
aop操作相关术语
pointcut(切入点)
在类里面可以有很多方法被增强,比如实际操作中,只是增强了类里面add的方法和update方法,实际增强的方法被称为切入点
Advice(增强)
实际增强的逻辑,称为增强,比如扩展日志功能,这个日志功能称为增强
增强(通知)有以下的分类
名称 | 介绍 |
---|---|
前置通知 | 在方法之前执行 |
后置通知 | 在方法之后执行 |
异常通知 | 方法出现异常后执行 |
最终通知 | 在后置之后执行 |
环绕通知 | 在方法之前和之后执行 |
Aspect(切面)
在增强应用到具体的方法上面,过程称为切面,
切面是切入点和通知(引介)的结合
以下不常用,了解即可
Joinpoint:连接点:类里面那些方法可以被增强,这些方法被称为连接点
Introduction(引介):在不修改类代码的前提上,动态添加属性和方法
Target(目标对象):要增强的类
Weaving(织入):把增强应用到目标的过程
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
public class User{
public void add(){}
public void update(){}
public void delete(){
}
public void findAll(){}
}
基于aspectj的AOP
基于aspectj的xml准备工作
@AspectJ简介
在Spring里面进行aop操作,使用aspectj实现
- AspectJ不是Spring的一部分,和是Spring一起使用进行AOP操作
- Spring2.0以后增加了对AspectJ的支持
- 新版本Spring建议使用AspectJ进行AOP操作
使用AspectJ实现AOP有两种方式
- 基于AspectJ的xml配置
- 基于AspectJ的注解方式
AOP操作的准备
-
需要导入aop相关的jar包:aspects aop。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.1.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.8.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>LATEST</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency>
-
创建spring核心配置文件,引入aop的相关约束:the aop schema
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
</beans>
使用表达式配置切入点
-
切入点:实际增强的方法
-
常用的表达式
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>) 作用对象 execution(* aop.Book.add(…)) 某个方法 execution(* aop.Book.*(…)) 某个类的所有方法 execution(* *.*.*(..))
所有 execution(* save*(…)) 匹配所以save开头的方法 前后置通知基本相同
<bean id="book" class="aop.Book"></bean> <bean id="mybook" class="aop.MyBokk"></bean> <!-- 配置aop操作--> <aop:config> <!-- 配置切入点--> <aop:pointcut id="pointcut1" expression="execution(* aop.Book.add(..))"/> <aop:aspect ref="mybook"> <!-- 配置增强的类型 method:增强类里面使用哪个方法作为前置 pointcut-ref:作用于哪一个切入点 --> <aop:before method="before1" pointcut-ref="pointcut1"></aop:before> </aop:aspect> </aop:config>
环绕通知
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //方法之前 System.out.println("方法之前。。。。。。。"); //抛出异常 proceedingJoinPoint.proceed(); //方法之后 System.out.println("方法之后........"); }
<aop:around method="around" pointcut-ref="pointcut1"></aop:around>
基于Aspect实现AOP(注解)
首先在配置文件中开启aop自动代理
<aop:aspectj-autoproxy/>
然后在增强类中编写注解
@Aspect
public class MyBook {
@Before(value = "execution(* aop.Book.*(..))")
public void before1(){
System.out.println("前置增强.......");
}
/**
*
* @param proceedingJoinPoint 用此调用被环绕的对象
*/
@Around(value = "execution(* aop.Book.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//方法之前
System.out.println("方法之前。。。。。。。");
//抛出异常
proceedingJoinPoint.proceed();
//方法之后
System.out.println("方法之后........");
}
}
@AspectJ提供不同的通知类型
- @Before前置通知,相当于BeforeAdvice
- @AfterReturning后置通知,相当于AfterReturningAdvice
- @Around 环绕通知,相当于MethodIntercepetor
- @AfterThrowing抛出通知,相当于ThrowAdvice
- @After 最终final通知,不管是否异常,该通知都会执行