自定义注解实现Spring AOP

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Mr_FLM/article/details/92090447

定义注解

    自定义注解在Spring AOP中用来设置切点(Pointcut)。定义一个@AspectAnnotation 注解,具体实现如下:

package com.ming.ssm.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD) //限定自定义注解的应用场合
@Retention(RetentionPolicy.RUNTIME) //限定自定义注解的作用范围
@Documented //将自定义注解信息添加到JavaDoc文档中
public @interface AspectAnnotation {
	
	/*
	 * 1、属性修饰符默认为public;
	 * 2、如果注解只有一个元素,一般取属性名为value,使用注解时可省略属性名,直接赋值;
	 * 3、如果没有默认值,后续使用注解必须赋值。
	 */
	String value() default "";
}

定义切面(Aspect)

package com.ming.ssm.aspect;

import java.lang.reflect.Method;

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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.ming.ssm.annotation.AspectAnnotation;

@Aspect
@Order(-1) //@Order值越小,越先被执行(比如动态切换数据源的问题,如果事务在前,数据源切换在后,会导致数据源切换失效,所以就用到了@Order(排序)这个注解,切面优先执行)
@Component
public class CustomAspect {
	
	/**
	 * 定义前置通知(Advice)
	 * @param point 连接点对象
	 * @param test 注解对象
	 */
	@Before("@annotation(test)")
	public void beforeMethod(JoinPoint point, AspectAnnotation test) {
	
		System.out.println("----------前置通知开始执行----------");
		
		//获取注解值
		System.out.println("直接通过注解获取属性值:" + test.value());
		
		//获取被增强的方法所在的对象:getTarget()
		//?表示不确定的java类型
		Class<?> className = point.getTarget().getClass(); //获取被增强类的class对象
		//获取被增强的方法信息:getSignature()
		String methodName = point.getSignature().getName(); //获取被增强方法的方法名
		//获取被增强的方法参数类型
		Class<?>[] methodArgs = ((MethodSignature)point.getSignature()).getParameterTypes();
		try {
			//获取被增强的方法对象
			Method method = className.getMethod(methodName, methodArgs);
			//判断方法上的注解是否是AspectAnnotation注解
			if (method.isAnnotationPresent(AspectAnnotation.class)) {
				//获取方法上的注解
				AspectAnnotation annotation = method.getAnnotation(AspectAnnotation.class);
				//获取注解属性值
				System.out.println("间接通过连接点获取属性值:" +  annotation.value());
			}
		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
		System.out.println("----------前置通知执行完毕----------");
	}
	
	/**
	 * 定义后置通知(Advice)
	 */
	@After("@annotation(com.ming.ssm.annotation.AspectAnnotation)")
	public void afterMethod() {
	
		System.out.println("++++++++++后置通知已经执行++++++++++");
	}
}

    注:上面定义的切面实现中,前置通知与后置通知由不同的方式实现。如果不需要获取注解中的属性值和连接点(被增强的方法)的信息,可直接通过下面方式实现:

	//@annotation中的值:自定义注解的包名+类名
    @After("@annotation(com.ming.ssm.annotation.AspectAnnotation)")
	public void afterMethod() {	
		//具体通知信息
	}

如果需要获取注解中的属性值和连接点(被增强的方法)的信息,可直接通过下面方式实现:

	//@annotation中的值(test)与 形参(test)相对应
    @Before("@annotation(test)")
	public void beforeMethod(JoinPoint point, AspectAnnotation test) {
		//具体通知信息
	}

应用自定义注解

package com.ming.ssm.component;

import org.springframework.stereotype.Component;

import com.ming.ssm.annotation.AspectAnnotation;

@Component
public class AnnotationService {
	/**
	 * 定义待增强的方法
	 */
	@AspectAnnotation("It's good") //利用注解设置切点
	public void annoTest() {
		System.out.println("被增强的方法已执行!!!");
	}
}

单元测试

    注:springboot进行单元测试时,一定要把工程的启动类放在根包(com.ming.ssm)下,否则会报IllegalStateException异常。

package com.ming.ssm;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.ming.ssm.service.AnnotationService;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootCustomerApplicationTests {
	
	@Autowired
	private AnnotationService annotationService;
	
	@Test
	public void contextLoads() {
		annotationService.annoTest();
	}
}

测试结果如下:

猜你喜欢

转载自blog.csdn.net/Mr_FLM/article/details/92090447