💨更多相关知识👇
💖Spring中的创建对象的三种方式、第三方资源配置管理详细描述及使用(XML版完结篇)
💖Spring中的bean的配置、作用范围、生命周期详细描述及使用(XML版上篇)
💖Spring中的依赖注入、setter与构造器注入、自动装配与集合注入详细描述及使用(XML版中篇)
💖异常处理与解决方案详解上篇
💖异常处理与解决方案详解下篇
💖Math类与System类的常用方法使用
💖JavaEE中的静态方法定义、方法重载要求、return作用详解
💖List接口的常用方法,精华总结
💖JavaEE中的Stream流的常用方法
💖JavaEE中的Stream流知识点使用,精华总结,一文直接上手
🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 ? 🍂个人博客首页: KJ.JK ? 💖系列专栏:JavaEE进阶教程系列
一、AOP概念的介绍
🍂AOP的开发思想(Aspect Oriented Programing/面向切面编程)
* 编写代码的时候只关注主业务功能 (非共性功能)
* 将共性功能抽取出来
* 在代码执行的时候将"共性功能与非共性功能","织入"到一起生成"代理对象"
🍂AOP的应用场景
银行系统会有一个取款流程,还会有一个查询余额流程,两者有一个相同的验证流程
验证用户的功能是相同的,但又在不同地方出现,我们可以把它提取出来,做成切面类在程序执行的时候动态的添加到业务程序中去,这个切面类可以在不同的地方重用
* 有了AOP,写代码时不需要把这个验证用户步骤写进去,即完全不考虑验证用户。只写取款和显示余额的业务代码
* "而在另一个地方,写好验证用户的代码。这个验证用户的代码就是切面代码",以后在执行取款和显示余额的时候,利用代理模式。将验证用户的功能在执行取款和显示余额前调用
🍂AOP的真正目的
"AOP真正目的是": 你写代码的时候,只需考虑主流程,而不用考虑那些不重要的,但又必须要写的其它相同的代码,
"这些其它的相同代码所在的类就是切面类"
"主要应用场景:"
1.事务处理
2.日志记录
3.用户权限
🌴总结
* 什么是AOP?
* 面向切面编程
* 它的特点是什么?
* 将切面代码(多个方法中的相同代码)与主业务代码分开编写
* 在程序执行过程中动态将"切面代码"与"主业务代码织入"到一起
* "底层使用的是动态代理模式"
🍂AOP相关术语
🔸JoinPoint(连接点)
* JoinPoint(连接点): 在程序执行过程中的某个阶段点,就是指主业务方法的调用,它是客观存在的
指的"接口或实现类中所有的方法"
🔸Pointcut(切入点)、 Advice(通知)、Aspect(切面)
* Pointcut(切入点):
"需要加强的方法", 是连接点中的部分方法。满足某一规则的类或方法都是切入点
------------------------------------------------------------------------------------------------------------------------------------------------
* Advice(通知):
"切入点处所要执行的程序代码",即"要执行的公共方法" (公共的功能)
通知的类型有: 前置通知、后置通知、异常通知、最终通知、环绕通知
------------------------------------------------------------------------------------------------------------------------------------------------
* Aspect(切面): 切面指的是 "切入点(规则)" 和 "通知(织入方法)的类"
"切面类 = 切入点规则 + 通知方法"
🔸Target(目标对象)、 Weaving(织入)、Proxy(代理)
* Target(目标对象): 被代理的对象,即主业务类 , 比如动态代理案例中的明星 (主要功能)
------------------------------------------------------------------------------------------------------------------------------------------------
* Weaving(织入): 织入指的是"把新增的功能用于目标对象",创建代理对象的过程 (把共性功能放在主要功能里面)
------------------------------------------------------------------------------------------------------------------------------------------------
* Proxy(代理): 一个类被AOP织入增强后产生的结果类,即代理类 , 比如动态代理案例中的经纪人
(一个完整的类,有主要功能和共性功能)
二、AOP入门案例
"AOP入门案例(注解版)操作顺序"
------------------------------------------------------------------------------------------------------------
1.导入aop相关坐标
<!--spring核心依赖,会将spring-aop传递进来-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!-- 导入Spring坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2.定义service接口与实现类
3.定义通知类,制作通知
public class MyAdvice {
public void before(){
System.out.println("现在的时间:" + new Date().toLocaleString());
}
}
4.定义切入点(切入点规则)
public class MyAdvice {
@Pointcut("execution(void com.itheima.service.AccountService.update())") private void pt(){}
}
5."绑定切入点与通知关系",并指定通知添加到目标连接点的具体执行位置
public class MyAdvice {
@Pointcut("execution(void com.itheima.service.AccountService.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在的时间:" + new Date().toLocaleString());
}}
6.使用@Aspect把切入点和通知关系一起的这个类变成通知类
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.itheima.service.AccountService.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在的时间:" + new Date().toLocaleString());
}
}
7.开启Spring对AOP注解驱动支持
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {}
?代码演示
<?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>com.itheima</groupId>
<artifactId>spring-07-aop</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--spring核心依赖,会将spring-aop传递进来-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!--切入点表达式依赖,目的是找到切入点方法,也就是找到要增强的方法-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
</project>
------------------------------------------------------------------------------------------------------------
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}
------------------------------------------------------------------------------------------------------------
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(public void com.itheima.service.impl.AccountServiceImpl.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
------------------------------------------------------------------------------------------------------------
public interface AccountService {
int save(String name);
void update();
}
------------------------------------------------------------------------------------------------------------
import com.itheima.service.AccountService;
import org.springframework.stereotype.Service;
@Service
public class AccountServiceImpl implements AccountService {
@Override
public int save(String name) {
System.out.println("保存" + name + "账户");
return 1;
}
@Override
public void update() {
System.out.println("更新账户");
}
}
------------------------------------------------------------------------------------------------------------
import com.itheima.config.SpringConfig;
import com.itheima.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AopTest {
@Autowired
private AccountService accountService;
@Test
public void testUpdate() {
accountService.update();
}
@Test
public void testSave() {
accountService.save("张三");
}
}
三、切入点表达式
🍂切入点表达式标准格式
* 切入点表达式标准格式:
动作关键字(访问修饰符 返回值 包名.接口/类名. 方法名(参数) 异常名 )
例子: execution(public void com.itheima.service.AccountService.save(String))
* 动作关键字: 描述切入点的行为动作,例如"execution表示执行到指定切入点"
1.访问修饰符:public,private等,可以省略
2.返回值
3.包名.类/接口名,可以省略
4.方法名
5.参数
6.异常名: 方法定义中抛出指定异常,可以省略,一般不理会
🍂使用通配符描述切入点,快速描述
1. * "单个独立的任意符号", 表示"任意一个", 可以独立出现, 也可以作为"前缀或者后缀的匹配符出现"
例子:
execution(public * com.itheima.*.UserService.find*(*))
------------------------------------------------------------------------------------------------------------
2. " .. 多个连续的任意符号", "表示任意个数(0个或1个或多个)",可以独立出现,常用于简化包名与参数的书写
例子:
execution(public User com..UserService.findById(..))
------------------------------------------------------------------------------------------------------------
3. +专用于"匹配子类类型"
例子:
execution(* com.itheima.AccountService+.update(..))
------------------------------------------------------------------------------------------------------------
?标准格式写法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut(
"execution(public void com.itheima.service.impl.AccountServiceImpl.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?省略 修饰符, 包名.类名写法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?返回值任意演示, com包下的任意一个子包的service包中的AccountService类的update方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut(" execution(* com.*.service.AccountService.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?返回值任意, com下的任意子包中的AccountService类的update方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut(" execution(* com..AccountService.update()) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?0个参数的方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut(" execution(* com.itheima.service.AccountService.*()) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?1个参数的方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut(" execution(* com.itheima.service.AccountService.*(*)) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?0个或1个或多个参数演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.service.AccountService.*(..)) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?e结尾的方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.service.AccountService.*e(..)) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?u开头的方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.itheima.service.AccountService.u*(..)) ) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?所有的类的方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* *(..)) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?方法名是save或update的方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* save(..)) || execution(* update(..)) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
?除了方法名是save的所有方法演示
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Date;
@Component
@Aspect
public class MyAdvice {
@Pointcut("!execution(* save(..)) ")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println("现在时间:"+new Date().toLocaleString());
}
}
🌴总结
* 切入点表达式由哪几个部分组成?
访问修改符 返回类型 包名.类名.方法名 (参数类型) 异常类型
* 必须的参数是哪三个?
1.返回类型
2.方法名
3.参数类型
作者:KJ.JK
文章对你有所帮助的话,欢迎给个赞或者 star,你的支持是对作者最大的鼓励,不足之处可以在评论区多多指正,交流学习
|