Spring 基础教程 (第 13 章) - Spring AOP

Spring AOP 是 Spring 框架的核心模块之一,它使用纯 Java 实现,因此不需要专门的编译过程和类加载器,可以在程序运行期通过代理方式向目标类织入增强代码。

13.1 Spring AOP 代理机制

Spring 在运行期会为目标对象生成一个动态代理对象,并在代理对象中实现对目标对象的增强。

Spring AOP 的底层是通过以下 2 种动态代理机制,为目标对象(Target Bean)执行横向织入的。如下表所示:

代理技术描述
JDK 动态代理Spring AOP 默认的动态代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类进行代理。
CGLIB 动态代理若目标对象没有实现任何接口,Spring 则使用 CGLIB 库生成目标对象的子类,以实现对目标对象的代理。

注意:由于被标记为 final 的方法是无法进行覆盖的,因此这类方法不管是通过 JDK 动态代理机制还是 CGLIB 动态代理机制都是无法完成代理的。

13.2 Spring AOP 连接点

Spring AOP 并没有像其他 AOP 框架(例如 AspectJ)一样提供了完成的 AOP 功能,它是 Spring 提供的一种简化版的 AOP 组件。其中最明显的简化就是,Spring AOP 只支持一种连接点类型:方法调用。您可能会认为这是一个严重的限制,但实际上 Spring AOP 这样设计的原因是为了让 Spring 更易于访问。

方法调用连接点是迄今为止最有用的连接点,通过它可以实现日常编程中绝大多数与 AOP 相关的有用的功能。如果需要使用其他类型的连接点(例如成员变量连接点),我们可以将 Spring AOP 与其他的 AOP 实现一起使用,最常见的组合就是 Spring AOP + ApectJ。

13.3 Spring AOP 通知类型

AOP 联盟为通知(Advice)定义了一个 org.aopalliance.aop.Interface.Advice 接口。

Spring AOP 按照通知(Advice)织入到目标类方法的连接点位置,为 Advice 接口提供了 6 个子接口,如下表所示:

通知类型接口描述
前置通知org.springframework.aop.MethodBeforeAdvice在目标方法执行前实施增强。
后置通知org.springframework.aop.AfterAdvice在目标方法执行后实施增强。
后置返回通知org.springframework.aop.AfterReturningAdvice在目标方法执行完成,并返回一个返回值后实施增强。
环绕通知org.aopalliance.intercept.MethodInterceptor在目标方法执行前后实施增强。
异常通知org.springframework.aop.ThrowsAdvice在方法抛出异常后实施增强。
引入通知org.springframework.aop.IntroductionInterceptor在目标类中添加一些新的方法和属性。

13.4 Spring AOP 切面类型

Spring 使用 org.springframework.aop.Advisor 接口表示切面的概念,实现对通知(Adivce)和连接点(Joinpoint)的管理。

在 Spring AOP 中,切面可以分为三类:一般切面、切点切面和引介切面。如下表所示:

切面类型接口描述
一般切面org.springframework.aop.AdvisorSpring AOP 默认的切面类型。 由于 Advisor 接口仅包含一个 Advice(通知)类型的属性,而没有定义 PointCut(切入点),因此它表示一个不带切点的简单切面。 这样的切面会对目标对象(Target)中的所有方法进行拦截并织入增强代码。由于这个切面太过宽泛,因此我们一般不会直接使用。
切点切面org.springframework.aop.PointcutAdvisorAdvisor 的子接口,用来表示带切点的切面,该接口在 Advisor 的基础上还维护了一个 PointCut(切点)类型的属性。 使用它,我们可以通过包名、类名、方法名等信息更加灵活的定义切面中的切入点,提供更具有适用性的切面。
引介切面org.springframework.aop.IntroductionAdvisorAdvisor 的子接口,用来代表引介切面,引介切面是对应引介增强的特殊的切面,它应用于类层面上,所以引介切面适用 ClassFilter 进行定义。

本节我们着重对 Advisor PointcutAdvisor 进行讲解,至于 IntroductionAdvisor,我们会在后面的教程中进行介绍。

13.5 一般切面的 AOP 开发

当我们在使用 Spring AOP 开发时,若没有对切面进行具体定义,Spring AOP 会通过 Advisor 为我们定义一个一般切面(不带切点的切面),然后对目标对象(Target)中的所有方法连接点进行拦截,并植入增强代码。

例 1

下面我们就通过一个简单的实例演示下一般切面的 AOP 开发流程。

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

package section5.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 section5.demo1.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import section5.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() 方法……");
    }
}

创建命名为 UserDaoBeforeAdvice 的前置增强类,代码如下:

package section5.demo1.advice;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

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

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        LOGGER.info("正在执行前置增强操作…………");
    }
}

修改 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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">


    <!--******Advisor:代表一般切面,Advice 本身就是一个切面,对目标类所有方法进行拦截(* 不带有切点的切面.针对所有方法进行拦截)*******-->
    <!-- 定义目标(target)对象 -->
    <bean id="userDao" class="section5.demo1.dao.impl.UserDaoImpl"/>
    <!-- 定义增强 -->
    <bean id="beforeAdvice" class="section5.demo1.advice.UserDaoBeforeAdvice"/>
    <!--通过配置生成代理 UserDao 的代理对象 -->
    <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 设置目标对象 -->
        <property name="target" ref="userDao"/>
        <!-- 设置实现的接口, value 中写接口的全路径 -->
        <property name="proxyInterfaces" value="section5.demo1.dao.UserDao"/>
        <!-- 需要使用 value:增强 Bean 的名称 -->
        <property name="interceptorNames" value="beforeAdvice"/>
    </bean>

</beans>

Spring 能够基于 org.springframework.aop.framework.ProxyFactoryBean 类,根据目标对象的类型(是否实现了接口)自动选择使用 JDK 动态代理或 CGLIB 动态代理机制,为目标对象(Target Bean)生成对应的代理对象(Proxy Bean)。

ProxyFactoryBean 的常用属性,如下表所示:

属性描述
target需要代理的目标对象(Bean)
proxyInterfaces代理需要实现的接口,如果需要实现多个接口,可以通过 元素进行赋值。
proxyTargetClass针对类的代理,该属性默认取值为 false(可省略), 表示使用 JDK 动态代理;取值为 true,表示使用 CGlib 动态代理
interceptorNames拦截器的名字,该属性的取值既可以是拦截器、也可以是 Advice(通知)类型的 Bean,还可以是切面(Advisor)的 Bean。
singleton返回的代理对象是否为单例模式,默认值为 true。
optimize是否对创建的代理进行优化(只适用于CGLIB)。

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

package section5.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 section5.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");
        // 获取名为 userDaoProxy 的 Bean
        UserDao userDao = context.getBean("userDaoProxy", UserDao.class);
        LOGGER.info("调用 UserDao 中的方法");
        userDao.add();
        userDao.delete();
        userDao.get();
        userDao.modify();
    }
}

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

4月 09, 2022 4:35:16 下午 section5.demo1.MainApp main
信息: 调用 UserDao 中的方法
4月 09, 2022 4:35:16 下午 section5.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 4:35:16 下午 section5.demo1.dao.impl.UserDaoImpl add
信息: 正在执行 UserDao 的 add() 方法……
4月 09, 2022 4:35:16 下午 section5.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 4:35:16 下午 section5.demo1.dao.impl.UserDaoImpl delete
信息: 正在执行 UserDao 的 delete() 方法……
4月 09, 2022 4:35:16 下午 section5.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 4:35:16 下午 section5.demo1.dao.impl.UserDaoImpl get
信息: 正在执行 UserDao 的 get() 方法……
4月 09, 2022 4:35:16 下午 section5.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 4:35:16 下午 section5.demo1.dao.impl.UserDaoImpl modify
信息: 正在执行 UserDao 的 modify() 方法……

从控制台输出可以看出,UserDao 接口中的所有方法都被增强了。

注意:需要将以下依赖引入到工程中,本教程已经把它们放到了 Spring-5.3.18 目录中。

  • org.springframework.core-5.3.18.jar
  • org.springframework.beans-5.3.18.jar
  • spring-context-5.3.18.jar
  • spring-expression-5.3.18.jar
  • commons.logging-1.2.jar
  • spring-aop-5.3.18.jar

13.6 基于 PointcutAdvisor 的 AOP 开发

PointCutAdvisor 是 Adivsor 接口的子接口,用来表示带切点的切面。使用它,我们可以通过包名、类名、方法名等信息更加灵活的定义切面中的切入点,提供更具有适用性的切面。

Spring 提供了多个 PointCutAdvisor 的实现,其中常用实现类如下:

  • NameMatchMethodPointcutAdvisor:指定 Advice 所要应用到的目标方法名称,例如 helloXxx 代表所有以 hello 开头的所有方法。
  • RegExpMethodPointcutAdvisor:使用正则表达式来定义切点(PointCut),RegExpMethodPointcutAdvisor 包含一个 pattern 属性,该属性使用正则表达式描述需要拦截的方法。
例 1

下面我们就通过一个简单的实例,演示下切点切面的 AOP 开发。

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

package section6.demo1.dao;

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

    void delete();

    void deleteBatch();

    void modify();

    void get();
}

创建 OrderDao 的实现类 OrderDaoImpl,代码如下:

package section6.demo1.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import section6.demo1.dao.OrderDao;

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

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

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

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

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

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

创建命名为 OrderDaoAroundAdvice 的环绕增强类,代码如下:

package section6.demo1.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

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

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        LOGGER.info("环绕增强前********");
        //执行被代理对象中的逻辑
        Object result = methodInvocation.proceed();
        LOGGER.info("环绕增强后********");
        return result;
    }
}

修改 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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--带切点的切面-->
    <!-- 定义目标(target)对象 -->
    <bean id="orderDao" class="section6.demo1.dao.impl.OrderDaoImpl"/>
    <!-- 定义增强 -->
    <bean id="aroundAdvice" class="section6.demo1.advice.OrderDaoAroundAdvice"/>
    <!--定义切面-->
    <bean id="myPointCutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--定义表达式,规定哪些方法进行拦截 .* 表示所有方法-->
        <!--<property name="pattern" value=".*"></property>-->
        <property name="patterns" value="section6.demo1.dao.impl.OrderDaoImpl.add.*, section6.demo1.dao.impl.OrderDaoImpl.del.*"/>
        <property name="advice" ref="aroundAdvice"/>
    </bean>
    <!--Spring 通过配置生成代理-->
    <bean id="orderDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 配置目标 -->
        <property name="target" ref="orderDao"/>
        <!-- 针对类的代理,该属性默认取值为 false(可省略), 表示使用 JDK 动态代理;取值为 true,表示使用 CGlib 动态代理-->
        <property name="proxyTargetClass" value="true"/>
        <!-- 在目标上应用增强 -->
        <property name="interceptorNames" value="myPointCutAdvisor"/>
    </bean>

</beans>

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

package section6.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 section6.demo1.dao.OrderDao;

/**
 * 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");
        // 获取名为 orderDaoProxy 的 Bean
        OrderDao orderDao = context.getBean("orderDaoProxy", OrderDao.class);
        LOGGER.info("调用 OrderDao 对象中的方法");
        orderDao.add();
        orderDao.delete();
        orderDao.deleteBatch();
        orderDao.get();
        orderDao.modify();
    }
}

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

4月 09, 2022 5:06:11 下午 section6.demo1.MainApp main
信息: 调用 OrderDao 对象中的方法
4月 09, 2022 5:06:11 下午 section6.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:06:11 下午 section6.demo1.dao.impl.OrderDaoImpl add
信息: 正在执行 OrderDao 的 add() 方法……
4月 09, 2022 5:06:11 下午 section6.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:06:11 下午 section6.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:06:11 下午 section6.demo1.dao.impl.OrderDaoImpl delete
信息: 正在执行 OrderDao 的 delete() 方法……
4月 09, 2022 5:06:11 下午 section6.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:06:11 下午 section6.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:06:11 下午 section6.demo1.dao.impl.OrderDaoImpl deleteBatch
信息: 正在执行 OrderDao 的 deleteBatch() 方法……
4月 09, 2022 5:06:11 下午 section6.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:06:11 下午 section6.demo1.dao.impl.OrderDaoImpl get
信息: 正在执行 OrderDao 的 get() 方法……
4月 09, 2022 5:06:11 下午 section6.demo1.dao.impl.OrderDaoImpl modify
信息: 正在执行 OrderDao 的 modify() 方法……

13.7 自动代理

在前面的案例中,所有目标对象(Target Bean)的代理对象(Proxy Bean)都是在 XML 配置中通过 ProxyFactoryBean 创建的。但在实际开发中,一个项目中往往包含非常多的 Bean, 如果每个 Bean 都通过 ProxyFactoryBean 创建,那么开发和维护成本会十分巨大。为了解决这个问题,Spring 为我们提供了自动代理机制。

Spring 提供的自动代理方案,都是基于后处理 Bean 实现的,即在 Bean 创建的过程中完成增强,并将目标对象替换为自动生成的代理对象。通过 Spring 的自动代理,我们在程序中直接拿到的 Bean 就已经是 Spring 自动生成的代理对象了。

Spring 为我们提供了 3 种自动代理方案:

  • BeanNameAutoProxyCreator:根据 Bean 名称创建代理对象。
  • DefaultAdvisorAutoProxyCreator:根据 Advisor 本身包含信息创建代理对象。
  • AnnotationAwareAspectJAutoProxyCreator:基于 Bean 中的 AspectJ 注解进行自动代理对象。

本节我们就通过两个简单的实例,对 BeanNameAutoProxyCreatorDefaultAdvisorAutoProxyCreator 进行演示,至于 AnnotationAwareAspectJAutoProxyCreator ,我们会在后续的教程中进行讲解。

1. 根据 Bean 名称创建代理对象

例 1

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

package section7.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 section7.demo1.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import section7.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() 方法……");
    }
}

创建命名为 UserDaoBeforeAdvice 的前置增强类,代码如下:

package section7.demo1.advice;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

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

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        LOGGER.info("正在执行前置增强操作…………");
    }
}

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

package section7.demo1.dao;

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

    void delete();

    void deleteBatch();

    void modify();

    void get();
}

创建 OrderDao 的实现类 OrderDaoImpl,代码如下:

package section7.demo1.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import section7.demo1.dao.OrderDao;

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

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

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

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

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

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

创建命名为 OrderDaoAroundAdvice 的环绕增强类,代码如下:

package section7.demo1.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

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

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        LOGGER.info("环绕增强前********");
        //执行被代理对象中的逻辑
        Object result = methodInvocation.proceed();
        LOGGER.info("环绕增强后********");
        return result;
    }
}

修改 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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义目标(target)对象 -->
    <bean id="userDao" class="section7.demo1.dao.impl.UserDaoImpl"/>
    <bean id="orderDao" class="section7.demo1.dao.impl.OrderDaoImpl"/>

    <!-- 定义增强 -->
    <bean id="beforeAdvice" class="section7.demo1.advice.UserDaoBeforeAdvice"/>
    <bean id="aroundAdvice" class="section7.demo1.advice.OrderDaoAroundAdvice"/>
    
    <!--Spring 自动代理:根据 Bean 名称创建代理对象-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*Dao"/>
        <property name="interceptorNames" value="beforeAdvice, aroundAdvice"/>
    </bean>

</beans>

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

package section7.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 section7.demo1.dao.OrderDao;
import section7.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");
        //获取代理对象
        UserDao userDao = context.getBean("userDao", UserDao.class);
        //获取代理对象
        OrderDao orderDao = context.getBean("orderDao", OrderDao.class);
        LOGGER.info("================调用 UserDao 对象中的方法================");
        userDao.add();
        userDao.delete();
        userDao.modify();
        userDao.get();
        LOGGER.info("================调用 OrderDao 对象中的方法================");
        orderDao.add();
        orderDao.delete();
        orderDao.deleteBatch();
        orderDao.get();
        orderDao.modify();
    }
}

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

4月 09, 2022 5:29:50 下午 section7.demo1.MainApp main
信息: ================调用 UserDao 对象中的方法================
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.UserDaoImpl add
信息: 正在执行 UserDao 的 add() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.UserDaoImpl delete
信息: 正在执行 UserDao 的 delete() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.UserDaoImpl modify
信息: 正在执行 UserDao 的 modify() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.UserDaoImpl get
信息: 正在执行 UserDao 的 get() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:29:50 下午 section7.demo1.MainApp main
信息: ================调用 OrderDao 对象中的方法================
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.OrderDaoImpl add
信息: 正在执行 OrderDao 的 add() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.OrderDaoImpl delete
信息: 正在执行 OrderDao 的 delete() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.OrderDaoImpl deleteBatch
信息: 正在执行 OrderDao 的 deleteBatch() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.OrderDaoImpl get
信息: 正在执行 OrderDao 的 get() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:29:50 下午 section7.demo1.advice.UserDaoBeforeAdvice before
信息: 正在执行前置增强操作…………
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:29:50 下午 section7.demo1.dao.impl.OrderDaoImpl modify
信息: 正在执行 OrderDao 的 modify() 方法……
4月 09, 2022 5:29:50 下午 section7.demo1.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********

2. 根据切面中信息创建代理对象

例 2

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

package section7.demo2.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 section7.demo2.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import section7.demo2.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() 方法……");
    }
}

创建命名为 UserDaoBeforeAdvice 的前置增强类,代码如下:

package section7.demo2.advice;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

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

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        LOGGER.info("正在执行前置增强操作…………");
    }
}

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

package section7.demo2.dao;

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

    void delete();

    void deleteBatch();

    void modify();

    void get();
}

创建 OrderDao 的实现类 OrderDaoImpl,代码如下:

package section7.demo2.dao.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import section7.demo1.dao.OrderDao;

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

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

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

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

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

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

创建命名为 OrderDaoAroundAdvice 的环绕增强类,代码如下:

package section7.demo2.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

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

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        LOGGER.info("环绕增强前********");
        //执行被代理对象中的逻辑
        Object result = methodInvocation.proceed();
        LOGGER.info("环绕增强后********");
        return result;
    }
}

修改 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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义目标(target)对象 -->
    <bean id="userDao" class="section7.demo2.dao.impl.UserDaoImpl"/>
    <bean id="orderDao" class="section7.demo2.dao.impl.OrderDaoImpl"/>

    <!-- 定义增强 -->
    <bean id="beforeAdvice" class="section7.demo2.advice.UserDaoBeforeAdvice"/>
    <bean id="aroundAdvice" class="section7.demo2.advice.OrderDaoAroundAdvice"/>

    <!--定义切面-->
    <bean id="myPointCutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--定义表达式,规定哪些方法进行拦截 .* 表示所有方法-->
        <!--<property name="pattern" value=".*"></property>-->
        <property name="patterns"
                  value="section7.demo2.dao.impl.OrderDaoImpl.del.*, section7.demo2.dao.impl.UserDaoImpl.add.*"/>
        <property name="advice" ref="aroundAdvice"/>
    </bean>
    <!--Spring 自动代理:根据切面 myPointCutAdvisor 中信息创建代理对象-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

</beans>

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

package section7.demo2;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import section7.demo2.dao.OrderDao;
import section7.demo2.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");
        //获取代理对象
        UserDao userDao = context.getBean("userDao", UserDao.class);
        //获取代理对象
        OrderDao orderDao = context.getBean("orderDao", OrderDao.class);
        LOGGER.info("================调用 UserDao 对象中的方法================");
        userDao.add();
        userDao.delete();
        userDao.modify();
        userDao.get();
        LOGGER.info("================调用 OrderDao 对象中的方法================");
        orderDao.add();
        orderDao.delete();
        orderDao.deleteBatch();
        orderDao.get();
        orderDao.modify();
    }
}

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

4月 09, 2022 5:30:42 下午 section7.demo2.MainApp main
信息: ================调用 UserDao 对象中的方法================
4月 09, 2022 5:30:42 下午 section7.demo2.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.UserDaoImpl add
信息: 正在执行 UserDao 的 add() 方法……
4月 09, 2022 5:30:42 下午 section7.demo2.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.UserDaoImpl delete
信息: 正在执行 UserDao 的 delete() 方法……
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.UserDaoImpl modify
信息: 正在执行 UserDao 的 modify() 方法……
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.UserDaoImpl get
信息: 正在执行 UserDao 的 get() 方法……
4月 09, 2022 5:30:42 下午 section7.demo2.MainApp main
信息: ================调用 OrderDao 对象中的方法================
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.OrderDaoImpl add
信息: 正在执行 OrderDao 的 add() 方法……
4月 09, 2022 5:30:42 下午 section7.demo2.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.OrderDaoImpl delete
信息: 正在执行 OrderDao 的 delete() 方法……
4月 09, 2022 5:30:42 下午 section7.demo2.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:30:42 下午 section7.demo2.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强前********
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.OrderDaoImpl deleteBatch
信息: 正在执行 OrderDao 的 deleteBatch() 方法……
4月 09, 2022 5:30:42 下午 section7.demo2.advice.OrderDaoAroundAdvice invoke
信息: 环绕增强后********
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.OrderDaoImpl get
信息: 正在执行 OrderDao 的 get() 方法……
4月 09, 2022 5:30:42 下午 section7.demo2.dao.impl.OrderDaoImpl modify
信息: 正在执行 OrderDao 的 modify() 方法……
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

传陆编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值