面向切面编程(Aspect Orient Programming,AOP):是一种编程范式,旨在将 横切关注点(Cross-Cutting Concerns,如日志、事务、安全等) 从业务逻辑中分离出来,通过模块化的方式增强代码的可维护性和复用性。核心思想是通过“切面”定义通用功能,并在运行时动态织入到目标代码中横切关注点(Cross-Cutting Concerns):指的是在系统中"横向"跨越多个模块、多个层次的功能需求,它们无法很好地被封装在单个类或模块中
@Slf4j
public class HardCoding {
public void demo() {
long startTime = System.currentTimeMillis();
//业务代码
log.info("消耗时间:{}", System.currentTimeMillis() - startTime);
}
public static void main(String[] args) {
new HardCoding().demo();
}
}
使用这种硬编码方式监控业务性能主要有以下缺点:

引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>3.4.8</version> <!-- 应与SpringBoot版本一致 -->
</dependency>@Slf4j
@RestController
@RequestMapping("/demo")
public class Controller {
@RequestMapping("/a")
public void methodA(){
log.info("执行methodA");
}
@RequestMapping("/b")
public void methodB(){
log.info("执行methodB");
}
@RequestMapping("/c")
public void methodC(){
log.info("执行methodC");
}
}@Component
@Slf4j
@Aspect
public class AOP {
@Around("execution(* org.example.springaop.blog_demo.Controller.*(..))")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//记录开始时间
long startTime = System.currentTimeMillis();
//执行业务
Object result = joinPoint.proceed();
//计算时间
log.info("消耗时间:{}", System.currentTimeMillis() - startTime);
return result;
}
}执行结果:

相较于硬编码的方式,使用Spring AOP监控业务性能有很多显著好处:


ABC业务的集合为一个切入点,具体能切到哪些方法与表达式有关切入点+通知
作用:用于标识一个类为切面(Aspect)。切面包含切入点(Pointcut)和通知(Advice),用于模块化横切关注点(如日志、事务、权限等)

execution:是Spring AOP中定义切点的一种表达式,用于指定在哪些方法或类上应用通知(Advice)。它将横切关注点(如日志、事务)与业务逻辑分离,通过表达式匹配目标方法或类
//语法结构
execution(<访问限定修饰符> <返回类型> <包名.类名.方法(方法参数)> <异常>)*:匹配任意字符(除包分隔符外)
..:匹配任意子包或多级目录;匹配任意数量参数
@Pointcut:是Spring AOP中的一个注解,用于定义一个可重用的切点表达式
@Slf4j
@RestController
@RequestMapping("/demo")
public class Controller {
@RequestMapping("/a")
public Object methodA(Integer id){
log.info("执行methodA");
return id;
}
@RequestMapping("/b")
public void methodB(){
log.info("执行methodB");
throw new RuntimeException("发生异常");
}
}@Component
@Slf4j
@Aspect
public class AOP {
@Pointcut("execution(* org.example.springaop.blog_demo.Controller.*(..))")
public void pointcut(){}
@Around("pointcut()")
//@Around("execution(* org.example.springaop.blog_demo.Controller.*(..))")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("doAround:业务执行前");
//执行业务
Object result = joinPoint.proceed();
log.info("doAround:业务执行后,result:{}", result);
return "success";
}
@Before("pointcut()")
//@Before("execution(* org.example.springaop.blog_demo.Controller.*(..))")
public void doBefore(){
log.info("doBefore");
}
@After("pointcut()")
//@After("execution(* org.example.springaop.blog_demo.Controller.*(..))")
public void doAfter(){
log.info("doAfter");
}
@AfterReturning(value = "pointcut()",returning = "id")
//@AfterReturning("execution(* org.example.springaop.blog_demo.Controller.*(..))")
public void doAfterReturning(Integer id){
//发生异常时不执行
log.info("doAfterReturning,id:{}", id);
}
@AfterThrowing(value = "pointcut()",throwing = "throwable")
//@AfterThrowing("execution(* org.example.springaop.blog_demo.Controller.*(..))")
public void doAfterThrowing(Throwable throwable){
log.info("doAfterThrowing,throwable:{}", throwable.getMessage());
}
}




作用:@annotation表达式用于匹配带有指定注解的方法,是AOP中实现精准切入的关键方式之一。通过该表达式,可以拦截被特定注解标记的方法,实现逻辑增强
1.创建自定义注解
@Target(ElementType.METHOD)//注解级别:方法注解
@Retention(RetentionPolicy.RUNTIME)//生命周期:运行时
public @interface CustomizeAspect {
}2.使用@annotation表达式描述切点
//添加到切面类中
@Around("@annotation(org.example.springaop.config.CustomizeAspect)")
public Object customize(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("customize before");
Object result = joinPoint.proceed();
log.info("customize after,result:{}", result);
return "success";
}3.将自定义注解添加到连接点上
//添加到Controller类中
@RequestMapping("/c")
@CustomizeAspect
public Object methodC(Integer id){
log.info("执行methodC");
return id;
}执行结果:


作用:用于指定Bean的加载顺序,其核心作用是通过数值定义优先级,数值越小优先级越高
@Component
@Slf4j
@Aspect
@Order(1)
public class AOP1 {
@Pointcut("execution(* org.example.springaop.controller.Controller.*(..))")
public void pointcut(){}
@Around("pointcut()")
//@Around("execution(* org.example.springaop.controller.Controller.*(..))")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("doAround1:业务执行前");
//执行业务
Object result = joinPoint.proceed();
//计算时间
log.info("doAround1:业务执行后,result:{}", result);
return "success1";
}
@Before("pointcut()")
public void doBefore(){
log.info("doBefore1");
}
@After("pointcut()")
public void doAfter(){
log.info("doAfter1");
}
@AfterReturning(value = "pointcut()",returning = "id")
public void doAfterReturning(Integer id){
//发生异常时不执行
log.info("doAfterReturning1,id:{}", id);
}
@AfterThrowing(value = "pointcut()",throwing = "throwable")
public void doAfterThrowing(Throwable throwable){
log.info("doAfterThrowing1,throwable:{}", throwable.getMessage());
}
@Around("@annotation(org.example.springaop.config.CustomizeAspect)")
public Object customize(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("customize before1");
Object result = joinPoint.proceed();
log.info("customize after1,result:{}", result);
return "success1";
}
}@Component
@Slf4j
@Aspect
@Order(2)
public class AOP2 {
@Pointcut("execution(* org.example.springaop.controller.Controller.*(..))")
public void pointcut(){}
@Around("pointcut()")
//@Around("execution(* org.example.springaop.controller.Controller.*(..))")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("doAround2:业务执行前");
//执行业务
Object result = joinPoint.proceed();
//计算时间
log.info("doAround2:业务执行后,result:{}", result);
return "success2";
}
@Before("pointcut()")
public void doBefore(){
log.info("doBefore2");
}
@After("pointcut()")
public void doAfter(){
log.info("doAfter2");
}
@AfterReturning(value = "pointcut()",returning = "id")
public void doAfterReturning(Integer id){
//发生异常时不执行
log.info("doAfterReturning2,id:{}", id);
}
@AfterThrowing(value = "pointcut()",throwing = "throwable")
public void doAfterThrowing(Throwable throwable){
log.info("doAfterThrowing2,throwable:{}", throwable.getMessage());
}
@Around("@annotation(org.example.springaop.config.CustomizeAspect)")
public Object customize(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("customize before2");
Object result = joinPoint.proceed();
log.info("customize after2,result:{}", result);
return "success2";
}
}执行结果:



代理模式(Proxy Pattern)通过创建一个代理对象来控制对目标对象的访问。代理对象作为目标对象的替代品,可以在访问前后添加额外逻辑(如权限控制、性能监控等)

代理类在编译期确定,需要手动为每个目标类编写代理类
以租房为例:租客(调用方)、房东(目标对象)、中介(代理对象)
public interface IHouse {
void rent();
void sell();
}//房东
public class RealHouse implements IHouse {
@Override
public void rent() {
System.out.println("房东出租房子");
}
@Override
public void sell() {
System.out.println("房东售卖房子");
}
}//中介
public class HouseProxy implements IHouse {
private final IHouse realHouse;
public HouseProxy(IHouse realHouse) {
this.realHouse = realHouse;
}
@Override
public void rent() {
System.out.println("开始代理");
realHouse.rent();
System.out.println("结束代理");
}
@Override
public void sell() {
System.out.println("开始代理");
realHouse.sell();
System.out.println("结束代理");
}
public static void main(String[] args) {
IHouse iHouse = new HouseProxy(new RealHouse());
iHouse.rent();
iHouse.sell();
}
}执行结果: 开始代理 房东出租房子 结束代理 开始代理 房东售卖房子 结束代理
AOP的底层原理依赖于
动态代理:不需要针对每一个目标对象创建一个代理对象,而是将代理对象的创建时机推迟到程序运行时交由JVM完成
JDK动态代理是Java标准库提供的方式,要求目标对象必须实现接口。通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口动态生成代理类,生成的代理对象和目标对象实现自同一接口(而非与目标类本身有直接继承关系),与上述静态代理类似,但把代理对象的生成时机推迟到程序运行时Proxy.newProxyInstance()是Java动态代理的核心方法,用于在运行时创建代理对象。该方法接收三个参数:
@Slf4j
public class JDKInvocationHandler implements InvocationHandler {
private final Object realHouse;
public JDKInvocationHandler(Object realHouse) {
this.realHouse = realHouse;
}
@Override
//Object proxy:生成的代理对象实例,Method method:被调用的目标方法对象,Object[] args:调用目标方法时传入的参数数组
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("JDK开始代理");
//调用realHouse的方法
Object invoke = method.invoke(realHouse, args);
log.info("JDK结束代理,invoke:{}", invoke);
return invoke;
}
}public class Main {
public static void main(String[] args) {
IHouse target = new RealHouse();
//JDK动态代理,只能代理接口
IHouse iHouse = (IHouse)Proxy.newProxyInstance(
RealHouse.class.getClassLoader(),
new Class[]{IHouse.class},
new JDKInvocationHandler(target)
);
iHouse.rent();
iHouse.sell();
}
}运行结果:

CGLIB(Code Generation Library)是第三方库,基于字节码增强(动态生成目标类的子类字节码,重写方法逻辑),通过继承方式实现代理,不要求目标对象实现接口。通过org.springframework.cglib.proxy.Enhancer类动态生成目标类的子类作为代理

//interface MethodInterceptor extends Callback
@Slf4j
public class CGLibMethodInterceptor implements MethodInterceptor {
private final Object realHouse;
public CGLibMethodInterceptor(Object realHouse) {
this.realHouse = realHouse;
}
@Override
//Object obj:动态生成的代理对象实例,Method method:当前被拦截的目标方法对象,Object[] args:方法调用时传入的参数数组,MethodProxy proxy:方法代理对象
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
log.info("开始代理");
IHouse result = (IHouse)method.invoke(realHouse, args);
log.info("结束代理");
return result;
}
}public class Main {
public static void main(String[] args) {
RealHouse target = new RealHouse();
RealHouse realHouse = (RealHouse)Enhancer.create(
RealHouse.class,
new CGLibMethodInterceptor(target)
);
realHouse.rent();
realHouse.sell();
}
}执行结果:
