java之annotation与框架的那些秘密

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介:

在大家使用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;就技术层面是这样的,否则只有修改设计是最佳的方法。


目录
相关文章
|
1月前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
1月前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
1月前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
148 3
|
26天前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
37 4
|
1月前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
218 12
基于开源框架Spring AI Alibaba快速构建Java应用
|
1月前
|
消息中间件 Java 数据库连接
Java 反射最全详解 ,框架设计必掌握!
本文详细解析Java反射机制,包括反射的概念、用途、实现原理及应用场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Java 反射最全详解 ,框架设计必掌握!
|
1月前
|
开发框架 Java 关系型数据库
Java哪个框架适合开发API接口?
在快速发展的软件开发领域,API接口连接了不同的系统和服务。Java作为成熟的编程语言,其生态系统中出现了许多API开发框架。Magic-API因其独特优势和强大功能,成为Java开发者优选的API开发框架。本文将从核心优势、实际应用价值及未来展望等方面,深入探讨Magic-API为何值得选择。
51 2
|
1月前
|
前端开发 Java 数据库连接
你不可不知道的JAVA EE 框架有哪些?
本文介绍了框架的基本概念及其在编程领域的应用,强调了软件框架作为通用、可复用的软件环境的重要性。文章分析了早期Java EE开发中使用JSP+Servlet技术的弊端,包括可维护性差和代码重用性低等问题,并阐述了使用框架的优势,如提高开发效率、增强代码规范性和可维护性及提升软件性能。最后,文中详细描述了几种主流的Java EE框架,包括Spring、Spring MVC、MyBatis、Hibernate和Struts 2,这些框架通过提供强大的功能和支持,显著提升了Java EE应用的开发效率和稳定性。
75 1
|
1月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
49 0
|
1月前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
34 0