目录
AOP
1 介绍
Spring中另外一个核心功能,AOP
AOP(Aspect Oriented Programming),即面向切面编程.
OOP(Object Oriented Programming ),即面向对象编程.
AOP面向切面编程,利用 一种称为
"横切"
的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为
抽取出封装到一个可重用模块),并将其命名 为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
面向切面编程的作用:就是将项目中与核心逻辑无关的代码横向抽取成切面类,通过织入作用到目标方法,以使目标方法执行前后达到增强的效果.
原理: AOP底层使用的就是动态代理,给AOP指定哪些类型(目标类)需要增强,就会产生对应的代理对象,代理对象执行方法前后会先执行增 强的方法.
好处:减少系统的重复代码,降低模块之间的耦合度,便于维护,可以只关注核心业务
补充:
AOP底层实现原理是代理设计模式,且是动态代理
动态代理(jdk动态代理技术 / cglib动态代理技术)
jdk代理技术,要求目标类必须是接口
cglib代理技术, 目标类可以是接口也可以是类
2 AOP术语
连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。即每个方法在切入之前,都是连接点
切入点
(Pointcut):被Spring切入连接点。即真正会增强的目标方法通知、
增强
(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。
目标对象
(Target):被代理的目标对象织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
代理
(Proxy):被AOP织入通知后,产生的结代理类。
切面
(Aspect):由切点和通知组成
3 应用场景
事务管理
后续spring管理事务用的AOP原理
权限校验
后期使用Spring Security注解开发时,其实利用了AOP思想
日志记录
性能检测
等等
4 演示
需求: 实现业务层代码执行时,能出现一些增强的效果
一般写在service层是咱们要被增强的地方
开发步骤
创建切面类,类上加注解
@Component ,加上该注解,springboot框架就会创建该类对象
@Aspect , 加上该注解,springboot框架内部就会知道该类是一个切面类
设置切入点方法,并加注解
@Pointcut , 用于定义要增强的目标方法路径
设置各种增强(或者叫通知)方法
注解 解释 @Around 环绕通知 @Before 前置通知 @After 后置通知 @AfterReturning 后置返回通知 @AfterThrowing 异常通知
1.pom.xml添加aop依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import com.qf.annotation.MyLog;
import com.qf.model.Log;
import com.qf.model.User;
import com.qf.service.MyLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.WebUtils;
/**
* @Author yhf
* @Date 2024/9/3
* @Desc 200不是二百
*/
@Component
@Aspect
public class MyLogAspect {
// 注入业务层对象
@Autowired
private MyLogService logService;
// 切入的目标是注解
@Before("@annotation(com.qf.annotation.MyLog)")
public void after(JoinPoint joinPoint) {
// 调用下方自定义方法,获得注解中的值
String desc = getAnnoDesc(joinPoint);
System.out.println("注解中的值:" + desc);
// 以下代码需要使用Springmvc,既控制层也需要由Spring托管,才会获得ip
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes( )).getRequest( );
// 能取出数据的前提是登录时将用户信息存储到session
User user = (User) WebUtils.getSessionAttribute(request, "user");
String name = "未登录";
if (user != null) { // 防止出现空指针异常
name = user.getName( );
}
//ip
String ip = request.getRemoteAddr( );
System.out.println("请求对象的ip:" + ip);
// 封装数据
Log log = new Log( );
log.setName(name);
log.setIp(ip);
log.setLogTime(new Date());
log.setLog(desc);
// 调用业务层,执行插入
logService.insertLog(log);
}
/*
* 封装的方法,通过反射技术获得目标方法上的注解的值
*/
public static String getAnnoDesc(JoinPoint joinPoint) {
String value = "";
try {
MethodSignature signature = (MethodSignature) joinPoint.getSignature( );
// 获得目标方法对象
Method method = signature.getMethod( );
// 获得目标方法对象上的日志注解
MyLog myLog = method.getDeclaredAnnotation(MyLog.class);
// 获得日志注解上的值
value = myLog.value( );
} catch (Exception e) {
throw new RuntimeException(e);
}
return value;
}