Spring AOP 面向切面编程

一、AOP核心概念与实战引入

AOP 的作用:在程序运行期间,在不修改源代码的基础上,对已有方法进行增强(无侵入性:解耦)。

1.1 传统开发中的痛点

以用户登录场景为例,假设我们有一个基础登录功能:

public class LoginService {
    public void login(String username, String password) {
        // 验证用户名密码
        System.out.println("核心登录逻辑执行");
    }
}

现在需要新增权限校验功能,传统方案有两种:

  1. 修改源代码:侵入性强,违反开闭原则
  2. 纵向继承扩展:产生冗余代码,维护困难

1.2 AOP的破局之道

// 目标类
public class LoginService {
    public void login(String username) {
        System.out.println(username + "登录成功!");
    }
}

// 切面类
@Aspect
@Component
public class AuthAspect {
    @Before("execution(* com.example.service.LoginService.login(..))")
    public void checkPermission() {
        System.out.println("[权限校验] 正在验证用户权限...");
    }
}

运行效果

[权限校验] 正在验证用户权限...
admin登录成功!

二、AOP底层原理深度剖析

1. 代理模式双雄

代理类型实现方式适用场景
JDK动态代理基于接口实现目标类有接口时优先使用
CGLIB动态代理生成子类继承目标类无接口或需要高性能场景

2. 动态代理实现原理

// JDK动态代理示例
public class JdkProxy implements InvocationHandler {
    private Object target;
    
    public Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置增强");
        Object result = method.invoke(target, args);
        System.out.println("后置增强");
        return result;
    }
}

三、XML配置方式

1.AOP相关的术语

Joinpoint(连接点) 类里面有哪些方法可以增强这些方法称为连接点

Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

Advice(通知/增强)-- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

Aspect(切面)-- 是 切入点+通知 的结合,以后咱们自己来编写和配置的

2. 核心配置模板

<!--配置 AOP 的增强-->
<aop:config>
    <!--配置切面 = 切入点 + 通知组成-->
    <aop:aspect ref="myXmlAspect">
        <!--前置通知:UserServiceImpl 的 save 方法执行前,会增强-->
        <aop:before method="log" pointcut="execution(public void
com.qcbyjy.demo2.UserServiceImpl.save())" />
    </aop:aspect>
</aop:config>

3. 切入点的表达式

切入点表达式的格式如下:

  •  execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 修饰符可以省略不写,不是必须要出现的。
  • 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
  • 包名例如:com.tx.demo3.BookDaoImpl
  • 首先 com 是不能省略不写的,但是可以使用 * 代替
  • 中间的包名可以使用 * 号代替
  • 如果想省略中间的包名可以使用 ..
  • 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
  • 方法也可以使用 * 号代替
  • 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..

比较通用的表达式:execution(public * com.qcby.*.*ServiceImpl.*(..))

四、注解方式

1. 半注解形式

编写切面类
给切面类添加注解 @Aspect,编写增强的方法,使用通知类型注解声明
package com.qcby;

@Component
@Aspect
public class MyAnnoAspect {
    @Before(value = "execution(* com.qcby.service.Impl.UserServiceImpl.save(..))")
    public void log(){
        System.out.println("增强的方法--前置通知");
    }

    @AfterReturning(value = "execution(* com.qcby..*ServiceImpl.save(..))")
    public void log1(){
        System.out.println("增强的方法-- 后置方法");
    }

    @After(value = "execution(* com.qcby..*ServiceImpl.save(..))")
    public void log2(){
        System.out.println("增强的方法-- 最终通知");
    }
    @AfterThrowing(value = "execution(* com.qcby..*ServiceImpl.save(..))")
    public void log3(){
        System.out.println("增强方法--异常通知");
    }
    @Around(value = "execution(* com.qcby..*ServiceImpl.save(..))")
    public Object log4(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知--执行前");
        Object result = joinPoint.proceed();
        System.out.println("环绕通知--执行后");
        return result;
    }
}
配置文件中开启自动代理
<aop:aspectj-autoproxy/>

2. 通知类型的注解

  • @Before -- 前置通知
  • @AfterReturing -- 后置通知
  • @Around -- 环绕通知(目标对象方法默认不执行的,需要手动执行)
  • @After -- 最终通知
  • @AfterThrowing -- 异常抛出通知

3. 纯注解的方式

@Configuration // 配置类
@ComponentScan(value = "com.qcbyjy.demo3") // 扫描包
@EnableAspectJAutoProxy // 开启自动代理 == <aop:aspectj-autoproxy/>
public class SpringConfig {
}
@Aspect
@Component
public class LogAspect {
    @Around("@annotation(com.example.anno.OperateLog)")
    public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        long time = System.currentTimeMillis() - start;
        System.out.println("方法执行耗时:" + time + "ms");
        return result;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值