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;
    
    
    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 注解方式。这两种方式非常简单方便使用,也是项目中普遍的使用方式。

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

    --结束END--

    本文标题: Spring AOP使用接口方式实现

    本文链接: https://www.lsjlt.com/news/83.html (转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    猜你喜欢