Java – SpringAOP – 基于XML的AOP

简介

Spring 支持通过XML配置AOP切面编程,可以事先创建切面类,和实体类,并通过SpringXML进行配置。

详细的通过注解配置AOP基础可查看下面文章了解。

简介 AOP 面向切面编程,通俗的讲,就是把一个实体类方法中的一些非业务代码抽取出来,并封装到一个类中……
2023-01-05

 

实现AOP(使用JDK动态代理)

添加依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.24</version>
        </dependency>

 

创建实体类接口

Spring一旦使用JDK动态代理的模式,在底层会自动把实体类从ioc中排除(即即使用在实体类中定义了 @Component  也不能通过 getBean 获得)

原因是:代理的目的就是在实体类的外面再封装一层类,ioc直接控制外层类。如果使用了代码,而ioc依然可以通过getBean 获取到实体类,那么用于代理的外层包装类就没有意义了,所以Spring在这方面进行了限制。如下图为代理模式下的实体类与外层类的关系。

 

 

// 创建一个运算的接口
public interface Calculator {

    // 定义一个加法
    int add(int x, int y);

    // 定义一个减法
    int min(int x, int y);

    // 定义一个乘法
    int sub(int x, int y);

    // 定义一个除法
    int div(int x, int y);

}

 

创建实现类

实现类为实现接口方法的类,也是实体类

// 使用注解定义实体类为 ioc Bean 类,归由ioc管理
@Component
public class CalculatorImpl implements Calculator {

    @Override
    public int add(int x, int y) {
        int result = x + y;
        System.out.println("内部方法add");
        return result;
    }

    @Override
    public int min(int x, int y) {
        int result = x - y;
        System.out.println("内部方法min");
        return result;
    }

    @Override
    public int sub(int x, int y) {
        int result = x * y;
        System.out.println("内部方法sub");
        return result;
    }

    @Override
    public int div(int x, int y) {
        int result = x / y;
        System.out.println("内部方法div");
        return result;
    }
}

JDK代理模式只能通过接口来获取实体类,不能直接 new  实体类(上面说过,开启了代理就不能直接控制实体类,且Spring也不允许)。

 

创建切面类

切面类,就是用于存放那些在实体类中抽取出来的非核心业务代码的类,它会随着实体类的执行,按照通知的周期来跟随执行。

@Component
public class LoggerAspect {

    /**
     * 定义一个前置通知方法
     * 使用 xml 定义前置通知方法不需要加注解,在xml中声明定义即可
     */

    public void BeforeMethod(JoinPoint joinPoint) {
        System.out.println("前置通知方法");
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        System.out.println("方法名是" + signature.getName());
        System.out.println("参数值为" + Arrays.toString(args));
    }

    /**
     * 要使用复用切入点表达式,只要传入注解该表达式的方法名即可
     */

    public void AfterMethod() {
    }
    
    public void After(){}


    public void AfterReturningMethod(JoinPoint joinPoint, Object result) {
        System.out.println(result);
    }


    public void AfterThrowingMethod(JoinPoint joinPoint, Exception e) {
        System.out.println("异常抛出:" + e);
    }


    public Object AroundMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            System.out.println("此处相当于@Before注解中的执行通知方法");
            /**
             * ProceedingJoinPoint 是一个带执行的切入点对象,它可以控制实体类方法何时执行
             * joinPoint.proceed() 则是执行实体类方法后得出的结果
             * joinPoint.proceed() 可以看作是实体类方法全部代码的调用方法,并把执行结果返回
             */
            result = joinPoint.proceed();

            System.out.println("此处相当于@AfterReturning注解中的执行通知方法");

        } catch (Throwable e) {

            System.out.println("此处相当于@AfterThrowing注解中的执行通知方法");

        }finally {
            System.out.println("此处相当于@After注解中的执行通知方法");
        }
        return result;
    }
}

注意:JoinPoint 接收的是来自实体类方法中的各种信息,包括方法名、接收参数等信息。

 

通知方法的接收参数

切面通知方法接收参数有三个,分别是【ProceedingJoinPoint】、【JoinPoint】、【Throwable】

1.【ProceedingJoinPoint】是用于使用 Around 环绕切面通知,Around 环绕切面通知相当于是【before】,【after-returning】,【after-throwing】,【after】四个切面方法。因此要在 Around 通知方法中判断什么时候调用什么切面方法,需要使用 ProceedingJoinPoint

ProceedingJoinPoint => proceed() 方法控制被代理的方法执行。

使用 try-catch-finally 定义【after-returning】、【after-throwing】、【after】方法情况

当 try 成功完成后,则是【after-returning】切面执行的方法

当 try 发生异常时,catch 则是【after-throwing】切面执行的方法

当 finally 执行时,则是【after】切面执行的方法。

 

2.【JoinPoint】仅提供被代理的方法的详细信息,如取得被代理的方法的参数、方法名、所在包名、切面表达式等。

 

3.【Throwable】是当被代理的方法发生异常时,所接收的异常信息。

注意:切面方法中如果要接收【Throwable】参数时,需要在xml中配置接收异常的参数名,如

<aop:after-throwing method="before" pointcut-ref="method1" throwing="e"></aop:after-throwing>

 

 

SpringXML切面类配置

<!--    扫描包中的注解ioc管理实体类-->
    <context:component-scan base-package="cn.unsoft.spring.aop.xml"></context:component-scan>
    
<!--    定义AOP切面-->
    <aop:config>
        <aop:pointcut id="pointCut" expression="execution(* cn.unsoft.spring.aop.xml.CalculatorImpl.*(..))"></aop:pointcut>
        
<!--        定义一个切面类 ref 则为这个类的ioc ID,因为使用了注解+扫描,所以ioc自动创建bean了-->
<!--        @Aspect-->
<!--        @Order(99)-->
        <aop:aspect ref="loggerAspect" order="99">
<!--            定义 @before("pointCut") 通知-->
            <aop:before method="AfterMethod" pointcut-ref="pointCut"></aop:before>
<!--            定义 @AfterReturning(value = "pointCut", returning = "result") 通知-->
            <aop:after-returning method="AfterReturningMethod" returning="result" pointcut-ref="pointCut"></aop:after-returning>
<!--            定义 @AfterThrowing(value = "pointCut", throwing= "e") 通知-->
            <aop:after-throwing method="AfterThrowingMethod" throwing="e" pointcut-ref="pointCut"></aop:after-throwing>
<!--            定义 @After("pointCut") 通知-->
            <aop:after method="After" pointcut-ref="pointCut"></aop:after>
<!--            定义 @Around("pointCut") 通知-->
            <aop:around method="AroundMethod" pointcut-ref="pointCut"></aop:around>
        </aop:aspect>
    </aop:config>

 

Advisor 接口实现AOP

Spring 提供一种接口实现的方式配置AOP通知方法的方式。

如果用户想自定义AOP通知,可以使用Aspect 方式,来指定哪个方法哪个时机被代理,执的通知方法是哪个类中的切面方法。

Aspect 中,如果想配置切面方法,需要在xml中(或注解)中定义,如上一节【SpringXML切面类配置】

Advisor 中,切面类,可以通过实现Advisor的子接中,实现对应的切面方法,也可以实现。

在 Advisor -> Advice 的子接口中,有多个AOP切面接口,通过实现接口方法

    // 把实现advisor接口的类放到Bean容器中管理
    <bean id="advisor" class="cn.unsoft.advice.MyAdvisor"></bean>

    <!-- aop切面配置 -->
    <aop:config>
        <!-- 定义需要被切面增强的类 -->
        <aop:pointcut id="pointcut1" expression="execution(* cn.unsoft.service.impl.*(..))"/>
        <aop:advisor advice-ref="advisor" pointcut-ref="pointcut1"></aop:advisor>
    </aop:config>

 

 

通过IoC取得实体类

IoC不能直获取实体类,因为实体类已经被代理封装,因此只能通过它实现的接口进行获取。

        // 获取ioc
        ApplicationContext ioc = new ClassPathXmlApplicationContext("aop-xml.xml");
        Calculator calculator = ioc.getBean(Calculator.class);
        calculator.add(1,1);

==>> 输出结果
前置通知方法
内部方法add

 

execution 表达式

execution 基础表达式格式如下:

execution([访问修饰符] 返回值类型 包名.类名.方法名 (参数))

其中,

1.访问修饰符可以省略不写;

2.返回值类型、某一级包名、类名、方法名 可以使用*表示任意;

3.包名与类名之间使用单点.表示该包下的类,使用双点.表示该包及其子包下的类;

4.参数列表可以使用两个点.表示任意参数。

//表示访问修饰符为public、无返回值、在cn.unsoft.aop包下的TargetImp1类的无参方法show
execution(public void cn.unsoft.aop.TargetImpl.show())
//表述cn.unsoft.aop包下的TargetImpl类的任意方法
execution(* cn.unsoft.aop.TargetImpl.*(..))
//表示cn.unsoft.aop包下的任意类的任意方法
execution(* cn.unsoft.aop.*.*(..))
//表示cn.unsoft.aop包及其子包下的任意类的任意方法
execution(* cn.unsoft.aop..*.*(..))
//表示任意包中的任意类的任意方法
execution(* *..*.*(..))

 

Aspect 的通知类型

通知名称 配置方式 执行时机
前置通知 <aop:before > 目标方法执行之前执行
后置通知 <aop:after-returning > 目标方法执行之后执行,目标方法异常时,不在执行
环绕通知 <aop:around > 目标方法执行前后执行,目标方法异常时,环绕后方法不在执行
异常通知 <aop:after-throwing > 目标方法抛出异常时执行
最终通知 <aop:after > 不管目标方法是否有异常,最终都会执行

 

简单实例

可下载查看对SpringAop的简单实例项目;

https://www.tzming.com/wp-content/uploads/2023/filesdown/SpringAopDemo.rar

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Java – SpringAOP – 基于XML的AOP
简介 Spring 支持通过XML配置AOP切面编程,可以事先创建切面类,和实体类,并通过SpringXML进行配置。 详细的通过注解配置AOP基础可查看下面文章了解。 ……
<<上一篇
下一篇>>