定义注解
自定义注解在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();
}
}
测试结果如下: