SSM--Spring复习(三)

Spring AOP

AOP 介绍

AOP(Aspect Oriented Programing:面向切面(方面)编程),是对 OOP 的补充,相对于 OOP 关注的核心任务,AOP 关注的则是一些增益性非核心任务。AOP 技术是一种“横切”技术,它将一些大量重复的、不利于模块重用的横切代码封装成公共可重用模块,从而减少系统重复的代码,降低模块间的耦合度。

Spring AOP 的实现,依赖于动态代理模式,建议学习 AOP 之前,先学习一下代理模式,这样更易于对 AOP 的理解。Spring 使用 JDK 动态代理CGLIB 动态代理 实现 AOP 编程。

JDK 动态代理:

只能针对实现了接口的类进行代理,不能直接对类进行代理;基于反射实现,生成代理对象比较高效;Spring AOP 默认使用 JDK 动态代理。

CGLIB 动态代理:

主要是针对类进行代理(对实现了接口的类也可以代理),对指定的类生成一个子类,生成类的过程效率较低,但执行过程效率很高。类或者类中的方法最好不要使用 final 修饰。使用 CGLIB 需导入 cglib.jar。目前 Spring 已经内嵌了对 CGLIB 的支持,无需再导包。

  • AOP 常见应用场景:日志记录,事务处理,异常处理,性能统计,安全控制等。

  • Spring AOP 实现有三种方式:注解配置方式XML配置方式Schema 配置方式

AOP 相关概念

  • 连接点:目标类中的方法

  • 通知:横切逻辑(比如日志记录、事务处理)。

  • 切入点:目标类中切入了通知的连接点

  • 切面:通知 + 切入点

  • 通知类型:

    • @Before 前置通知:在连接点(目标方法)执行之前执行。

    • @After 后置通知:在连接点执行完毕(正常结束或抛出异常结束)后执行。

    • @AfterReturning 正常返回通知: 在连接点正常执行借宿后执行,如果连接点抛出异常,则不会执行。

    • @AfterThrowing 异常返回通知:在连接点抛出异常后执行。

    • @Around 环绕通知:在连接点执行前后执行,如果连接点抛出异常,则后半部分通知不执行。

  • 切入点表达式:

    • execution(* 包.*.*(..)) - 向包下所有类的所有方法织入通知。

    • execution(* 包.类.*(..)) - 向包下指定类的所有方法织入通知。

    • execution(* 包.类.方法(..)) - 向包下指定类的指定方法织入通知。

jar 包支持

为了使用 Spring AOP 功能,项目中需引入 aspects.jar :

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.20</version>
</dependency>

准备目标类

下文中通过对目标类 DemoBean 的方法切入运行日志记录,演示注解方式使用 Spring AOP。

@Component
public class DemoBean {
    public int showInfo(){
        System.out.println("show info");
        //异常代码
        //"".charAt(1);
        return 1;
    }
​
    public void showMsg(){
        System.out.println("show message");
    }
}

注解配置

@Log4j
@Component
@Aspect
public class LogAspect {
    @Pointcut("execution(* com.lsy.demo.aop.*.*(..))")
    public void pointCut(){}
    
    @Pointcut("execution(* com.lsy.demo.aop.DemoBean.showInfo(..))")
    public void pointCut1(){}
​
    @Pointcut("execution(* com.lsy.demo.aop.DemoBean.showMsg(..))")
    public void pointCut2(){}
​
    // JoinPoint 即连接点(目标类中的方法),需要就添加此形参,不需要就无需添加
    @Before("pointCut()")
    public void beforeAdvice(JoinPoint joinPoint){
        log.info("beforeAdvice: 方法开始执行");
    }
​
    @After("pointCut()")
    public void afterAdvice(){
        log.info("afterAdvice: 方法执行完毕");
    }
​
    @AfterThrowing("pointCut()")
    public void adviceThrow(){
        log.info("异常通知:在连接点抛出异常退出时执行的通知");
    }
​
    @AfterReturning("pointCut()")
    public void adviceAfterReturn(){
        log.info("最终通知:方法执行完毕返回值后执行的通知");
    }
​
    // ProceedingJoinPoint 即连接点(目标类中的方法)
    @Around("pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("before: 方法开始执行 -------------");
        Object obj = joinPoint.proceed();
        log.info("after: 方法执行完毕 ---------------");
        return obj;
    }
}

启用 AOP

方式1:XML 配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!-- proxy-target-class 默认为false,即使用 JDK 动态代理;true 代表使用 CGLIB动态代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

方式2:注解配置

@Configuration
@EnableAspectJAutoProxy
public class SpringConfig {}

常用场景示例

@Log4j
@Component
@Aspect
public class DemoAop {
    @Pointcut("execution(* com.lsy.demo.aop.*.*(..))")
    public void pointCut(){}
    
     /** 1. 日志记录 */
    @Around("pc()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("方法开始执行......");
        Object obj = joinPoint.proceed();     
        log.info("方法结束执行......");
        return obj;
    }
    
    /** 2. 性能统计 */
    @Around("pc()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        long begin = System.currentTimeMillis();
        Object obj = joinPoint.proceed();       //目标类方法执行   
        long end = System.currentTimeMillis();
        System.out.println(end - begin);
        return obj;
    }
​
    /** 3.异常处理 */
    @Around("pc()")
    public Object aroundAdvice2(ProceedingJoinPoint joinPoint) {
        Object obj = null;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        return obj;
    }
}    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值