【Spring学习笔记(十一)】基于注解的SpringAop配置

淺唱寂寞╮ 提交于 2020-02-28 19:15:29

首先我们要配置好我们Maven项目中的pom.xml文件,这里主要是导入Spring和aspectj两个坐标。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>day03_eesy_04AdviceTypeSpring</artifactId>
    <version>1.0-SNAPSHOT</version>

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

        <dependency>
            <!--用于解析切入点表达式-->
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
    </dependencies>
</project>

配置bean.xml文件,扫描我们的包,并且配置Spring开启注解Aop的支持

<?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">

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

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

</beans>

使用@Service对我们的业务层的实现类进行注解

package com.ysw.service.impl;

import com.ysw.service.IAccountService;
import org.springframework.stereotype.Service;

/**
 * 账户的业务层实现类
 */
@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    public void saveAccount() {
        System.out.println("执行了保存");
    }

    public void updateAccount(Integer i) {
        System.out.println("执行了更新" + i);
    }

    public Integer deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}

配置我们的切面Logger类

关键注解:

       @Aspect

       @Pointcut("execution(切入点表达式)")

       private void pt1(){};     //切记这个特殊的,pointcut配置时一定要是一个方法体

       @Before(“pt1()”)        //前置通知

       @AfterReturning("pt1()")    //后置通知

       @AfterThrowing("pt1()")    //异常通知

       @After("pt1()")         //最终通知

       @EnableAspectJAutoProxy      //自动开启Spring对注解Aop的支持

       @ComponentScan(basePackages="com.ysw")    //扫描包

       @Around("pt1()")      //环绕通知

package com.ysw.utils;

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

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

/**
 * 因为这里service、Repository、Controller都不合适
 * 所以用@Component
 */
@Component("logger")
@Aspect  //表示当前类为切面类
public class Logger {

    /**
     * 注意,这四个通知的执行顺序是有问题的,但是环绕通知没有问题
     * 切面就是我们的logger类
     * 切点就是我们service层的impl中的方法
     * 通知就是我们切面中的方法
     */

    @Pointcut("execution(* com.ysw.service.impl.*.*(..))")
    private void pt1(){};

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

    /**
     * 后置通知
     */
    @AfterReturning("pt1()")
    public void afterReturningPrintLog(){
        System.out.println("后置通知:Logger类中的afterReturningPrintLog开始记录日志了......");
    }

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

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

    /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理中的环绕通知,
     *      有明确的业务层切入点方法调用,而我们的代码中没有
     *
     * 解决:
     *      Spring框架为我们提供了一个接口,ProceedingJoinPoint
     *      该接口有一个方法proceed(),此方法相当于明确调用切入点方法
     *      该接口可以作为环绕通知的方法参数,在程序执行时Spring框架会为我们提供该接口的实现类供我们使用
     *
     * Spring中的环绕通知:
     *      它是Spring框架为我们提供的一种可以在代码中通过手动控制的方式控制增强方法何时执行的方式
     *
     */
//    @Around("pt1()")
    public Object aroundPrintLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            Object[] args = pjp.getArgs();  //得到方法执行所需要的参数

            //把通知写在之前,就是前置通知
            System.out.println("Logger类中的aroundPrintLog开始记录日志了......");

            rtValue = pjp.proceed(args);      //明确调用业务层方法,也叫切入点方法

            //把通知写在之后,就是后置通知
            System.out.println("Logger类中的aroundPrintLog开始记录日志了......");

            return rtValue;
        } catch (Throwable throwable) {
            //把通知写在异常处,就是异常通知
            System.out.println("Logger类中的aroundPrintLog开始记录日志了......");

            throw new RuntimeException(throwable);
        } finally {
            //把异常写在finally就是最终通知
            System.out.println("Logger类中的aroundPrintLog开始记录日志了......");
        }
    }

}

 

 

 

 

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!