Spring AOP使用接口方式实现

网友投稿 594 2022-12-15

Spring AOP使用接口方式实现

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识别. 常见的自己定义通知有:前置通知, 后置通知, 返回通知, 异常通知, 环绕通知. 对应实现的接口是:

前置通知: MethodBeforeAdvice

后置通知: AfterAdvice

返回通知:AfterReturningAdvice

异常通知:ThrowsAdvice

环绕通知:MethodInterceptor

实现步骤如下:

1. 业务接口实现

package com.lxl.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.aop.interfaceAop;//业务类,也是目标对象

import com.lxl.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.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.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("==========进入afterReturYCVJfning()=========== \n");

System.out.println("切入点方法执行完成");

System.out.println("后置通知--目标对象:" + target);

System.out.println("后置通知--方法名: "+method);

System.out.println("后置通知--方法入参: "+ args.toString());

System.out.println("后置通知--方法返回值: "+ returnValue);

}

}

异常通知

package com.lxl.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.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);

Systehttp://m.out.println("===========around环绕通知方法 结束===========");

return returnValue;

}

}

4. 自定义切## 点

package com.lxl.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 targetYCVJfClass) {

// 设置单个方法匹配

this.setMappedName("add");

// 设置多个方法匹配

String[] methods = { "add", "div" };

//也可以用“ * ” 来做匹配符号

// this.setMappedName("get*");

this.setMappedNames(methods);

return super.matches(method, targetClass);

}

}

5.配置xml文件

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:p="http://springframework.org/schema/p"

xmlns:context="http://springframework.org/schema/context"

xmlns:aop="http://springframework.org/schema/aop"

xsi:schemaLocation="

http://springframework.org/schema/beans

http://springframework.org/schema/beans/spring-beans-3.0.xsd

http://springframework.org/schema/context

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

http://springframework.org/schema/aop

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

>

com.lxl.aop.interfaceAop.IBaseCalculate

matchBeforeAdvisor

baseAfterReturn

baseAround

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:p="http://springframework.org/schema/p"

xmlns:context="http://springframework.org/schema/context"

xmlns:aop="http://springframework.org/schema/aop"

xsi:schemaLocation="

http://springframework.org/schema/beans

http://springframework.org/schema/beans/spring-beans-3.0.xsd

http://springframework.org/schema/context

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

http://springframework.org/schema/aop

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

>

com.lxl.aop.interfaceAop.IBaseCalculate

matchBeforeAdvisor

baseAfterReturn

baseAround

6. 方法入口

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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot feign动态设置数据源(https请求)
下一篇:解决mybatis resultMap根据type找不到对应的包问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~