Spring AOP 通知

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010285684/article/details/80274670

  • AOP(Aspect-Oriented programming,面向切面编程)是对传统OOP(Object-Oriented programming)的补充
  • AOP的主要编程对象是切面(aspect)
  • 切点:切面模板化横切关注点。也就是我们在切面上面有选择的处理部分关注点。如我们一个类里面有15个方法,也主是对应了15个连接点,我们只选择其中的5个连接点来处理通知,事务等,那么选择的这5个连接点组全就是一个切点。
  • 在应用AOP的时候,仍然要定义公共功能,但可以明确定义这个功能在什么地方,以什么方式应用,并且不影响通知的类。这样一来,切点就被模块化切面(类)里面了。
  • AOP的好处:每个业务逻辑位于一个位置,代码不分散,便于维护和升级;业务模块只关注业务,只处理核心代码。
  • AOP的使用:
  1. 在spring的配置文件中加入aop的命名空间:
  2. 基于注解的方式来使用 AOP,在配置文件中配置自动扫描的包 :<context:component-scan base-package="com.dadi.aop"></context:component-scan>
  3. 加入AspectJ注解起作用的配置:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  4. 编写切面类:一个一般的 Java 类,在其中添加要额外实现的功能.
  5. 配置切面:  ①切面必须是 IOC 中的 bean: 实际添加了 @Component 注解; ②声明是一个切面: 添加 @Aspect; ③声明通知(@Before,@After,@Around,@AfterRunnig,@AfterThrowing): 即额外加入功能对应的方法. ;
  6. 在通知中访问连接细节: 可以在通知方法中添加 JoinPoint 类型的参数, 从中可以访问到方法的签名和方法的参数. 
  • 关于五个是@Before: 前置通知, 在方法执行之前执行; @After: 后置通知, 在方法执行之后执行 ; @AfterRunning: 返回通知, 在方法返回结果之后执行; @AfterThrowing: 异常通知, 在方法抛出异常之后;@Around: 环绕通知, 围绕着方法执行
  • 通知中的表达式:execution(访问类型 返回值 包路径.类路径.方法名.(参数类型,参数类型)),例如:①execution(public int com.dadi.aop.CalculationService.div(int, int));②可以用通配符,如返回任意类型,任意类,任意方法,任意参数的切面表达式execution(* com.dadi.aop.*.*(..))

下面是代码的示例

一、配置spring的配置文件

<?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: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.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 自动扫描指定包下面的带注解的类 -->
    <context:component-scan base-package="com.dadi.aop"></context:component-scan>
    <!-- 配置切面注解起作用 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


</beans>

二、编写java类

①定义接口

package com.dadi.aop;

/**
 * 计算接口
 */
public interface CalculationService {
	/**
	 * 加
	 *
	 * @param i
	 * @param j
	 * @return
	 */
	int add(int i, int j);

	/**
	 * 减
	 *
	 * @param i
	 * @param j
	 * @return
	 */
	int sub(int i, int j);

	/**
	 * 乘
	 *
	 * @param i
	 * @param j
	 * @return
	 */
	int mul(int i, int j);

	/**
	 * 加
	 *
	 * @param i
	 * @param j
	 * @return
	 */
	int div(int i, int j);
}

②接口的实现类,这个类只关心核心业务的代码,不校验数据的合法性和记录日志,数据的合法性和日志的记录交给切面来实现

package com.dadi.aop;

import org.springframework.stereotype.Component;

@Component("calculationService")
public class CalculationServiceImpl implements CalculationService {

	@Override
	public int add(int i, int j) {
		return i + j;
	}

	@Override
	public int sub(int i, int j) {
		return i - j;
	}

	@Override
	public int mul(int i, int j) {
		return i * j;
	}

	@Override
	public int div(int i, int j) { return i / j; }
}

③定义一个切面类,其中@Order(int j)是指定切面的优先级,值越小优先级越高

package com.dadi.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Objects;

/**
 * 验证数据的合法性
 */
// @Order中的值越小,优先级越高
@Order(1)
@Aspect
@Component
public class VlidationAspect {
	/**
	 * 验证数据传入的合法性
	 *
	 * @param joinPoint
	 */
	// 环绕通知
	@Around("execution(* com.dadi.aop.CalculationService.div(int, int))")
	public Object aroundMethod(ProceedingJoinPoint joinPoint) {
		// 得到代理对象
		String s = joinPoint.getTarget().getClass().getName();
		// 得到代理的对象的方法
		String methodName = joinPoint.getSignature().getName();
		// 得到代理的对象的方法的属性值
		Object[] args = joinPoint.getArgs();
		// 取得第二个参数的值
		System.out.println("前置通知:对象的路径:" + s + " 拦截到的方法:" + methodName + ",参数: " + Arrays.asList(args));
		String value = String.valueOf(args[1]);
		if (Objects.equals("div", joinPoint.getSignature().getName()) && Objects.equals("0", value)) {
			System.out.println("被除数验证为0,修改为1");
			args[1] = 1;
		}
		Object result = null;
		//执行目标方法
		try {
			result = joinPoint.proceed(args);
			//返回通知
			System.out.println("返回通知:对象的路径:" + s + " 拦截到的方法:"+methodName + " ,结果 " + result);
		} catch (Throwable e) {
			//异常通知
			System.out.println("The method " + methodName + " occurs exception:" + e);
			e.printStackTrace();
		}
		//后置通知
		System.out.println("后置通知:对象的路径:" + s + " 拦截到的方法:" + methodName + ",参数: " + Arrays.asList(args));
		return result;
	}


}

日志通知切面类

package com.dadi.aop;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
// 记录日志通知
@Order(2)
@Aspect
@Component
public class LoggingAspect {
	// exceution对应的表达式 类型 返回值 包路径 方法 类型参数

	/**
	 * 可以在通知方法中添加 JoinPoint 类型的参数, 从中可以访问到方法的签名和方法的参数.
	 */

	@Before("execution(public int com.dadi.aop.CalculationService.add(int, int))")
	public void before(JoinPoint joinPoint) {
		// 得到代理对象
		String s = joinPoint.getTarget().getClass().getName();
		// 得到代理的对象的方法
		String methodName = joinPoint.getSignature().getName();
		// 得到代理的对象的方法的参数
		Object[] args = joinPoint.getArgs();
		System.out.println("前置通知:对象的路径:" + s + " 拦截到的方法:" + methodName + ",参数: " + Arrays.asList(args));
	}

	// 任意类型,任意返回值,包下面的任意对象的任意方法都会被后置通知处理
	@After("execution(* com.dadi.aop.*.sub(..))")
	public void after(JoinPoint joinPoint) {
		// 得到代理对象
		String s = joinPoint.getTarget().getClass().getName();
		// 得到代理的对象的方法
		String methodName = joinPoint.getSignature().getName();
		// 得到代理的对象的方法的参数
		Object[] args = joinPoint.getArgs();
		System.out.println("后置通知:对象的路径:" + s + " 拦截到的方法:" + methodName + ",参数: " + Arrays.asList(args));
	}

}

 ④测试用例

	@Test
	public void add() {
		CalculationService cs = (CalculationService) ac.getBean("calculationService");
		int result = cs.add(3, 5);
		System.out.println(String.format("结果:%s",result));
	}

	@Test
	public void sub() {
	}

	@Test
	public void mul() {
	}

	@Test
	public void div() {
		CalculationService cs = (CalculationService) ac.getBean("calculationService");
		int result = cs.div(3, 1);
		System.out.println(String.format("结果:%s",result));

		int result = cs.div(3, 0);
		System.out.println(String.format("结果:%s",result));
	}


切面的核心思想是动态代理,java的动态代理请参考https://blog.csdn.net/u010285684/article/details/80265745

扫描二维码关注公众号,回复: 3013988 查看本文章




猜你喜欢

转载自blog.csdn.net/u010285684/article/details/80274670
今日推荐