谈谈Java中⾃定义注解及使⽤场景
Java⾃定义注解⼀般使⽤场景为:⾃定义注解+或者AOP,使⽤⾃定义注解来⾃⼰设计框架,使得代码看起来⾮常优雅。本⽂将先从⾃定义注解的基础概念说起,然后开始实战,写⼩段代码实现⾃定义注解+,⾃定义注解+AOP。
⼀. 什么是注解(Annotation)
Java注解是什么,以下是引⽤⾃的内容
Java注解⼜称Java标注,是JDK5.0版本开始⽀持加⼊源代码的特殊语法元数据。
Java语⾔中的类、⽅法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器⽣成类⽂件时,标注可以被嵌⼊到字节码中。Java虚拟机可以保留标注内容,在运⾏时可以获取到标注内容。当然它也⽀持⾃定义Java标注。
⼆. 注解体系图
元注解:java.lang.annotation中提供了元注解,可以使⽤这些注解来定义⾃⼰的注解。主要使⽤的是Target和Retention注解
注解处理类:既然上⾯定义了注解,那得有办法拿到我们定义的注解啊。flect.AnnotationElement接⼝则提供了该功能。注解的处理是通过java反射来处理的。
如下,反射相关的类Class, Method, Field都实现了AnnotationElement接⼝。
因此,只要我们通过反射拿到Class, Method, Field类,就能够通过getAnnotation(Class<T>)拿到我们想要的注解并取值。
三. 常⽤元注解
Target:描述了注解修饰的对象范围,取值在java.lang.annotation.ElementType定义,常⽤的包括:METHOD:⽤于描述⽅法
PACKAGE:⽤于描述包
PARAMETER:⽤于描述⽅法变量
TYPE:⽤于描述类、接⼝或enum类型
Retention: 表⽰注解保留时间长短。取值在java.lang.annotation.RetentionPolicy中,取值为:
SOURCE:在源⽂件中有效,编译过程中会被忽略
CLASS:随源⽂件⼀起编译在class⽂件中,运⾏时忽略
RUNTIME:在运⾏时有效
只有定义为RetentionPolicy.RUNTIME时,我们才能通过注解反射获取到注解。
所以,假设我们要⾃定义⼀个注解,它⽤在字段上,并且可以通过反射获取到,功能是⽤来描述字段的长度和作⽤。
@Target(ElementType.FIELD) // 注解⽤于字段上
@Retention(RetentionPolicy.RUNTIME) // 保留到运⾏时,可通过注解获取
public @interface MyField {
String description();
int length();
}
四. ⽰例-反射获取注解
先定义⼀个注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {
String description();
int length();
}
通过反射获取注解
public class MyFieldTest {
//使⽤我们的⾃定义注解
@MyField(description = "⽤户名", length = 12)
private String username;
@Test
public void testMyField() {
// 获取类模板
Class c = MyFieldTest.class;
// 获取所有字段
for (Field f : c.getDeclaredFields()) {
// 判断这个字段是否有MyField注解
if (f.isAnnotationPresent(MyField.class)) {
MyField annotation = f.getAnnotation(MyField.class);
System.out.println("字段:[" + f.getName() + "], 描述:[" + annotation.description() + "], 长度:[" + annotation.length() + "]");
}
}
}
}
运⾏结果
应⽤场景⼀:⾃定义注解+实现登录校验
接下来,我们使⽤springboot实现这样⼀个功能,如果⽅法上加了@LoginRequired,则提⽰⽤户该接⼝需要登录才能访问,否则不需要登录。
⾸先定义⼀个LoginRequired注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}
然后写两个简单的接⼝,访问sourceA,sourceB资源
@RestController
public class IndexController {
@GetMapping("/sourceA")
public String sourceA() {
return "你正在访问sourceA资源";
}
@GetMapping("/sourceB")
public String sourceB() {
return "你正在访问sourceB资源";
}
}
没添加之前成功访问
实现spring的HandlerInterceptor 类先实现,但不拦截,只是简单打印⽇志,如下:
public class SourceAccessInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进⼊了");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { }
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
实现spring类WebMvcConfigurer,创建配置类把添加到链中
@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**");
}
}
拦截成功如下
在sourceB⽅法上添加我们的登录注解@LoginRequired
@RestController
public class IndexController {
@GetMapping("/sourceA")
public String sourceA() {
return "你正在访问sourceA资源";
}
@LoginRequired
@GetMapping("/sourceB")
public String sourceB() {
return "你正在访问sourceB资源";
}
}
简单实现登录拦截逻辑
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进⼊了");
// 反射获取⽅法上的LoginRequred注解
HandlerMethod handlerMethod = (HandlerMethod) handler;
LoginRequired loginRequired = Method().getAnnotation(LoginRequired.class);
if (loginRequired == null) {
return true;
}
// 有LoginRequired注解说明需要登录,提⽰⽤户登录
response.setContentType("application/json; charset=utf-8");
return false;
}
运⾏成功,访问sourceB时需要登录了,访问sourceA则不⽤登录
应⽤场景⼆:⾃定义注解+AOP 实现⽇志打印
先导⼊切⾯需要的依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定义⼀个注解@MyLog
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}
定义⼀个切⾯类,见如下代码注释理解:
@Aspect // 1.表明这是⼀个切⾯类
@Component
spring aop应用场景public class MyLogAspect {
// 2. PointCut表⽰这是⼀个切点,@annotation表⽰这个切点切到⼀个注解上,后⾯带该注解的全类名 // 切⾯最主要的就是切点,所有的故事都围绕切点发⽣
// logPointCut()代表切点名称
@Pointcut("@bin.demo.annotationdemo.aoplog.MyLog)")
public void logPointCut() {};
// 3. 环绕通知
@Around("logPointCut()")
public void logAround(ProceedingJoinPoint joinPoint) {
// 获取⽅法名称
String methodName = Signature().getName();
// 获取⼊参
Object[] param = Args();
StringBuilder sb = new StringBuilder();
for (Object o : param) {
sb.append(o + "; ");
}
System.out.println("进⼊[" + methodName + "]⽅法,参数为:" + sb.toString());
// 继续执⾏⽅法
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println(methodName + "⽅法执⾏结束");
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论