初识AOP
1、初识AOP
1.1 AOP 思想概述
AOP(Aspect Oriented Programming)是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,即解剖对象的内部,将那些影响了多个类的公共行为抽取到一个可重用模块里,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性。
AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理、增强处理。
1.2 AOP 使用场景
1.3 AOP 实现方式
AOP 的初衷是,保证开发者不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的源代码,达到增强功能的目的。按照 AOP 框架修改源代码的时机,可以将其分为两类:
-
静态 AOP 实现:AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
-
动态 AOP 实现:AOP 框架在运行阶段动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。
1.4 AOP 特性概念
-
通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
-
连接点(Join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
-
切点(PointCut): 可以插入增强处理的连接点。
-
切面(Aspect): 切面是通知和切点的结合。
-
引入(Introduction):允许我们向现有的类添加新的方法或者属性。
-
织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的代理对象。
2、初识Spring AOP
2.1 切点指示器
2.2 通知类型
一、简介
Spring AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个核心功能,用于在应用程序中实现横切关注点(cross-cutting concerns)的模块化。在传统的面向对象编程(OOP)中,横切关注点(如日志记录、性能监控、事务管理、安全控制等)往往散布在整个代码库中,导致代码的复用性和可维护性下降。AOP提供了一种将这些关注点从它们所服务的业务逻辑中分离出来的方式。
下面是一些Spring AOP的关键概念:
-
切面(Aspect):
-
切面是描述横切关注点的模块。它封装了影响多个类的行为。在Spring中,切面通常是由带有
@Aspect
注解的类来表示。 -
连接点(JoinPoint):
-
连接点是在程序执行过程中某个特定的点,如方法调用或异常抛出。AOP允许在这些连接点上添加行为。
-
通知(Advice):
-
通知是切面在特定的连接点上执行的动作。通知有多种类型,包括:
-
前置通知(Before advice):在连接点之前执行。
-
后置通知(After returning advice):在连接点成功返回后执行。
-
异常通知(After throwing advice):在连接点抛出异常后执行。
-
最终通知(After (finally) advice):无论连接点是否正常完成,都会执行。
-
环绕通知(Around advice):包围连接点,可以控制是否继续执行连接点。
-
切入点(Pointcut):
-
切入点是一个或多个连接点的集合,用于定义通知应该在何处插入。Spring AOP使用表达式语言来定义切入点。
-
代理(Proxy):
-
代理是Spring AOP为受保护的对象创建的包装器。当应用程序调用目标对象时,实际上是调用了代理,后者再调用目标对象的方法,并在调用前后添加额外的行为。
-
目标对象(Target Object):
-
目标对象是被一个或多个切面所通知的对象。在Spring AOP中,这通常是一个被代理的对象。
二、什么是AspectJ
AspectJ 是一个全面的面向切面编程(Aspect-Oriented Programming, AOP)框架,它扩展了 Java 编程语言,以支持更自然的横切关注点(cross-cutting concerns)的表达和处理。AspectJ 的设计目的是为了更好地解决那些在传统面向对象编程中难以模块化的问题,比如事务管理、日志记录、性能监控、安全控制等。
三、实现AOP的方式
3.1引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3.2 定义通知
@Aspect
@Component
public class MyAOP {
// 定义切入点
@Pointcut("execution(* com.beiyou.springboot04.*.*(..))")
public void point() {}
// 前置通知
@Before("point()")
public void before() {
System.out.println("前置");
}
// 后置通知 始终会执行
@After("point()")
public void after() {
System.out.println("后置");
}
// 环绕通知
@Around("point()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
Object result = pjp.proceed();
System.out.println("环绕后");
return result;
}
// 后置 发生异常时不会执行
@AfterReturning("point()")
public void returning() {
System.out.println("After returning 后置");
}
// 发生异常
@AfterThrowing("point()")
public void throwing() {
System.out.println("发生异常了");
}
}
四、配置说明
//定义切入点
//两种占位符
//* 代表的是一个单词,b* 代表的是以b开头的单词。 例如 bds
//.. 通配符 ,代表的是0个或者多个匹配项
//@Pointcut("execution(* com.beiyou.service..*.*(..))") //com.beiyou.service 下面以及子包所有类的所有方法
//@Pointcut("execution(* com.beiyou.service..*.*(java.lang.String,..))") //com.beiyou.service 下面以及子包所有类的,第一个参数是String类型的方法
//@Pointcut("execution(* com.beiyou.service.*.*(..))") //com.beiyou.service 下面所有类的所有方法
//execution(* com.bao.User.add(..)) com.bao.user类下面的add方法
//excution(* com.bao.user.*(..)) com.bao.user下的所用方法
//配置com.bao.User下的add()
execution(* com.bao.User.add(..))
//配置com.bao.User下的所有方法
execution(* com.bao.User.*(..))
//配置所有包下的所有方法
execution(**.*(..))
//配置所有包下的a开头方法
execution(* a*(..))
//配置com.service包下的所有类的所有方法
execution(* com.service.*.*(..)))
五、通过注解使用切面
声明注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PageX {
}
切换注解
先引入分页组件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
package com.beiyou.aop;
import com.beiyou.model.Product;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Component
@Aspect
@Slf4j
public class MyAop {
//定义切入点
//* 代表的是一个单词
//.. 通配符 ,代表的是0个或者多个匹配项 () 代表的是参数个数 ,(..)所有参数都匹配
// @Pointcut("execution(* com.beiyou.*.*.*(..))")
@Pointcut("@annotation(com.beiyou.annotation.PageX)")
public void point(){
}
// @Before("point()")
// public void before(){
// log.debug("前置通知");
// }
//
// @After("point()")
// public void after(){
// log.debug("后置通知");
// }
@Around("point()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.debug("环绕前");
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
String pageNum = request.getParameter("pageNum");
String pageSize = request.getParameter("pageSize");
if(pageNum!= null && pageSize!= null){
int page_num = Integer.parseInt(pageNum);
int page_size = Integer.parseInt(pageSize);
PageHelper.startPage(page_num,page_size);
}
Object value = joinPoint.proceed();
if(value instanceof Page){
Page page = (Page) value;
PageInfo pageInfo = new PageInfo();
pageInfo.setTotal(page.getTotal());
pageInfo.setList(page.getResult());
}
log.debug("环绕后");
return value;
}
@AfterReturning(value = "point()",returning = "returnValue")
public void returning(Object returnValue){
log.debug("目标方法正常返回结果->{}",returnValue);
}
@AfterThrowing(value = "point()",throwing = "ex")
public void throwing(Exception ex){
log.debug("目标方法抛异常了,->{}",ex.getMessage());
}
}
使用
public interface ProductDao {
@PageX
List<Product> select();
List<Product> selectOne();
}