在大家使用spring MVC或Hibernate 3.0以上的版本时,可能会注意到annotation带来的方便性,不过这往往让人觉得annotation真的很强大,而这算是一种接近错误的理解吧,annotation其实本身是属于一种文档注解的方式,帮助我们在编译时、运行时、文档生成时使用,部分annotation其实基本和注释差不多,这里其实是要说下annotation的原理,以及各种功能在它上面如何实现的,以及在继承的时候,他会发生什么?为什么会这样?
首先,就我个人使用的理解,annotation是一种在类、类型、属性、参数、局部变量、方法、构造方法、包、annotation本身等上面的一个附属品(ElementType这个枚举中有阐述),他依赖于这些元素而存在,他本身并没有任何作用,annotation的作用是根据其附属在这些对象上,根据外部程序解析引起了他的作用,例如编译时的,其实编译阶段就在运行:java Compiler,他就会检查这些元素,例如:@SuppressWarnings、@Override、@Deprecated等等;
生成文档运行javadoc也是单独的一个进程去解析的,其实他是识别这些内容的,而spring MVC和Hibernate的注解,框架程序在运行时去解析这些annotation,至于运行的初始化还是什么时候要和具体的框架结合起来看,那么今天我们就要说下所谓的annotation是如何实现功能的(再次强调:它本身没有功能,功能又程序决定,他只是上面描述的几大元素的附属品而已,如果认为他本身有功能,就永远不知道annotation是什么);
我们首先自己来写个annotation,写annotation就像写类一样,创建一个java文件,和annotation的名称保持一致,他也会生成class文件,说明他也是java,只是以前要么是interface、abstract class、class开头,现在多了一个@interface,可见它是属于jvm可以识别的一种新的对象,就像序列化接口一样的标记,那么我们简单写一个:
下面的代码可能你看了觉得没啥意思,接着向下可能你会找到有意思的地方:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR , ElementType.FIELD , ElementType.TYPE})
public @interface NewAnnotation {
String value() default "";
}
那么上面的annotation代表:
在Runtime的时候将会使用(RetentionPolicy里面有阐述其他的SOURCE、CLASS级别),可以注解到方法、构造方法、属性、类型和类上面;名称为:NewAnnotation、里面有一个属性为value,为String类型,默认值为空字符串,也就是可以不传递参数。
程序中使用例如:
public class A {
@NewAnnotation
private String b;
@NewAnnotation(value = "abc")
public void setB() {...}
}
那么很多人看到这里都会问,这样写了有什么用途呢?貌似是没啥用途,我第一次看到这里也没太看懂,而且看到spring MVC做得如此多功能,这到底是怎么回事?
再一些项目的框架制作中,我逐步发现一些功能,如果有一种代码的附属品,将会将框架制作得更加漂亮和简洁,于是又联想到了spring的东西,spring的AOP是基于字节码增强技术完成,拦截器的实现不再是神话,那么反过来如如果annotation是可以被解析的,基于annotation的注入就是十分简单明了的事情了,Hibernate也是如此,当然我这不讨论一些解析的缓存问题,因为不会让每个对象都这样去解析一次,都会尽量记忆下来使得性能更高,这里只说他的原理而已。
这里拿一个简单的request对象转换为javaBean对象的,假如我们用DO为后缀,而部分请求的参数名和实际的属性并不一样(一般规范要求是一样的),其次在网络传输中某个项目前台的日期提交到后台都是以毫秒之方式提交到后台,但是需要转换为对应的字符串格式来处理,提交中包含:String、int、Integer、Long、long、String[]这几种数据类型,日期的我们大家可以扩展,带着这些小需求我们来简单写一个:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ReuqestAnnotation {
String name() default "";//传入的参数名
boolean dateString() default false;//是否为dateString类型
}
这里的annotation一个是name一个是dateString,两个都有默认值,name我们认为是request中参数名,否则直接以属性名为主,dateString属性是否是日期字符串(前面描述传递的日期都会变成毫秒值,所以需要自动转换下)。
那么我们写一个DO:
import xxx.xxx.ReuqestAnnotation;//import部分请自己根据项目引用
public class RequestTemplateDO {
private String name;
@ReuqestAnnotation(name = "myemail")
private String email;
private String desc;
@ReuqestAnnotation(dateString = true)
private String inputDate;
private Integer int1;
private int int2;
public int getInt1() {
return int1;
}
public int getInt2() {
return int2;
}
public String getInputDate() {
return inputDate;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public String getDesc() {
return desc;
}
}
注意这里没有写set方法,我们为了说明问题,而不是怎么去调用set方法,所以我们直接用属性去设置值,属性是private一样可以设置,下面就是一个转换方法:
假如有一个HttpUtils,我们写一个静态方法:
/**
* 通过request获取对应的对象
* @param <T>
* @param request
* @param clazz
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
@SuppressWarnings("unchecked")
public static <T extends Object> T convertRequestToDO(HttpServletRequest request , Class<T> clazz)
throws InstantiationException, IllegalAccessException {
Object object = clazz.newInstance();
Field []fields = clazz.getDeclaredFields();
for(Field field : fields) {
field.setAccessible(true);
String requestName = field.getName();
ReuqestAnnotation requestAnnotation = field.getAnnotation(ReuqestAnnotation.class);
boolean isDateString = false;
if(requestAnnotation != null) {
if(StringUtils.isNotEmpty(requestAnnotation.name())) requestName = requestAnnotation.name();
isDateString = requestAnnotation.dateString();
}
Class <?>clazzf = field.getType();
if(clazzf == String.class) {
if(isDateString) {
String dateStr = request.getParameter(requestName);
if(dateStr != null) {
field.set(object, DateTimeUtil.getDateTime(new Date(Long.valueOf(dateStr)) , DateTimeUtil.DEFAULT_DATE_FORMAT));
}
}else {
field.set(object, request.getParameter(requestName));
}
}
else if(clazzf == Integer.class) field.set(object, getInteger(request.getParameter(requestName)));
else if(clazzf == int.class) field.set(object, getInt(request.getParameter(requestName)));
else if(clazzf == Long.class) field.set(object, getLongWapper(request.getParameter(requestName)));
else if(clazzf == long.class) field.setLong(object, getLong(request.getParameter(requestName)));
else if(clazzf == String[].class) field.set(object, request.getParameterValues(requestName));
}
return (T)object;
}
这里面就会负责将request相应的值填充到数据中,返回对应的DO,而代码中使用的是:
RequestTemplateDO requestTemplateDO = HttpUtils.convertRequestToDO(request , RequestTemplateDO.class);
注意:这部分spring帮我们写了,只是我在说大概原理,而且spring本身实现和这部分也有区别,也更加完整,这里仅仅是为了说明局部问题。spring在拦截器中拦截后就可以组装好这个DO,所以在spring MVC中可以将其直接作为扩展参数传递进入我们的业务方法中,首先知道业务方法的annotation,根据URL决定方法后,获取参数列表,根据参数类型,如果是业务DO,那么填充业务DO即可,Hibernate也可以同样的方式去推理。
OK,貌似很简单,如果你真的觉得简单了,那么这块你就真的懂了,那么我们说点特殊的,就是继承,貌似annotation很少去继承,但是在我遇到一些朋友的项目中,由于部分设计需要或本身设计缺陷但是又不想修改的时候,就会遇到,多个DO大部分属性是一样的,如果不抽象父亲类出来,如果修改属性要同时修改非常多的DO,而且操作的时候绝大部分情况是操作这些共享的属性,所以还想用上溯造型来完成代码的通用性并保持多态,当时一问我还真蒙了,因为是基于类似annotation的一些框架,例如hibernate,后来带着问题做了很多测试并且和资料对应上,是如果annotation在class级别、构造方法级别,是不会被子类所拥有的,也就是当子类通过XXX.class.getAnnotation(XXXAnnotation.class)的时候是获取不到,不过public类型的方法、public的属性是可以的,其次,如果子类重写了父类的某个属性或某个方法,不管子类是否写过annotation,这个子类中父类的属性或方法的所有的annotation全部失效,也就是如父亲类有一个属性A,有两个annotation,若A是public的,子类可以继承这个属性和annotation,若子类也有一个A属性,不管A是否有annotation,父类中这些annotation在子类中都将失效掉。
这就是为什么我说annotation是属性、方法、包。。。的附属品,他是被绑定在这些元素上的,而并非拥有实际功能,当重写的时候,将会被覆盖,属性、方法当被覆盖的时候,其annotation也随之被覆盖,而不会按照annotation再单独有一个覆盖;所以当时要解决那个问题,我就告诉他,要用的方法只有public,否则没办法,即使用反射也不行,因为hibernate根本找不到这个field,还没有机会提供一个setAccessible的能力,因为这个时候根本看不到这些field,但是通过父类本身可以看到这些field;就技术层面是这样的,否则只有修改设计是最佳的方法。
分享到:
相关推荐
Java注解(Annotation)是Java语言的一个重要特性,它为元数据提供了强大的支持。元数据是一种描述数据的数据,可以提供有关代码的附加信息,而这些信息并不直接影响代码的执行。在Java中,注解用于向编译器、JVM或...
Java 5引入的注解(Annotation)是一种元数据,它提供了在代码中嵌入信息的方式,这些信息可以被编译器、JVM或其他工具在编译时或运行时使用。注解可以用来简化代码,提高可维护性,并帮助工具进行静态分析。 1. ...
Java-Annotation使用大全 Java-Annotation使用大全 Java-Annotation使用大全
### Java Annotation 概述与应用 #### 一、Java Annotation 的定义及作用 Java Annotation(注解)是自 Java 5.0 开始引入的一种语言元素,它为开发者提供了在代码中添加元数据的能力。简单来说,注解就像是对代码...
Java annotation 什么是java annotation?annotation 的7种标注类型。nnotation提供了一条与程序元素关联任何信息或者任何元数据(metadata)的途径。从某些方面看,annotation就像修饰符一样被使用,并应用于包、...
"基于Annotation的Java单元测试框架" 本文主要介绍了基于Annotation的Java单元测试框架,讨论了Annotation在Java EE中的应用、反射技术的使用和JUnit单元测试框架的结合,建立了一个自动化单元测试框架结构。 一、...
Java 1.5 引入了一种新的元编程机制——注解(Annotation),极大地增强了代码的可读性和可维护性。注解是一种在代码中添加元数据的方式,它允许程序员在源代码上添加一些信息,这些信息可以被编译器或运行时环境...
总之,Java Annotation 提供了一种强大的方式来增强代码的元信息,使得工具和框架能更好地理解并操作代码,从而提高了开发效率和代码质量。通过熟练掌握 Annotation 的使用,开发者可以编写出更加简洁、易于维护的...
Java SSH项目是基于三个主要框架——Struts、Spring和Hibernate构建的企业级Web应用程序。这个项目中,Hibernate作为ORM(对象关系映射)工具被用来处理数据库操作,而使用了注解方式来配置Hibernate,这是一种更加...
Java 注解(Annotation)是Java语言提供的一种元编程机制,它允许程序员在源代码的各个元素(如类、方法、变量等)上添加信息。这些信息可以被编译器或运行时系统用来验证代码、执行特定操作或者提供额外的运行时...
《Java Annotation手册》 在Java编程语言中,注解(Annotation)是一种元数据,它提供了在编译时或运行时处理代码的一种方式。通过注解,开发者可以向编译器或JVM提供有关代码的附加信息,而这些信息通常不直接影响...
Java注解(JDK5)是Java编程语言中的一个重要特性,自Java 5版本开始引入。注解(Annotation)提供了一种元数据的形式,允许程序员在代码中嵌入额外的信息,这些信息可以被编译器、JVM或者专门的处理工具在编译时或...
Java注解(Annotation)是Java语言的一个重要特性,它为元数据提供了强大的支持。元数据是关于数据的数据,可以提供额外的信息,这些信息虽然不是程序运行所必需的,但能够帮助编译器、JVM(Java虚拟机)或工具更好...
Java Annotation,也称为注解,是Java编程语言中的一种元数据机制,用于向编译器、JVM或工具提供有关代码的附加信息。这些信息不直接影响代码的执行,但可以被编译器或运行时环境用来执行特定的操作,如代码分析、...
网上的Java并发编程源码一般都缺失一些Annotation,导致导入会报错。
Java 注解(Annotation)是Java语言的一个重要特性,它为代码提供元数据,即关于代码的信息,但这些信息不直接影响程序的运行。注解在Java中主要用于编译器检查、运行时处理、框架生成元数据等场景。本篇将深入探讨...
Annotation简化了代码,提高了可读性和可维护性,尤其是在现代Java框架中广泛应用。 ### Annotation的基本概念 1. **Annotation类型**:定义了一个特定的注解,类似于接口,包含名称和多个注解元素。每个定义的...