首页 > 其他分享 >Spring AOP使用接口方式实现

Spring AOP使用接口方式实现

时间:2023-02-06 18:12:11浏览次数:39  
标签:int Spring AOP System 通知 println import 接口方式 out

目录
  • 一. 环境准备
  • 二、spring接口方式实现aop步骤
    • 1. 业务接口实现
    • 2. 业务类
    • 3. 通知类
    • 4. 自定义切## 点
    • 5.配置xml文件
    • 6. 方法入口
  • 三. 分析

Spring 提供了很多的实现AOP的方式:Spring 接口方式,schema配置方式和注解.

本文重点介绍Spring使用接口方式实现AOP. 研究使用接口方式实现AOP, 以了解为目的. 更好地理解spring使用动态代理实现AOP. 通常我们使用的更多的是使用注解的方式实现AOP

下面来看看如何实现接口方式的AOP

 

一. 环境准备

要在项目中使用Spring AOP 则需要在项目中导入除了spring jar包之外, 还需要引入aspectjrt.jar,aspectjweaver.jar,aopalliance.jar ,spring-aop-3.2.0.M2.jar和cglib.jar

 

二、Spring接口方式实现AOP步骤

使用Spring aop接口方式实现aop, 可以通过自定义通知来供Spring AOP识别. 常见的自己定义通知有:前置通知, 后置通知, 返回通知, 异常通知, 环绕通知. 对应实现的接口是:

  1. 前置通知: MethodBeforeAdvice
  2. 后置通知: AfterAdvice
  3. 返回通知:AfterReturningAdvice
  4. 异常通知:ThrowsAdvice
  5. 环绕通知:MethodInterceptor

实现步骤如下:

 

1. 业务接口实现


package com.lxl.www.aop.interfaceAop;


public interface IBaseCalculate {

    int add(int numA, int numB);

    int sub(int numA, int numB);

    int div(int numA, int numB);

    int multi(int numA, int numB);

    int mod(int numA, int numB);

}

 

2. 业务类


package com.lxl.www.aop.interfaceAop;//业务类,也是目标对象

import com.lxl.www.aop.Calculate;

import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;


@Service
public class BaseCalculate implements IBaseCalculate {

    @Override
    public int add(int numA, int numB) {
        System.out.println("执行目标方法: add");
        return numA + numB;
    }

    @Override
    public int sub(int numA, int numB) {
        System.out.println("执行目标方法: sub");
        return numA - numB;
    }

    @Override
    public int multi(int numA, int numB) {
        System.out.println("执行目标方法: multi");
        return numA * numB;
    }

    @Override
    public int div(int numA, int numB) {
        System.out.println("执行目标方法: div");
        return numA / numB;
    }

    @Override
    public int mod(int numA, int numB) {
        System.out.println("执行目标方法: mod");

        int retVal = ((Calculate) AopContext.currentProxy()).add(numA, numB);
        return retVal % numA;
    }
}

 

3. 通知类

前置通知


package com.lxl.www.aop.interfaceAop;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Component
public class BaseBeforeAdvice implements MethodBeforeAdvice {

    
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("===========进入beforeAdvice()============");

        System.out.println("目标对象:" + target);
        System.out.println("方法名: "+method);

        System.out.println("即将进入切入点方法");
    }
}

后置通知


package com.lxl.www.aop.interfaceAop;

import org.aspectj.lang.annotation.AfterReturning;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

public class BaseAfterReturnAdvice implements AfterReturningAdvice {

    
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("==========进入afterReturning()=========== \n");
        System.out.println("切入点方法执行完成");

        System.out.println("后置通知--目标对象:" + target);
        System.out.println("后置通知--方法名: "+method);
        System.out.println("后置通知--方法入参: "+ args.toString());
        System.out.println("后置通知--方法返回值: "+ returnValue);
    }
}

异常通知


package com.lxl.www.aop.interfaceAop;

import org.springframework.aop.ThrowsAdvice;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
@Component
public class BaseAfterThrowsAdvice implements ThrowsAdvice {

    
    public void afterThrowing(Method method, Object[] args, Object target, Throwable throwable) {
        System.out.println("出错啦");
    }
}

环绕通知


package com.lxl.www.aop.interfaceAop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Component
public class BaseAroundAdvice implements MethodInterceptor {

    
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("===========around环绕通知方法 开始===========");

        // 调用目标方法之前执行的动作
        System.out.println("环绕通知--调用方法之前: 执行");

        // 调用方法的参数
        Object[] args = invocation.getArguments();
        // 调用的方法
        Method method = invocation.getMethod();
        // 获取目标对象
        Object target = invocation.getThis();
        System.out.println("输入参数:" + args[0] + ";" + method + ";" + target);

        // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
        Object returnValue = invocation.proceed();

        System.out.println("环绕通知--调用方法之后: 执行");
      
        System.out.println("输出参数:" + args[0] + ";" + method + ";" + target + ";" + returnValue);

        System.out.println("===========around环绕通知方法  结束===========");

        return returnValue;
    }
}

 

4. 自定义切## 点


package com.lxl.www.aop.interfaceAop;


import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class Pointcut extends NameMatchMethodPointcut {

    private static final long serialVersionUID = 3990456017285944475L;

    @SuppressWarnings("rawtypes")
    @Override
    public boolean matches(Method method, Class targetClass) {
        // 设置单个方法匹配
        this.setMappedName("add");
        // 设置多个方法匹配
        String[] methods = { "add", "div" };
      
        //也可以用“ * ” 来做匹配符号
        // this.setMappedName("get*");
      
        this.setMappedNames(methods);

        return super.matches(method, targetClass);
    }
}

 

5.配置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:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context-3.0.xsd

         http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
>

    <!-- ==============================aop配置================================ -->
    <!-- 声明一个业务类 -->
    <bean id="baseCalculate" class="com.lxl.www.aop.interfaceAop.BaseCalculate"/>

    <!-- 声明通知类 -->
    <bean id="baseBefore" class="com.lxl.www.aop.interfaceAop.BaseBeforeAdvice"/>
    <bean id="baseAfterReturn" class="com.lxl.www.aop.interfaceAop.BaseAfterReturnAdvice"/>
    <bean id="baseAfterThrows" class="com.lxl.www.aop.interfaceAop.BaseAfterThrowsAdvice"/>
    <bean id="baseAround" class="com.lxl.www.aop.interfaceAop.BaseAroundAdvice"/>

    <!-- 指定切点匹配类 -->
    <bean id="pointcut" class="com.lxl.www.aop.interfaceAop.Pointcut"/>

    <!-- 包装通知,指定切点 -->
    <bean id="matchBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="pointcut">
            <ref bean="pointcut"/>
        </property>
        <property name="advice">
            <ref bean="baseBefore"/>
        </property>
    </bean>

    <!-- 使用ProxyFactoryBean 产生代理对象 -->
    <bean id="businessProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 代理对象所实现的接口 ,如果有接口可以这样设置 -->
        <property name="proxyInterfaces">
            <value>com.lxl.www.aop.interfaceAop.IBaseCalculate</value>
        </property>

        <!-- 设置目标对象 -->
        <property name="target">
            <ref bean="baseCalculate"/>
        </property>
        <!-- 代理对象所使用的拦截器 -->
        <property name="interceptorNames">
            <list>
                <value>matchBeforeAdvisor</value>
                <value>baseAfterReturn</value>
                <value>baseAround</value>
            </list>
        </property>
    </bean>
</beans>

 

6. 方法入口


package com.lxl.www.aop.interfaceAop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class InterfaceMainClass{

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("aop/aop.xml");
        BaseCalculate calculate = (BaseCalculate) context.getBean("baseCalculate");
        calculate.add(1, 3);
    }
}

 

三. 分析

各种类型通知的执行顺序: 前置方法会在切入点方法之前执行,后置会在切入点方法执行之后执行,环绕则会在切入点方法执行前执行同事方法结束也会执行对应的部分。主要是调用proceed()方法来执行切入点方法。来作为环绕通知前后方法的分水岭 在xml 配置 businessProxy这个bean的时候,ProxyFactoryBean类中指定了,proxyInterfaces参数。这里把他配置了IBaseCalculate接口。因为在项目开发过程中,往往业务类都会有对应的接口,以方便利用ioc解耦。但Spring AOP却也能支持没有接口的代理。这就是为什么需要导入cglib.jar包了。看过spring的源码,知道在目标切入对象如果有实现接口,spring会默认使用jdk动态代理来实现代理类。如果没有接口,则会通过cglib来实现代理类。

这个业务类现在有 前置通知,后置通知,环绕三个通知同时作用,可能以及更多的通知进行作用。那么这些通知的执行顺序是怎么样的?就这个例子而言,同时实现了三个通知。在例 子xml中,则显示执行before通知,然后执行around的前处理,执行切点方法,再执行return处理。最后执行around的后处理。经过测 试,知道spring 处理顺序是按照xml配置顺序依次处理通知,以队列的方式存放前通知,以压栈的方式存放后通知。所以是前通知依次执行,后通知到切入点执行完之后,从栈里 在后进先出的形式把后通知执行。

在实现过程中发现通知执行对应目标对象的整个类中的方法,如何精确到某个方法,则需要定义一个切点匹配的方式:spring提供了方法名匹配或正则方式来匹配。然后通过DefaultPointcutAdvisor来包装通知,指定切点。

使用接口方式配置起来,可见代码还是非常的厚重的,定义一个切面就要定义一个切面类,然而切面类中,就一个通知方法,着实没有必要。所以Spring提供了,依赖aspectj的schema配置和基于aspectj 注解方式。这两种方式非常简单方便使用,也是项目中普遍的使用方式。

标签:int,Spring,AOP,System,通知,println,import,接口方式,out
From: https://www.cnblogs.com/dituirenwu/p/17096327.html

相关文章

  • 若依前后端分离版怎样修改主页面显示请求的SpringBoot后台数据
    场景使用若依的前后端分离版,本来的首页效果是 现在如果要根据具体业务实现从后台获取要显示的数据实现类似下面的效果 注:霸道的程序猿获取编程相关电子书、教程推送与免......
  • Spring16 - 回顾 Java反射 reflect
    回顾Java反射Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对......
  • gradle构建springboot多模块项目配置
    父模块配置>build.gradlebuildscript{//统一版本管理ext{springBootVersion='2.3.12.RELEASE'springCloudVersion='Hoxton.SR12'......
  • Spring15 - 全注解开发
    Spring全注解开发全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件。packagecom.atguigu.spring6.config;importorg.springframework.context.ann......
  • Spring14 - @Resource注解实现注入
    @Resource注入@Resource注解也可以完成属性注入。那它和@Autowired注解有什么区别?@Resource注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该注解是标准注解,更加具......
  • spring boot 2.6.2解决log4j漏洞
    公司版本2.3,因为那个log4j漏洞准备升级2.6.2测试下,记录下出现的问题高版本不允许循环依赖,如果写的时候不太注意,改的时候也要改很多地方,最后决定添加个配置解决在bootstra......
  • SpringBoot数据分页工具类
    SpringBoot数据分页工具类/***数据分页工具*/publicclassPageUtil{publicstaticPagepageHelp(@NotNullIntegerpageNum,@NotNullIntegerpageSize,@No......
  • Spring13 - 自动注入@Autowired
    @Autowired注入单独使用@Autowired注解,默认根据类型装配。【默认是byType】查看源码:packageorg.springframework.beans.factory.annotation;importjava.lang.annota......
  • Spring init-method与destroy-method属性的用法解析
    目录springinit-method与destroy-method属性使用知识点介绍:操作步骤:init-method="init"和destroy-method="close"作用 Springinit-method与destroy-method......
  • Spring与Web环境集成
    ApplicationContext应用上下文获取方式初步设想代码示例:<!--pom.xml配置文件--><dependencies> <dependency> <groupId>org.springframework</groupId> <artifactI......