Spring 基础教程 (第 14 章) - Spring 集成 AspectJ

我们知道,Spring AOP 是一个简化版的 AOP 实现,并没有提供完整版的 AOP 功能。通常情况下,Spring AOP 是能够满足我们日常开发过程中的大多数场景的,但在某些情况下,我们可能需要使用 Spring AOP 范围外的某些 AOP 功能。

例如 Spring AOP 仅支持执行公共(public)非静态方法的调用作为连接点,如果我们需要向受保护的(protected)或私有的(private)的方法进行增强,此时就需要使用功能更加全面的 AOP 框架来实现,其中使用最多的就是 AspectJ。

AspectJ 是一个基于 Java 语言的全功能的 AOP 框架,它并不是 Spring 组成部分,是一款独立的 AOP 框架。

但由于 AspectJ 支持通过 Spring 配置 AspectJ 切面,因此它是 Spring AOP 的完美补充,通常情况下,我们都是将 AspectJ 和 Spirng 框架一起使用,简化 AOP 操作。

使用 AspectJ 需要在 Spring 项目中导入 Spring AOP 和 AspectJ 相关 Jar 包:

  • spring-aop-xxx.jar
  • spring-aspects-xxx.jar
  • aspectjweaver-xxxx.jar

在以上 3 个 Jar 包中,spring-aop-xxx.jar 和 spring-aspects-xxx.jar 为 Spring 框架提供的 Jar 包,而 aspectjweaver-xxxx.jar 则是 AspectJ 提供的。

基于 AspectJ 实现 AOP 操作

  • 基于 xml 配置实现 AspectJ 的 AOP 开发
  • 基于注解方式实现 AspectJ 的 AOP 开发

14.1 AspectJ Jar 包下载

AspectJ Jar 包的下载步骤如下:

1)使用浏览器访问 AspectJ 包下载页面:https://www.eclipse.org/aspectj/downloads.php,选择相应的版本,这里我们以 1.9.6 稳定版本为例进行介绍。

在这里插入图片描述

2)点击 aspectj-1.9.6.jar 进入下载页面,选择 Select another mirror,如下图:

在这里插入图片描述

3)根据自己所处地区选择下载,这里我们选择的是中国科学技术大学的下载地址:

在这里插入图片描述

4)下载完成后解压该 Jar 文件,需要导入的 jar 包在 files 文件夹的 lib 目录下:

在这里插入图片描述

14.2 基于 XML 使用 AspectJ

我们可以在 Spring 项目中通过 XML 配置,对切面(Aspect 或 Advisor)、切点(PointCut)以及通知(Advice)进行定义和管理,以实现基于 AspectJ 的 AOP 开发。

Spring 提供了基于 XML 的 AOP 支持,并提供了一个名为 aop 的命名空间,该命名空间提供了一个 <aop:config> 元素。

  • 在 Spring 配置中,所有的切面信息(切面、切点、通知)都必须定义在 <aop:config> 元素中;
  • 在 Spring 配置中,可以使用多个 <aop:config>
  • 每一个 <aop:config> 元素内可以包含 3 个子元素: pointcutadvisoraspect ,这些子元素必须按照这个顺序进行声明。

1. 引入 aop 命名空间

首先,我们需要在 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"
       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">
			 ...
</beans>

2. 定义切面 <aop:aspect>

在 Spring 配置文件中,使用 <aop:aspect> 元素定义切面。该元素可以将定义好的 Bean 转换为切面 Bean,所以使用 <aop:aspect> 之前需要先定义一个普通的 Spring Bean。

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>

其中,id 用来定义该切面的唯一标识名称,ref 用于引用普通的 Spring Bean。

3. 定义切入点 <aop:pointcut>

<aop:pointcut> 用来定义一个切入点,用来表示对哪个类中的那个方法进行增强。它既可以在 <aop:pointcut> 元素中使用,也可以在 <aop:aspect> 元素下使用。

  • <aop:pointcut> 元素作为 <aop:config> 元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;
  • <aop:pointcut> 元素作为 <aop:aspect> 元素的子元素时,表示该切入点只对当前切面有效。
<aop:config>
    <aop:pointcut id="myPointCut"
        expression="execution(* section1.demo1.dao.*.*(..))"/>
</aop:config>

其中,id 用于指定切入点的唯一标识名称,execution 用于指定切入点关联的切入点表达式。

execution 的语法格式为:

 execution([权限修饰符][返回值类型][类的完全限定名][方法名称]([参数列表]))

其中:

  • 返回值类型、方法名、参数列表是必须配置的选项,而其它参数则为可选配置项。
  • 返回值类型:* 表示可以为任何返回值。如果返回值为对象,则需指定全路径的类名。
  • 类的完全限定名:指定包名 + 类名。
  • 方法名:* 代表所有方法,set* 代表以 set 开头的所有方法。
  • 参数列表:(..) 代表所有参数;(*) 代表只有一个参数,参数类型为任意类型;(*, String) 代表有两个参数,第一个参数可以为任何值,第二个为 String 类型的值。

举例 1:对 section1.demo1.dao.impl 包下 UserDaoImpl 类中的 add() 方法进行增强,配置如下:

execution(* section1.demo1.dao.impl.UserDaoImpl.add(..))

举例 2:对 section1.demo1.dao.impl 包下 UserDaoImpl 类中的所有方法进行增强,配置如下:

execution(* section1.demo1.dao.impl.UserDaoImpl.*(..))

举例 3:对 section1.demo1.dao.impl 包下所有类中的所有方法进行增强,配置如下:

execution(* section1.demo1.dao.impl.*.*(..))

4. 定义通知

AspectJ 支持 5 种类型的 advice,如下:

<aop:aspect id="myAspect" ref="aBean">
    <!-- 前置通知 -->
    <aop:before pointcut-ref="myPointCut" method="..."/>
    <!-- 后置通知 -->
    <aop:after-returning pointcut-ref="myPointCut" method="..."/>
    <!-- 环绕通知 -->
    <aop:around pointcut-ref="myPointCut" method="..."/>
    <!-- 异常通知 -->
    <aop:after-throwing pointcut-ref="myPointCut" method="..."/>
    <!-- 最终通知 -->
    <aop:after pointcut-ref="myPointCut" method="..."/>
    .... 
</aop:aspect>
例 1

下面我们通过一个示例来演示下 Spring 集成 AspectJ 基于 XML 实现 AOP 开发。

创建命名为 UserDao 的接口,代码如下:

package section2.demo1.dao;

/**
 * ClassName: UserDao
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public interface UserDao {
    void add();

    void delete();

    void modify();

    void get();
}

创建 UserDao 的实现类 UserDaoImpl,代码如下:

package section2.demo1.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import section2.demo1.dao.UserDao;

/**
 * ClassName: UserDaoImpl
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    private static final Log LOGGER = LogFactory.getLog(UserDaoImpl.class);

    @Override
    public void add() {
        LOGGER.info("正在执行 UserDao 的 add() 方法……");
    }

    @Override
    public void delete() {
        LOGGER.info("正在执行 UserDao 的 delete() 方法……");
    }

    @Override
    public void modify() {
        LOGGER.info("正在执行 UserDao 的 modify() 方法……");
    }

    @Override
    public void get() {
        LOGGER.info("正在执行 UserDao 的 get() 方法……");
    }
}

创建命名为 MyUserAspect 的类,代码如下:

package section2.demo1.aspect;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * ClassName: MyUserAspect
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public class MyUserAspect {
    private static final Log LOGGER = LogFactory.getLog(MyUserAspect.class);

    public void before() {
        LOGGER.info("前置增强……");
    }

    public void after() {
        LOGGER.info("最终增强……");
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        LOGGER.info("环绕增强---前……");
        proceedingJoinPoint.proceed();
        LOGGER.info("环绕增强---后……");
    }

    public void afterThrow(Throwable exception) {
        LOGGER.info("异常增强…… 异常信息为:" + exception.getMessage());
    }

    public void afterReturning(Object returnValue) {
        LOGGER.info("后置返回增强…… 方法返回值为:" + returnValue);
    }
}

修改 Spring 配置文件 spring-config.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">

    <!--定义 Bean-->
    <bean id="userDao" class="section2.demo1.dao.impl.UserDaoImpl"/>
    <!--定义切面-->
    <bean id="myUserAspect" class="section2.demo1.aspect.MyUserAspect"/>
    <aop:config>
        <aop:pointcut id="beforePointCut"
                      expression="execution(* section2.demo1.dao.impl.UserDaoImpl.add(..))"/>
        <aop:pointcut id="throwPointCut"
                      expression="execution(* section2.demo1.dao.impl.UserDaoImpl.get(..))"/>
        <aop:pointcut id="afterReturnPointCut"
                      expression="execution(* section2.demo1.dao.impl.UserDaoImpl.modify(..))"/>
        <aop:pointcut id="afterPointCut"
                      expression="execution(* section2.demo1.dao.impl.UserDaoImpl.*(..))"/>
        <aop:aspect ref="myUserAspect">
            <!--前置增强-->
            <aop:before method="before" pointcut-ref="beforePointCut"/>
            <!--后置返回增强-->
            <aop:after-returning method="afterReturning"
                                 pointcut-ref="afterReturnPointCut"
                                 returning="returnValue"/>
            <!--异常通知-->
            <aop:after-throwing method="afterThrow" pointcut-ref="throwPointCut"
                                throwing="exception"/>
            <!--最终通知-->
            <aop:after method="after" pointcut-ref="afterPointCut"/>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="beforePointCut"/>
        </aop:aspect>
    </aop:config>
</beans>

创建命名为 MainApp 的类,代码如下:

package section2.demo1;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import section2.demo1.dao.UserDao;

/**
 * ClassName: MainApp
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public class MainApp {
    private static final Log LOGGER = LogFactory.getLog(MainApp.class);

    public static void main(String[] args) {
        // 获取 ApplicationContext 容器
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        // 获取名为 employee 的 Bean
        UserDao userDao = context.getBean("userDao", UserDao.class);
        LOGGER.info("调用 UserDao 中的方法");
        userDao.add();
        userDao.delete();
        userDao.get();
        userDao.modify();
    }
}

执行 MainApp 中的 main 方法,控制台输出如下:

4月 09, 2022 8:35:34 下午 section2.demo1.MainApp main
信息: 调用 UserDao 中的方法
4月 09, 2022 8:35:34 下午 section2.demo1.aspect.MyUserAspect before
信息: 前置增强……
4月 09, 2022 8:35:34 下午 section2.demo1.aspect.MyUserAspect around
信息: 环绕增强---前……
4月 09, 2022 8:35:34 下午 section2.demo1.dao.impl.UserDaoImpl add
信息: 正在执行 UserDao 的 add() 方法……
4月 09, 2022 8:35:34 下午 section2.demo1.aspect.MyUserAspect around
信息: 环绕增强---后……
4月 09, 2022 8:35:34 下午 section2.demo1.aspect.MyUserAspect after
信息: 最终增强……
4月 09, 2022 8:35:34 下午 section2.demo1.dao.impl.UserDaoImpl delete
信息: 正在执行 UserDao 的 delete() 方法……
4月 09, 2022 8:35:34 下午 section2.demo1.aspect.MyUserAspect after
信息: 最终增强……
4月 09, 2022 8:35:34 下午 section2.demo1.dao.impl.UserDaoImpl get
信息: 正在执行 UserDao 的 get() 方法……
4月 09, 2022 8:35:34 下午 section2.demo1.aspect.MyUserAspect after
信息: 最终增强……
4月 09, 2022 8:35:34 下午 section2.demo1.dao.impl.UserDaoImpl modify
信息: 正在执行 UserDao 的 modify() 方法……
4月 09, 2022 8:35:34 下午 section2.demo1.aspect.MyUserAspect after
信息: 最终增强……
4月 09, 2022 8:35:34 下午 section2.demo1.aspect.MyUserAspect afterReturning
信息: 后置返回增强…… 方法返回值为:null

14.3 基于注解使用 AspectJ

在 Spring 中,虽然我们可以使用 XML 配置文件可以实现 AOP 开发,但如果所有的配置都集中在 XML 配置文件中,就势必会造成 XML 配置文件过于臃肿,从而给维护和升级带来一定困难。

为此,AspectJ 框架为 AOP 开发提供了一套 @AspectJ 注解。它允许我们直接在 Java 类中通过注解的方式对切面(Aspect)、切入点(Pointcut)和增强(Advice)进行定义,Spring 框架可以根据这些注解生成 AOP 代理。

关于注解的介绍如表 1 所示:

名称说明
@Aspect用于定义一个切面。
@Pointcut用于定义一个切入点。
@Before用于定义前置通知,相当于 BeforeAdvice。
@AfterReturning用于定义后置通知,相当于 AfterReturningAdvice。
@Around用于定义环绕通知,相当于 MethodInterceptor。
@AfterThrowing用于定义抛出通知,相当于 ThrowAdvice。
@After用于定义最终通知,不管是否异常,该通知都会执行。
@DeclareParents用于定义引介通知,相当于 IntroductionInterceptor(不要求掌握)。

1. 启用 @AspectJ 注解支持

在使用 @AspectJ 注解进行 AOP 开发前,首先我们要先启用 @AspectJ 注解支持。

我们可以通过以下 2 种方式来启用 @AspectJ 注解:

1)基于 XML 配置启用

在 Spring 的 XML 配置文件中,添加以下内容启用 @AspectJ 注解支持。

<!-- 开启注解扫描 -->
<context:component-scan base-package="section2.demo2"/>
<!-- 开启AspectJ 自动代理 -->
<aop:aspectj-autoproxy/>
2)基于注解配置启用

我们可以在 Java 配置类(标注了 @Configuration 注解的类)中,使用 @EnableAspectJAutoProxy@ComponentScan 注解启用 @AspectJ 注解支持。

@Configuration
@ComponentScan(basePackages = "section2.demo2") // 注解扫描
@EnableAspectJAutoProxy // 开启 AspectJ 的自动代理
public class AppConfig {
  ...
}

2. 定义切面 @Aspect

我们可以通过 @AspectJ 注解将一个 Bean 定义为切面。

在启用了 @AspectJ 注解支持的情况下,Spring 会自动将 IoC 容器(ApplicationContext)中的所有使用了 @AspectJ 注解的 Bean 识别为一个切面。

1)基于 XML 定义

我们可以在 XML 配置中通过一些配置将这个类定义为一个 Bean,如下:

<bean id="myAspect" class="section2.demo2.aspect.MyAspect"/>
   ...
</bean>
2)基于注解定义

在定义完 Bean 后,我们只需要在Bean 对应的 Java 类中使用一个 @AspectJ 注解,将这个 Bean 定义为一个切面,代码如下:

package section2.demo2.aspect;

import org.aspectj.lang.annotation.*;

@Aspect // 定义为切面
public class MyAspect {
  ...
}
3)全注解方式定义

我们也可以在 Java 类上使用以下 2 个注解,使用全注解方式定义切面,示例代码如下:

package section2.demo2.aspect;

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

@Component // 定义成 Bean
@Aspect // 定义为切面
public class MyAspect {
  ...
}

在以上代码中共使用两个注解:

  • @Component 注解:将这个类的对象定义为一个 Bean;
  • @Aspect 注解:则是将这个 Bean 定义为一个切面。

3. 定义切点 @Pointcut

在 AspectJ 中,我们可以使用 @Pointcut 注解用来定义一个切点。需要注意的是,定义为切点的方法,它的返回值类型必须为 void,示例代码如下:

// 要求:方法必须是private,返回值类型为 void,名称自定义,没有参数
@Pointcut("execution(* section2.demo2..*.*(..))")
private void myPointCut() {
  ...
}

@Pointcut 注解中有一个 value 属性,这个属性的值就是切入点表达式。有关切入点表达式的具体介绍请参考本章第二小节 “基于 XML 使用 AspectJ 进行 AOP 开发” 中的 execution 语法格式介绍。

值得注意的是,我除了可以通过切入点表达式(execution)直接对切点进行定义外,还可以通过切入点方法的名称来引用其他的切入点。在使用方法名引用其他切入点时,还可以使用 &&||! 等表示 的含义,示例代码如下:

/**
* 将 section2.demo2.dao.impl 包下 UserDaoImpl 类中的 get() 方法定义为一个切点
*/
@Pointcut(value ="execution(* section2.demo2.dao.impl.UserDaoImpl.get(..))")
public void pointCut1(){
  ...
}
/**
* 将 section2.demo2.dao.impl 包下 UserDaoImpl 类中的 delete() 方法定义为一个切点
*/
@Pointcut(value ="execution(* section2.demo2.dao.impl.UserDaoImpl.delete(..))")
public void pointCut2(){
  ...
}
/**
* 除了 section2.demo2.dao.impl 包下 UserDaoImpl 类中 get() 方法和 delete() 方法外,其他方法都定义为切点
*
* ! 表示 非 ,即 "不是",求补集
* && 表示 与,即 "并且",求交集
* || 表示 或,即 "或者",求并集
*/
@Pointcut(value ="!pointCut1() && !pointCut2()")
public void pointCut3(){
  ...
}

4. 定义通知 Advice

AspectJ 为我们提供了以下 6 个注解,来定义 6 种不同类型的通知(Advice),如下表所示:

注解说明
@Before用于定义前置通知,相当于 BeforeAdvice。
@AfterReturning用于定义后置通知,相当于 AfterReturningAdvice。
@Around用于定义环绕通知,相当于 MethodInterceptor。
@AfterThrowing用于定义抛出通知,相当于 ThrowAdvice。
@After用于定义最终通知,不管是否异常,该通知都会执行。
@DeclareParents用于定义引介通知,相当于 IntroductionInterceptor(不要求掌握)。

以上这些通知注解中都有一个 value 属性,这个 value 属性的取值就是这些通知(Advice)作用的切点(PointCut),它既可以是切入点表达式,也可以是切入点的引用(切入点对应的方法名称),示例代码如下:

@Pointcut(value ="execution(* section2.demo2.dao.impl.UserDaoImpl.get(..))")
public void pointCut1(){
}

@Pointcut(value ="execution(* section2.demo2.dao.impl.UserDaoImpl.delete(..))")
public void pointCut2(){
}

@Pointcut(value ="!pointCut1() && !pointCut2()")
public void pointCut3(){
}

//使用切入点引用
@Before("MyAspect.pointCut3()")
public void around() throws Throwable {
    System.out.println("环绕增强……");
}
//使用切入点表达式
@AfterReturning(value = "execution(* section2.demo2.dao.impl.UserDaoImpl.get(..))" ,returning = "returnValue")
public void afterReturning(Object returnValue){
    System.out.println("方法返回值为:" + returnValue);
}
例 1

下面,我们就通过一个完整的实例,来演示下如何通过注解的方式实现 AspectJ AOP 开发。

创建命名为 UserDao 的接口,代码如下:

package section3.demo1.dao;

import java.util.Date;

/**
 * ClassName: UserDao
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public interface UserDao {
    void add();

    void delete();

    Integer modify();

    String get();
}

创建 UserDao 的实现类 UserDaoImpl,代码如下:

package section3.demo1.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import section3.demo1.dao.UserDao;

import java.util.Date;

/**
 * ClassName: UserDaoImpl
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    private static final Log LOGGER = LogFactory.getLog(UserDaoImpl.class);

    @Override
    public void add() {
        LOGGER.info("正在执行 UserDao 的 add() 方法……");
    }

    @Override
    public void delete() {
        LOGGER.info("正在执行 UserDao 的 delete() 方法……");
    }

    @Override
    public Integer modify() {
        LOGGER.info("正在执行 UserDao 的 modify() 方法……");
        return 1;
    }

    @Override
    public String get() {
        LOGGER.info("正在执行 UserDao 的 get() 方法……");
        return "传陆编程";
    }
}

创建命名为 MyUserAspect 的类,代码如下:

package section3.demo1.aspect;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * ClassName: MyUserAspect
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
@Component // 定义成 Bean
@Aspect // 定义为切面
public class MyUserAspect {
    private static final Log LOGGER = LogFactory.getLog(MyUserAspect.class);

    @Before("execution(* section3.demo1.dao.UserDao.add(..))")
    public void before(JoinPoint joinPoint) {
        LOGGER.info("前置增强……" + joinPoint);
    }

    @After("execution(* section3.demo1.dao.UserDao.get(..))")
    public void after(JoinPoint joinPoint) {
        LOGGER.info("最终增强……" + joinPoint);
    }

    /**
     * 将 section3.demo1.dao 包下的 UserDao 类中的 get() 方法 定义为一个切点
     */
    @Pointcut(value = "execution(* section3.demo1.dao.UserDao.get(..))")
    public void pointCut1() {
    }

    /**
     * 将 section3.demo1.dao 包下的 UserDao 类中的 delete() 方法 定义为一个切点
     */
    @Pointcut(value = "execution(* section3.demo1.dao.UserDao.delete(..))")
    public void pointCut2() {
    }

    //使用切入点引用
    @Around("MyUserAspect.pointCut1()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        LOGGER.info("环绕增强……1");
        Object object = proceedingJoinPoint.proceed();
        LOGGER.info("环绕增强……2>>>" + (object == null ? "无返回值" : object.toString()));
        return object;
    }

    //使用切入点表达式
    @AfterReturning(value = "execution(* section3.demo1.dao.UserDao.modify(..))", returning = "returnValue")
    public void afterReturning(Object returnValue) {
        LOGGER.info("后置返回增强……,方法返回值为:" + returnValue);
    }
}

创建命名为 AppConfig 的类,代码如下:

package section3.demo1.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * ClassName: AppConfig
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
@Configuration
@ComponentScan(basePackages = "section3.demo1") //注解扫描
@EnableAspectJAutoProxy //开启 AspectJ 的自动代理
public class AppConfig {
}

创建命名为 MainApp 的类,代码如下:

package section3.demo1;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import section3.demo1.config.AppConfig;
import section3.demo1.dao.UserDao;

/**
 * ClassName: MainApp
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public class MainApp {
    private static final Log LOGGER = LogFactory.getLog(MainApp.class);

    public static void main(String[] args) {
        // 获取 ApplicationContext 容器
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        // 获取名为 employee 的 Bean
        UserDao userDao = context.getBean("userDao", UserDao.class);
        LOGGER.info("调用 UserDao 中的方法");
        userDao.add();
        userDao.delete();
        userDao.get();
        userDao.modify();
    }
}

执行 MainApp 中的 main 方法,控制台输出如下:

4月 09, 2022 10:25:10 下午 section3.demo1.MainApp main
信息: 调用 UserDao 中的方法
4月 09, 2022 10:25:10 下午 section3.demo1.aspect.MyUserAspect before
信息: 前置增强……execution(void section3.demo1.dao.UserDao.add())
4月 09, 2022 10:25:10 下午 section3.demo1.dao.impl.UserDaoImpl add
信息: 正在执行 UserDao 的 add() 方法……
4月 09, 2022 10:25:10 下午 section3.demo1.dao.impl.UserDaoImpl delete
信息: 正在执行 UserDao 的 delete() 方法……
4月 09, 2022 10:25:10 下午 section3.demo1.aspect.MyUserAspect around
信息: 环绕增强……1
4月 09, 2022 10:25:10 下午 section3.demo1.dao.impl.UserDaoImpl get
信息: 正在执行 UserDao 的 get() 方法……
4月 09, 2022 10:25:10 下午 section3.demo1.aspect.MyUserAspect after
信息: 最终增强……execution(String section3.demo1.dao.UserDao.get())
4月 09, 2022 10:25:10 下午 section3.demo1.aspect.MyUserAspect around
信息: 环绕增强……2>>>传陆编程
4月 09, 2022 10:25:10 下午 section3.demo1.dao.impl.UserDaoImpl modify
信息: 正在执行 UserDao 的 modify() 方法……
4月 09, 2022 10:25:10 下午 section3.demo1.aspect.MyUserAspect afterReturning
信息: 后置返回增强……,方法返回值为:1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

传陆编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值