1.Spring AOP
1.1 AOP介绍
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
知识扩展: 1.面向过程编程 C语言 谭浩强!!!
2.半面向对象的程序设计 C++
3.面向对象的编程技术 java
编程方式:
1.面向接口编程
2.面向切面编程
总结: AOP(面向切面编程) 主要利用动态代理的模式 降低程序的耦合度,扩展业务功能方法.
1.2 关于AOP名词介绍
1).连接点: 用户可以被扩展的方法
2).切入点: 用户实际扩展的方法
3).通知: 扩展方法的具体实现
4).切面: 将通知应用到切入点的过程
1.3 通知类型(必会)
- before: 在目标方法执行之前执行
- afterReturning: 在目标方法执行之后返回时执行
- afterThrowing: 在目标方法执行之后,抛出异常时执行
- after: 无论程序是否执行成功,都要最后执行的通知
- around: 在目标方法执行前后 都要执行的通知(完美体现了动态代理模式)
功能最为强大 只有环绕通知可以控制目标方法的执行
关于通知方法总结:
1.环绕通知是处理业务的首选. 可以修改程序的执行轨迹
2.另外的四大通知一般用来做程序的监控.(监控系统) 只做记录
1.4 切入点表达式
概念:当程序满足切入点表达式,才能进入切面,执行通知方法.
1.bean(“bean的ID”) 根据beanId进行拦截 只能匹配一个
2.within(“包名.类名”) 可以使用通配符*? 能匹配多个.
粒度: 上述的切入点表达式 粒度是类级别的. 粗粒度.
3.execution(返回值类型 包名.类名.方法名(参数列表…))
粒度: 控制的是方法参数级别. 所以粒度较细. 最常用的.
4.@annotation(包名.注解名) 只拦截注解.
粒度: 注解是一种标记 根据规则标识某个方法/属性/类 细粒度
1.5 AOP入门案例
1.5.1 导入jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring_demo_9_aop</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--Spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入SpringBean-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入context包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入表达式jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入日志依赖-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--引入测试包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--引入AOP包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
</project>
1.5.2 配置切面类
切面 = 切入点表达式 + 通知方法
//1.AOP需要被Spring容器管理
@Component
//2.标识该类为AOP切面
// Spring容器默认不能识别切面注解,需要手动配置
@Aspect
public class SpringAOP {
//1.定义before通知
@Before("bean(deptServiceImpl)")
public void before(){
System.out.println("我是before通知");
}
}
1.5.3 编辑配置类
@Configuration
@ComponentScan("com.jt")
@EnableAspectJAutoProxy(proxyTargetClass=false) //启动AOP注解 创建代理对象
//默认启用JDK动态代理,
//目标对象没有实现接口时,则采用CGLIB
//强制使用cglib proxyTargetClass=true
//JDK代理创建速度快.运行时稍慢
//CGLIB创建时速度较慢,运行时更快
public class SpringConfig {
}
1.5.4 编辑测试代码
1.6 关于表达式写法
package com.jt.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//1.AOP需要被Spring容器管理
@Component
//2.标识该类为AOP切面
// Spring容器默认不能识别切面注解,需要手动配置
@Aspect
public class SpringAOP {
/**
* 切入点表达式练习
* within:
* 1.within(com.jt.*.DeptServiceImpl) 一级包下的类
* 2.within(com.jt..*.DeptServiceImpl) ..代表多级包下的类
* 3.within(com.jt..*) 包下的所有的类
*
* execution(返回值类型 包名.类名.方法名(参数列表))
* 1.execution(* com.jt..*.DeptServiceImpl.add*())
* 注释: 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类
* 的add开头的方法 ,并且没有参数.
*
* 2.execution(* com.jt..*.*(..))
* 注释: 返回值类型任意,com.jt包下的所有包的所有类的所有方法 任意参数.
*
* 3.execution(int com.jt..*.*(int))
* 4.execution(Integer com.jt..*.*(Integer))
* 强调: 在Spring表达式中没有自动拆装箱功能! 注意参数类型
*
* @annotation(包名.注解名)
* @Before("@annotation(com.jt.anno.Cache)")
* 只拦截特定注解的内容.
*/
//1.定义before通知
//@Before("bean(deptServiceImpl)")
//@Before("within(com.jt..*)")
//@Before("execution(* com.jt..*.DeptServiceImpl.add*())")
@Before("@annotation(com.jt.anno.Cache)")
public void before(){
System.out.println("我是before通知");
}
}
1.7 关于通知方法测试
1.7.1 抽取通知方法
1.7.2 @Before作用
说明: 前置通知,在目标方法执行之前执行
用途: 如果需要记录程序在方法执行前的状态,则使用前置通知.
需求: 1.获取目标对象的类型
2.获取目标方法的名称
3.获取目标方法的参数
/*Spring为了AOP动态获取目标对象及方法中的数据,则通过joinPoint对象
* 进行数据的传递.
* getSignature : 方法签名 获取方法的参数
* */
@Before("pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("获取目标对象的类型:"+joinPoint.getTarget().getClass());
System.out.println("获取目标对象类名:"+joinPoint.getSignature().getDeclaringTypeName());
System.out.println("获取目标对象方法名:"+joinPoint.getSignature().getName());
System.out.println("获取方法参数:"+ Arrays.toString(joinPoint.getArgs()));
System.out.println("我是before通知");
}
1.7.3 @AfterReturning
说明: afterReturning,在目标方法执行之之后执行
用途: 用来监控方法的返回值,进行日志的记录
1).在接口中添加测试方法
2).实现目标方法
3).编辑AOP方法测试
1.7.4 @AfterThrowing
作用: 当目标方法执行时,抛出异常时 可以使用AfterThrowing 进行记录.
1.7.4.1 编辑业务接口
1).编辑业务接口
2).编辑业务实现类
1.7.4.3 编辑异常通知类型
1.7.5 @Around
1.7.5.1 @Around作用
规则: 在目标方法执行前后都要执行
实际作用: 可以控制目标方法是否执行.
1.7.5.2 环绕通知学习
/**
* 关于环绕通知的说明
* 作用: 可以控制目标方法是否执行.
* 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.
* 注意事项:
* ProceedingJoinPoint is only supported for around advice
* @return
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
Object result = null;
try {
System.out.println("环绕通知开始");
//1.执行下一个通知 2.执行目标方法 3.接收返回值
Long start = System.currentTimeMillis();
result = joinPoint.proceed();
Long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕通知结束");
return result;
}
1.8 关于Spring 中AOP流程介绍
1.8.1 AOP中的名词解释
1).连接点: 用户可以被扩展的方法 joinPoint
2).切入点: 用户实际扩展的方法(判断方法能否进入切面) pointcut 切入点表达式
3).通知: 扩展方法的具体实现 @before
4).切面: 将通知应用到切入点的过程 方法功能得到扩展全部配置(切面=切入点表达式+通知方法)
1.8.2 关于知识点讲解
1.8.2.1 pointcut
说明:判断方法能否进入切面 IF判断
注解说明: @Pointcut 内部编辑切入点表达式
判断依据:
1.8.2.2 joinPoint(连接点)
说明: 当用户执行方法时,如果方法满足pointcut表达式,则该方法就是连接点.
如果通知中需要获取方法相关参数 则Spring会将joinPoint对象进行封装.为通知参数进行赋值
1.9 关于通知方法的执行顺序
1.执行around开始
2.执行before
3.执行目标方法
4.执行afterReturning
5.执行afterThrowing
6.执行after
7,执行around通知结束
1.10 关于Order注解说明
说明: 研究如果是多个切面,如何控制切面的执行的顺序.
方法: 通过Order注解实现.