java自定义注解
一、注解定义
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
二、元注解
元注解(Meta-Annotation)是用于注解其他注解的注解,它们提供了一种方式来指定注解的行为和用法。在 Java 中,有四种元注解,它们分别是 @Retention、@Target、@Inherited 和 @Documented。
1.@Rentention
表示注解的生命周期,根据RententionPolicy属性来确定,该属性有三个枚举值:
- SOURCE: 注解只保留在源代码中,编译器会忽略它。
- CLASS: 注解保留在编译的字节码中,在运行时会被忽略。
- RUNTIME: 注解保留在编译后的字节码中,并在运行时可用。
2.@TARGET
该注解用于指定注解的标注位置,有一个ElementType类型的数组属性value,ElementType有以下枚举值
- TYPE:类、接口或枚举类型。
- FIELD:字段、枚举常量。
- METHOD:方法。
- PARAMETER:方法参数。
- CONSTRUCTOR:构造方法。
- LOCAL_VARIABLE:局部变量。
- ANNOTATION_TYPE:注解类型。
- PACKAGE:包。
- TYPE_PARAMETER:类型参数(Java 8+)。
- TYPE_USE:类型使用(Java 8+)。
3.@Inherited
如果一个类上添加了@Inherited修饰的注解,其子类也会继承这个注解
注意:
- 对接口无效,接口用上了@Inherited修饰的注解,实现类不会继承
- 只有注解使用在类上,才生效;用在属性和方法上,不生效
4.@Documented
@Documented 用于指定注解是否会被包含在 Javadoc 中。如果一个注解被 @Documented 修饰,那么它的文档将会包含在 Javadoc 中。
三、自定义注解
使用@interface 自定义注解
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD,ElementType.TYPE})
public @interface MyAnnotation {
String name();
}
使用注解
@MyAnnotation(name = "test")
public class TestAnnotation {
}
测试
public static void main(String[] args) {
MyAnnotation annotation = TestAnnotation.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.name());
}
四、自定义注解配合aop使用
利用自定义注解加spring的AOP可以实现对方法的增强,本次演示一个可以输出方法运行时间的注解
1.自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimeSpend {
String methodName();
}
2.aop切面
@Aspect
@Component
@Slf4j
public class TimeJoinPoint {
@Pointcut("@annotation(org.example.annotation.TimeSpend)")
public void aopPoint(){}
@Around("aopPoint()")
public Object doAround(ProceedingJoinPoint jp) throws Throwable {
Method method = getMethod(jp);
TimeSpend annotation = method.getAnnotation(TimeSpend.class);
long start = System.currentTimeMillis();
Object proceed = jp.proceed();
long end = System.currentTimeMillis();
log.info("{}耗时:{}s",annotation.methodName(),(end-start)/1000);
return proceed;
}
private Method getMethod(ProceedingJoinPoint jp) throws NoSuchMethodException {
MethodSignature signature =(MethodSignature) jp.getSignature();
return signature.getMethod();
}
}
1.使用注解 @Aspect,定义切面类。这是一个非常常用的切面定义方式。
2.@Component 注解,将类生成为 Bean 对象。
3.@Pointcut
定义切点。在 Pointcut 中提供了很多的切点寻找方式
// 任意公共方法的执行:
execution(public * *(..))
// 任何一个名字以“set”开始的方法的执行:
execution(* set*(..))
// AccountService接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
// 在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
// 在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
// 在service包中的任意连接点(在Spring AOP中只是方法执行):
within(com.xyz.service.*)
// 在service包或其子包中的任意连接点(在Spring AOP中只是方法执行):
within(com.xyz.service..*)
// 实现了AccountService接口的代理对象的任意连接点 (在Spring AOP中只是方法执行):
this(com.xyz.service.AccountService)// 'this'在绑定表单中更加常用
// 实现AccountService接口的目标对象的任意连接点 (在Spring AOP中只是方法执行):
target(com.xyz.service.AccountService) // 'target'在绑定表单中更加常用
// 任何一个只接受一个参数,并且运行时所传入的参数是Serializable 接口的连接点(在Spring AOP中只是方法执行)
args(java.io.Serializable) // 'args'在绑定表单中更加常用; 请注意在例子中给出的切入点不同于 execution(* *(java.io.Serializable)): args版本只有在动态运行时候传入参数是Serializable时才匹配,而execution版本在方法签名中声明只有一个 Serializable类型的参数时候匹配。
// 目标对象中有一个 @Transactional 注解的任意连接点 (在Spring AOP中只是方法执行)
@target(org.springframework.transaction.annotation.Transactional)// '@target'在绑定表单中更加常用
// 任何一个目标对象声明的类型有一个 @Transactional 注解的连接点 (在Spring AOP中只是方法执行):
@within(org.springframework.transaction.annotation.Transactional) // '@within'在绑定表单中更加常用
// 任何一个执行的方法有一个 @Transactional 注解的连接点 (在Spring AOP中只是方法执行)
@annotation(org.springframework.transaction.annotation.Transactional) // '@annotation'在绑定表单中更加常用
// 任何一个只接受一个参数,并且运行时所传入的参数类型具有@Classified 注解的连接点(在Spring AOP中只是方法执行)
@args(com.xyz.security.Classified) // '@args'在绑定表单中更加常用
// 任何一个在名为'tradeService'的Spring bean之上的连接点 (在Spring AOP中只是方法执行)
bean(tradeService)
// 任何一个在名字匹配通配符表达式'*Service'的Spring bean之上的连接点 (在Spring AOP中只是方法执行)
bean(*Service)
用了@AspectJ框架为AOP的实现提供了一套注解。
注解名称 | 解释 |
---|---|
@Aspect | 用来定义一个切面。 |
@pointcut | 用于定义切入点表达式。在使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点名称,这个方法签名就是一个返回值为void,且方法体为空的普通方法。 |
@Before | 用于定义前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式(可以是已有的切入点,也可以直接定义切入点表达式)。 |
@AfterReturning | 用于定义后置通知,相当于AfterReturningAdvice。在使用时可以指定pointcut / value和returning属性,其中pointcut / value这两个属性的作用一样,都用于指定切入点表达式。 |
@Around | 用于定义环绕通知,相当于MethodInterceptor。在使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。 |
@After-Throwing | 用于定义异常通知来处理程序中未处理的异常,相当于ThrowAdvice。在使用时可指定pointcut / value和throwing属性。其中pointcut/value用于指定切入点表达式,而throwing属性值用于指定-一个形参名来表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。 |
@After | 用于定义最终final 通知,不管是否异常,该通知都会执行。使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。 |
@DeclareParents | 用于定义引介通知,相当于IntroductionInterceptor (不要求掌握)。 |