Spring框架学习day03 基于注解的 AOP 配置

以一个 Logger 工具类和 账户转账 作为示例

环境搭建

第一步:准备必要的代码

一个 AccountService 接口

/**
 * 账户的业务层接口
 */
public interface AccountService {

    /**
     * 模拟保存账户
     */
    void saveAccount();

    /**
     * 模拟更新账户
     * @param id
     */
    void updateAccountById(int id);

    /**
     * 模拟删除账户
     * @return
     */
    int deleteAccountById();
}

一个 AccountServiceImpl 实现了实现 AccountService 接口

/**
 * 账户业务层实现类
 */
public class AccountServiceImpl implements AccountService {
    public void saveAccount() {
        System.out.println("保存账户");
    }

    public void updateAccountById(int id) {
        System.out.println("更新账户");
    }

    public int deleteAccountById() {
        System.out.println("删除账户");
        return 0;
    }
}

一个logger工具类

/**
 * 用于记录日志的工具类,它里面提供了公共代码
 */
public class Logger {

    /**
     * 前置通知
     */
    public void beforePrintLog(){
        System.out.println("前置通知 logger类中的printLog方法开始记录日志");
    }

    /**
     * 后置通知
     */
    public void afterReturnPrintLog(){
        System.out.println("后置通知 logger类中的printLog方法开始记录日志");
    }

    /**
     * 异常通知
     */
    public void afterThrowingPrintLog(){
        System.out.println("异常通知 logger类中的printLog方法开始记录日志");
    }

    /**
     * 最终通知
     */
    public void afterPrintLog(){
        System.out.println("最终通知 logger类中的printLog方法开始记录日志");
    }

    /**
     * 环绕通知
     */
    public void afterPrintLog(){
        System.out.println("环绕通知 logger类中的printLog方法开始记录日志");
    }

第二步:引入 jar包 创建 spring 的配置文件并导入约束

引入 jar

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
    </dependencies>

创建 bean.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">



</beans>

第三步:把资源使用注解配置

AccountService

/**
 * 账户业务层实现类
 */
@Service("accountService")
public class AccountServiceImpl implements AccountService {
		...
		省略中间重复内容
		...
}

第四步:在配置文件中指定 spring 要扫描的包

    <!-- 配置spring创建容器时要扫描的包 -->
    <context:component-scan base-package="cn.itcast"></context:component-scan>

配置步骤

第一步:在 spring 配置文件中开启注解 AOP 的支持

    <!-- 配置spring开启注解AOP的支持 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

第二步:把 Logger 类也使用注解配置

/**
 * 用于记录日志的工具类,它里面提供了公共代码
 */
@Component("logger")
public class Logger {
		...
		省略中间重复内容
		...
}

第三步:在通知类上使用@Aspect 注解声明为切面

作用: 把当前类声明为切面类

@Component("logger")
@Aspect
public class Logger {
		...
		省略中间重复内容
		...
}

第四步:在增强的方法上使用注解配置通知

/**
 * 用于记录日志的工具类,它里面提供了公共代码
 */
@Component("logger")
@Aspect
public class Logger {

    @Pointcut("execution(* cn.itcast.service.impl.*.*(..))")
    public void pt1(){};

    /**
     * 前置通知
     */
    @Before("pt1()")
    public void beforePrintLog(){
        System.out.println("前置通知 logger类中的printLog方法开始记录日志");
    }

    /**
     * 后置通知
     */
    @AfterReturning("pt1()")
    public void afterReturnPrintLog(){
        System.out.println("后置通知 logger类中的printLog方法开始记录日志");
    }

    /**
     * 异常通知
     */
    @AfterThrowing("pt1()")
    public void afterThrowingPrintLog(){
        System.out.println("异常通知 logger类中的printLog方法开始记录日志");
    }

    /**
     * 最终通知
     */
    @After("pt1()")
    public void afterPrintLog(){
        System.out.println("最终通知 logger类中的printLog方法开始记录日志");
    }
}

环绕通知注解配置

问题:
当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
分析:
通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有
解决:
spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法
该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用

    @Pointcut("execution(* cn.itcast.service.impl.*.*(..))")
    public void pt1(){};

    @Around("pt1()")
    public Object aroundPrintLog(ProceedingJoinPoint pjp){
        Object retrunValue= null;
        try {
            System.out.println("环绕通知 logger类中的printLog方法开始记录日志  前置通知");
            Object[] args = pjp.getArgs();
            retrunValue = pjp.proceed();    //明确调用业务层方法(切入点方法)
            System.out.println("环绕通知 logger类中的printLog方法开始记录日志  后置通知");

        } catch (Throwable throwable) {
            System.out.println("环绕通知 logger类中的printLog方法开始记录日志  异常通知");
            throwable.printStackTrace();
        } finally {
            System.out.println("环绕通知 logger类中的printLog方法开始记录日志  最终通知");

        }
        return retrunValue;

    }
发布了17 篇原创文章 · 获赞 0 · 访问量 146

猜你喜欢

转载自blog.csdn.net/weixin_46539792/article/details/105349621