springAOP初学笔记总结及练习代码

AOP概念

AOP(Aspect-Oriented Programming,面向切面编程),简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或业务封装起来,再把封装功能整合到业务中。

AOP的核心思想就是“将应用程序中的业务逻辑同对其提供支持的通用服务进行分离"

好处:

1,便于减少系统的重复代码

2,降低模块间的耦合度

3,有利于未来的可操作性和可维护性。

例如:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

aop:切面 = 切点 + 增强(通知)
@Aspect:作用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出异常或者正常退出都会执行

 环绕通知具体实现方法   相当于前置和后置通知的整合 但是也有区别:
 1.前后置通知无法决定是否调用业务方法  而环绕通知可以决定是否调用业务方法
 2.前后置通知无法获取业务方法的返回值,而环绕通知可以获取执行业务方法的返回值
 3.目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知是不能决定的,
  它们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。joinPoint.proceed()就是执行目标方法的代码。
 4.环绕通知可以控制返回对象,即可以返回一个与目标对象完全不同的返回值。虽然这很危险,但是却可以做到。

aop有两种方式:1.手动配置xml文件 2.使用注解

一:手动配置xml文件

首先要先导包(这里可以直接复制)

   <!--公共版本  -->
    <properties>
        <spring.version>5.3.16</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--aop包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

创建一个util工具类(这里模拟一个日志记录)

package com.qyq.utils;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
@Component
// 在使用springboot写aop的时候,有个JoinPoint类,用来获取代理类和被代理类的信息。
public class LogrecordUtil {
    public void logRecord(JoinPoint joinPoint ){
        //获取目标对象
        String targeName = joinPoint.getTarget().getClass().getName();
        //获取方法名称
        String methodName = joinPoint.getSignature().getName();
        // 获取执行方法参数
        Object[] args = joinPoint.getArgs();
        if (args!= null && args.length>0){
            args[0].getClass().getName();
        }
        System.out.println("在"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) +"执行"+targeName+"中的:"+methodName+",该方法参数为:"+args);
    }

    /**
     * 前置通知
     * @param joinPoint
     */
    public void beforeNotice(JoinPoint joinPoint){
        System.out.println("----------在业务方法执行之前做的事--------------");
    }

    /**
     * 后置通知
     * @param joinPoint
     */
    public void beafter(JoinPoint joinPoint){
        System.out.println("----------在业务方法执行之后做的事--------------");
    }

    /** 环绕通知具体实现方法   相当于前置和后置通知的整合 但是也有区别:
     *  1,前后置通知无法决定是否调用业务方法  而环绕通知可以决定是否调用业务方法
     *  2,前后置通知无法获取业务方法的返回值,而环绕通知可以获取执行业务方法的返回值
     *
     *  3.目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知是不能决定的,
     *  它们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。joinPoint.proceed()就是执行目标方法的代码。
     * 4.环绕通知可以控制返回对象,即可以返回一个与目标对象完全不同的返回值。虽然这很危险,但是却可以做到。
     *
     * JoinPoint是proceedingJoinPoint的父类
     * @param proceedingJoinPoint
     * @return
     */
    public Object aroundNotice(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("-----环绕通知,在执行业务之前做的事--------");
        Object result = null;
        try {
            //调用业务方法
            result = proceedingJoinPoint.proceed();

        }catch (Throwable throwable){
            throwable.printStackTrace();
        }
        System.out.println("业务方法的返回值:"+result);
        System.out.println("-----环绕通知,在执行业务之后做的事--------");
        return result;
    }

    /**
     *  异常通知
     * @param joinPoint
     * @param e
     */
    public void exceptionNotice(JoinPoint joinPoint,Exception e){
        System.out.println("---当业务出现异常时,才执行此异常方法---在执行"+joinPoint.getSignature().getName()+"出现了"+e.getClass().getName()+"异常描述:"+e.getMessage());
    }

    /**
     * 最终通知
     * @param joinPoint
     */
    public void finallyNotice(JoinPoint joinPoint){
        System.out.println("after无论包不报错都会执行此方法");
    }
}

手动配置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"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        		http://www.springframework.org/schema/context
        		http://www.springframework.org/schema/context/spring-context.xsd">


    <context:component-scan base-package="com.qyq"></context:component-scan>
    <!--开启切面自动代理功能 扫描-->
    <!--<aop:aspectj-autoproxy></aop:aspectj-autoproxy>-->
<!-- spring aop 配置
    切面 = 切点 + 增强(通知)
-->

    <aop:config>
        <!-- 切入点配置 让aop配置喝业务联系-->
        <aop:pointcut id="pointcutA" expression="execution(* com.qyq.service.impl.*.*(..))"/>
        <aop:pointcut id="pointcutB" expression="execution(* com.qyq.service.impl.*.add*(..))
                                                ||execution(* com.qyq.service.impl.*.update*(..))
                                                ||execution(* com.qyq.service.impl.*.del*(..))"/>

        <!--切面配置 -->
        <aop:aspect ref="logrecordUtil">
            <!--前置通知-->
<!--            <aop:before method="beforeNotice" pointcut-ref="pointcutA"></aop:before>-->
            <!--后置通知-->
            <aop:after-returning method="logRecord"  pointcut-ref="pointcutB"></aop:after-returning>
            <!--环绕通知-->
<!--            <aop:around method="aroundNotice" pointcut-ref="pointcutA"></aop:around>-->
            <!--异常通知-->
            <aop:after-throwing method="exceptionNotice" pointcut-ref="pointcutB" throwing="e" ></aop:after-throwing>
            <!--最终通知-->
            <aop:after method="finallyNotice" pointcut-ref="pointcutB"></aop:after>
        </aop:aspect>
    </aop:config>

</beans>

提前创建好一个实体类

配置完后就可以进行测试了

import com.qyq.entity.Dept;
import com.qyq.service.DeptService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest {
    @Test
    public void testAop(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:SpringAop.xml");
        //获取的首字母一定要小写 deptServiceImpl
        DeptService deptService = (DeptService) applicationContext.getBean("deptServiceImpl");

        Dept dept = new Dept("张三");
        deptService.findAll();
        deptService.addDept(dept);
        deptService.delById(1);
        deptService.updateDept(1);


    }
}

二:使用注解开启aop 

使用注解的话只需要把上面手动配置xml中的    <aop:config></aop:config> 中的代码注释掉,开启aop自动代理扫描即可:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.qyq.utils;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogrecordUtil02 {
    //  配置切入点
    @Pointcut(value = "execution(* com.qyq.service.impl.*.add*(..))" +
            "||execution(* com.qyq.service.impl.*.update*(..))" +
            "||execution(* com.qyq.service.*.del*(..))")
    public void poinCutA() {

    }

    //后置通知 统一记录日志
//    @AfterReturning("poinCutA()")
//    public void logRecord(JoinPoint joinPoint) {
//        //获取目标对象
//        String targetNaem = joinPoint.getTarget().getClass().getName();
//        //获取方法名称
//        String methodName = joinPoint.getSignature().getName();
//        //获取参数
//        Object[] args = joinPoint.getArgs();
//        String argsType = null;
//        if (args != null && args.length > 0) {
//            argsType = args[0].getClass().getName();
//        }
//        System.out.println("在" + new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()) + "执行" + targetNaem + "中的方法" + methodName + "该方法的参数有:" + argsType);
//    }

    //前置通知
//    @Before("poinCutA()")
//    public void beforeNotice(JoinPoint joinPoint) {
//        System.out.println("--------在程序前运行的代码-------------");
//    }

    //环绕通知
    @Around("poinCutA()")
    public Object aroundNotice(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("------前置通知------");
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("业务方法的返回值是:" + result);
        System.out.println("-------执行的后置通知------");
        return result;
    }

    //异常通知
    @AfterThrowing(value = "poinCutA()", throwing = "e")
    public void throwingNotice(JoinPoint joinPoint, Exception e) {
        System.out.println("代码异常执行的方法--" + joinPoint.getSignature().getName() + ",出现的异常为:" + e.getClass().getName() + "报的错为:" + e.getMessage());
    }

    // 最终通知
    @After("poinCutA()")
    public void afterNotice(JoinPoint joinPoint) {
        System.out.println("最终执行的方法After,不管无论有没有异常都会执行");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值