什么是AOP
是面向切面 是一种编程范式 指导开发者组织程序结构 OOP面向对象,AOP和OOP是一种编程思想,是如何指导开发者写程序完成某些功能 ,这个两种是不同的编程范式。
AOP的核心概念
通知:在切入点执行操作 也就是共性功能
通知类: 定义通知的类
切入点:对接连接点,一个切入点可以描述一个具体方法,也可以匹配多个方法
切面 :描述通知与切入点的关系
连接点: 程序执行过程中的任意问题 ,粒度为执行方法,抛出异常,设置变量 。理解为方法执行
目标对象:被代理的对象
代理对象:目标对象不可访问,通过代理对象增加功能访问
代理模式:1.动态代理 jdk CGLib 动态代理 2.静态代理
AOP的作用
在不惊动原始设计的情况下对其功能进行增强,这样功能底层实现采用代理模式。
AOP解决了什么问题
横切关注点与业务逻辑进行解耦,降低模块之间的耦合度 ,业务模块所调用的逻辑封装起来,提高可维护性和重用性 减少系统重复代码。
1.创建项目 maven.pom aop-annotation
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.6</version>
</dependency>
<!--junit5测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
2.声明了接口
package com.yunda.calculator;
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
3.实现声明的接口方法
package com.yunda.calculator.impl;
import com.yunda.calculator.Calculator;
public class CalculatorPureImpl implements Calculator {
@Override
public int add(int i, int j) {
System.out.println("参数是"+i+","+j);
int result=i+j;
System.out.println("内部方法是:"+result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("参数是"+i+","+j);
int result=i-j;
System.out.println("内部方法是:"+result);
return i-j;
}
@Override
public int mul(int i, int j) {
System.out.println("参数是"+i+","+j);
int result=i*j;
System.out.println("内部方法是:"+result);
return i*j;
}
@Override
public int div(int i, int j) {
System.out.println("参数是"+i+","+j);
int result=i/j;
System.out.println("内部方法是:"+result);
return i/j;
}
}
4.创建静态代理对象
package com.yunda.calculator;
/**
* 静态代理
*/
public class CalculatorStaticProxy implements Calculator {
// 将代理的对象声明成员变量
private Calculator calculator;
@Override
public int add(int i, int j) {
System.out.println("参数是"+i+","+j);
int result=calculator.add(i,j);
System.out.println("内部方法是:"+result);
return 0;
}
@Override
public int sub(int i, int j) {
return 0;
}
@Override
public int mul(int i, int j) {
return 0;
}
@Override
public int div(int i, int j) {
return 0;
}
}
5.创建代理工厂实现接口
package com.yunda.calculator.py;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* jdk代理类
*/
public class ProxyFactory {
// 将代理对象写成成员变量
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy() {
// 1.获取类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
// 2.获取类接口
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object target =null;
try {
System.out.println("动态代理日志"+method.getName()+"参数"+ Arrays.toString(args));
target = method.invoke(proxy, args);
} catch (Exception e) {
e.printStackTrace();
System.out.println("动态代理日志"+method.getName()+"异常"+e.getMessage());
}
finally {
System.out.println("动态代理日志"+method.getName()+"方法执行完毕");
}
return target;
}
};
return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
}
}
6.进行单元测试
package com.yundacalculator;
import com.yunda.calculator.Calculator;
import com.yunda.calculator.impl.CalculatorPureImpl;
import com.yunda.calculator.py.ProxyFactory;
public class Test {
@org.junit.jupiter.api.Test
public void test(){
ProxyFactory proxyFactory = new ProxyFactory(new CalculatorPureImpl());
Calculator proxy = (Calculator) proxyFactory.getProxy();
proxy.div(1,0);
}
}
采用注解方式实现
1.添加依赖 pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.6</version>
</dependency>
2.准备接口
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
3.创建类实现接口
@Component
public class CalculatorPureImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}
4.声明切面
package com.yunda.advier;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect // 标记这个类是一个切面类
@Component // 将切面类交给spring ioc 容器进行管理
public class LogAspect {
@Before(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))") // 标记是前置通知
public void printBefore() {
System.out.println("前置通知...");
}
@AfterReturning(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))")
public void printReturning(){
System.out.println("后返回通知...");
}
@AfterThrowing(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))")
public void printThrowing(){
System.out.println("后异常通知...");
}
@After( value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))")
public void printAfter(){
System.out.println("后置通知....");
}
}
5.启动面向切面
package com.yunda.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration // 当前类为配置类 交给spring ioc 容器进行管理
@ComponentScan("com.yunda") // 包扫描
@EnableAspectJAutoProxy // 启动切面代理模式
public class MySpringAop {
}
6.测试方法
@org.junit.jupiter.api.Test
public void pro(){
calculator.add(1,1);
}
获取通知细节信息
@Before(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))") // 标记是前置通知
public void printBefore(JoinPoint joinPoint) {
// 1.获取签名
Signature signature = joinPoint.getSignature();
// 2.获取目标方法详细信息
String name = signature.getName();
System.out.println("目标方法"+name);
// 3.获取修改器
int modifiers = signature.getModifiers();
System.out.println("修改器"+modifiers);
// 4.获取声明类型名称
String declaringTypeName = signature.getDeclaringTypeName();
System.out.println("获取声明类型名称"+declaringTypeName);
// 5.调用目标方法时传入的实参
Object[] args = joinPoint.getArgs(); // 获取参数
// 6. 转换成list集合
List<Object> objects = Arrays.asList(args);
System.out.println(objects);
System.out.println("前置通知..."+"方法开始"+name+"参数列表"+objects);
}
1.方法返回值
// returning 返回
@AfterReturning(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))",returning =
"targetMethodReturnValue")
public void printReturning(JoinPoint joinPoint,Object targetMethodReturnValue){
String name = joinPoint.getSignature().getName();
System.out.println("后返回通知..."+"方法"+name+"返回值"+targetMethodReturnValue);
}
2.异常对象
@AfterThrowing(value = "execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))",
throwing = "targetMethodException")
public void printThrowing(JoinPoint joinPoint ,Throwable targetMethodException){
String name = joinPoint.getSignature().getName();
System.out.println("后异常通知..."+"方法"+name+"异常"+targetMethodException.getClass().getName());
}
3.改造将连接点的方法使用引入切入点
package com.yunda.advier;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Aspect // 标记这个类是一个切面类
@Component // 将切面类交给spring ioc 容器进行管理
public class LogAspect {
// 切入点
@Pointcut("execution(public int com.yunda.calculator.impl.CalculatorPureImpl.add(int,int))")
public void pry(){};
@Before(value = "pry()") // 标记是前置通知
public void printBefore(JoinPoint joinPoint) {
// 1.获取签名
Signature signature = joinPoint.getSignature();
// 2.获取目标方法详细信息
String name = signature.getName();
System.out.println("目标方法"+name);
// 3.获取修改器
int modifiers = signature.getModifiers();
System.out.println("修改器"+modifiers);
// 4.获取声明类型名称
String declaringTypeName = signature.getDeclaringTypeName();
System.out.println("获取声明类型名称"+declaringTypeName);
// 5.调用目标方法时传入的实参
Object[] args = joinPoint.getArgs(); // 获取参数
// 6. 转换成list集合
List<Object> objects = Arrays.asList(args);
System.out.println(objects);
System.out.println("前置通知..."+"方法开始"+name+"参数列表"+objects);
}
// returning 返回
@AfterReturning(value = "pry()",returning =
"targetMethodReturnValue")
public void printReturning(JoinPoint joinPoint,Object targetMethodReturnValue){
String name = joinPoint.getSignature().getName();
System.out.println("后返回通知..."+"方法"+name+"返回值"+targetMethodReturnValue);
}
@AfterThrowing(value = "pry()",
throwing = "targetMethodException")
public void printThrowing(JoinPoint joinPoint ,Throwable targetMethodException){
String name = joinPoint.getSignature().getName();
System.out.println("后异常通知..."+"方法"+name+"异常"+targetMethodException.getClass().getName());
}
@After( value = "pry()")
public void printAfter(){
System.out.println("后置通知....");
}
}
环绕通知的使用
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Around("pt()")
public void around(){
System.out.println("around before advice ...");
System.out.println("around after advice ...");
}
}
常用注解:
@EnableAspectJAutoProxy 启动切面代理
@Aspect 设置当前类为aop切面类
@Pointcut 设置切入点方法
@Before 前置通知 设置当前通知方法与切入点绑定关系 当前通知方法在切入点方法前运行
@After 后置通知 设置当前通知方法与切入点绑定关系 当前通知方法在切入点方法后运行
@AfterReturning 后返回通知 设置当前通知方法与切入点绑定关系 当前通知方法在原始切入点正常执行完毕后执行
@AfterThrowing 后异常通知 设置当前通知方法与切入点绑定关系 当前通知方法在原始切入点正常执行运行出现异常后执行
@Around 环绕通知 设置当前通知方法与切入点绑定关系 当前通知方法在原始切入点正常前后运行