Spring AOP使用接口方式实现

目录
  • 一. 环境准备
  • 二、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;

/**
 * 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
 *
 * 业务接口类-- 计算器接口类
 *
 * 定义三个业务逻辑方法
 */
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 {

    /**
     *
     * @param method 切入的方法
     * @param args 切入方法的参数
     * @param target 目标对象
     * @throws Throwable
     */
    @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 {

    /**
     *
     * @param returnValue 切入点执行完方法的返回值,但不能修改
     * @param method 切入点方法
     * @param args 切入点方法的参数数组
     * @param target 目标对象
     * @throws Throwable
     */
    @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 {

    /**
     * @param method    可选:切入的方法
     * @param args      可选:切入的方法的参数
     * @param target    可选:目标对象
     * @param throwable 必填 : 异常子类,出现这个异常类的子类,则会进入这个通知。
     */
    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 {

    /**
     * invocation :连接点
     */
    @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;

/**
 * 切点
 *
 * 继承NameMatchMethodPointcut类,来用方法名匹配
 */
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 注解方式。这两种方式非常简单方便使用,也是项目中普遍的使用方式。

到此这篇关于Spring AOP使用接口方式实现的文章就介绍到这了,更多相关Spring AOP 接口实现内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot中使用AOP打印接口日志的方法

    前言 AOP 是 Aspect Oriented Program (面向切面)的编程的缩写.他是和面向对象编程相对的一个概念.在面向对象的编程中,我们倾向于采用封装.继承.多态等概念,将一个个的功能在对象中来实现.但是,我们在实际情况中也发现,会有另外一种需求就是一类功能在很多对象的很多方法中都有需要.例如有一些对数据库访问的方法有事务管理的需求,有很多方法中要求打印日志.按照面向对象的方式,那么这些相同的功能要在很多地方来实现或者在很多地方来调用.这就非常繁琐并且和这些和业务不相关的需求耦合太

  • Spring Boot2集成AOPLog来记录接口访问日志

    前言 日志是一个Web项目中必不可少的部分,借助它我们可以做许多事情,比如问题排查.访问统计.监控告警等.一般通过引入slf4j的一些实现框架来做日志功能,如log4j,logback,log4j2,其性能也是依次增强.在springboot中,默认使用的框架是logback. 我们经常需要在方法开头或结尾加日志记录传入参数或返回结果,以此来复现当时的请求情况.但是手动添加日志,不仅繁琐重复,也影响代码的美观简洁.本文引入一个基于AOP实现的日志框架,并通过spring-boot-starter

  • Spring Boot使用AOP实现REST接口简易灵活的安全认证的方法

    本文将通过AOP的方式实现一个相对更加简易灵活的API安全认证服务. 我们先看实现,然后介绍和分析AOP基本原理和常用术语. 一.Authorized实现 1.定义注解 package com.power.demo.common; import java.lang.annotation.*; /* * 安全认证 * */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Doc

  • Springboot接口项目如何使用AOP记录日志

    一. 背景 一直想给项目构建一个统一的日志收集系统,先迈出第一步,构建一个日志收集类,用AOP实现无侵入日志收集 二. 环境 1.此随笔内容基于spring boot项目 2.数据库为mysql 5.7.9版本 3.jdk 版本为1.8 三. 说明 此版采用数据库存储,之后考虑使用elasticsearch等工具存储 四. 内容 1.构建日志采集实体类:BaseLogMessage public class BaseLogMessage { private String serverIP; pr

  • Spring AOP使用接口方式实现

    目录 一. 环境准备 二.Spring接口方式实现AOP步骤 1. 业务接口实现 2. 业务类 3. 通知类 4. 自定义切## 点 5.配置xml文件 6. 方法入口 三. 分析 Spring 提供了很多的实现AOP的方式:Spring 接口方式,schema配置方式和注解. 本文重点介绍Spring使用接口方式实现AOP. 研究使用接口方式实现AOP, 以了解为目的. 更好地理解spring使用动态代理实现AOP. 通常我们使用的更多的是使用注解的方式实现AOP 下面来看看如何实现接口方式的

  • spring AOP自定义注解方式实现日志管理的实例讲解

    今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在applicationContext-mvc.xml中要添加的 <mvc:annotation-driven /> <!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 --> <context:component-scan base-package=&qu

  • Spring AOP注解失效的坑及JDK动态代理

    @Transactional @Async等注解不起作用 之前很多人在使用Spring中的@Transactional, @Async等注解时,都多少碰到过注解不起作用的情况. 为什么会出现这些情况呢?因为这些注解的功能实际上都是Spring AOP实现的,而其实现原理是通过代理实现的. JDK动态代理 以一个简单的例子理解一下JDK动态代理的基本原理: //目标类接口 public interface JDKProxyTestService { void run(); } //目标类 publ

  • Spring AOP日志框架实现过程图解

    AOP日志框架实现 JDK动态代理实现日志框架 首先,在项目包com.ay.test 下创建业务接口类BusinessClassService,具体代码如下: BusinessC lassService 业务接口类可以理解为日常开发业务创建的接口类, 接口中有一个简 单的方法doSomeThing .然后,开发业务类的实现类BusinessClassServiceImpl,具体代码如下: 实现类BusinessClassServicelmpl 实现了BusinessClassServ ice 接

  • Aspectj与Spring AOP的对比分析

    1.简介 今天有多个可用的 AOP 库, 它们需要能够回答许多问题: 1.是否与用户现有的或新的应用程序兼容? 2.在哪里可以实现 AOP? 3.与自己的应用程序集成多快? 4.性能开销是多少? 在本文中, 我们将研究如何回答这些问题, 并介绍 Spring aop 和 AspectJ, 这是 Java 的两个最受欢迎的 aop 框架. 2.AOP概念 在开始之前, 让我们对术语和核心概念进行快速.高层次的审查: Aspect -- 一种标准代码/功能, 分散在应用程序中的多个位置, 通常与实际

  • 解决Spring AOP拦截抽象类(父类)中方法失效问题

    目录 背景 原因分析 解决方案 后记 背景 最近工作中需要对组内各个系统依赖的第三方接口进行监控报警,对于下游出现问题的接口能够及时感知.首先我们写了一个Spring AOP注解,用于收集调用第三方时返回的信息.而我们调用第三方的类抽象出一个父类.并在父类的方法中加入我们的自定义注解用于监控日志并打印日志. 很多子类继承了这个父类并使用父类中的方法.如: 当调用子类的doSomething方法时问题出现了,发现Spring AOP没有拦截doPost()方法.而将注解加在子类方法上时,Sprin

  • Spring Aop之AspectJ注解配置实现日志管理的方法

    最近项目要做一个日志功能,我用Spring Aop的注解方式来实现. 创建日志注解 package com.wyj.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lan

  • 详解Spring学习总结——Spring实现AOP的多种方式

    目录 一.基于XML配置的Spring AOP 二.使用注解配置AOP 三.AspectJ切点函数 四.AspectJ通知注解 五.零配置实现Spring IoC与AOP AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的横向多模块统一控制的一种技术.AOP是OOP的补充,是spring框架中的一个重要内容.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发

  • spring aop两种配置方式

    第一种:注解配置AOP 注解配置AOP(使用 AspectJ 类库实现的),大致分为三步: 1. 使用注解@Aspect来定义一个切面,在切面中定义切入点(@Pointcut),通知类型(@Before, @AfterReturning,@After,@AfterThrowing,@Around). 2. 开发需要被拦截的类. 3. 将切面配置到xml中,当然,我们也可以使用自动扫描Bean的方式.这样的话,那就交由Spring AoP容器管理. 另外需要引用 aspectJ 的 jar 包:

  • Spring AOP拦截-三种方式实现自动代理详解

    这里的自动代理,我讲的是自动代理bean对象,其实就是在xml中让我们不用配置代理工厂,也就是不用配置class为org.springframework.aop.framework.ProxyFactoryBean的bean. 总结了一下自己目前所学的知识. 发现有三种方式实现自动代理 用Spring一个自动代理类DefaultAdvisorAutoProxyCreator: <bean class="org.springframework.aop.framework.autoproxy.

随机推荐