十年前的2004年 , Java 1.5开始提供注释。 很难想象没有此功能的代码。 实际上,首先引入了注释,以减轻开发人员编写繁琐的样板代码的痛苦,并使代码更具可读性。 考虑一下J2EE 1.4(没有可用的注释)和Java EE5。注释的采用通过消除所有配置XML大大简化了Java EE应用程序的开发。 即使在今天,更多的注释仍被添加到最新版本的Java EE中。 这样做的目的是减轻开发人员的负担并提高生产率。 其他技术和框架也广泛使用它们。
到处都有注释
让我们看一个有关注释如何简化代码的示例(摘自我有关JPA实体图的文章 ):
电影.java
@Entity
@Table(name = "MOVIE_ENTITY_GRAPH")
@NamedQueries({@NamedQuery(name = "Movie.findAll", query = "SELECT m FROM Movie m")
})
@NamedEntityGraphs({@NamedEntityGraph(name = "movieWithActors",attributeNodes = {@NamedAttributeNode("movieActors")}),@NamedEntityGraph(name = "movieWithActorsAndAwards",attributeNodes = {@NamedAttributeNode(value = "movieActors", subgraph = "movieActorsGraph")},subgraphs = {@NamedSubgraph(name = "movieActorsGraph",attributeNodes = {@NamedAttributeNode("movieActorAwards")})})
})
public class Movie implements Serializable {@Idprivate Integer id;@NotNull@Size(max = 50)private String name;@OneToMany@JoinColumn(name = "ID")private Set<MovieActor> movieActors;@OneToMany(fetch = FetchType.EAGER)@JoinColumn(name = "ID")private Set<MovieDirector> movieDirectors;@OneToMany@JoinColumn(name = "ID")private Set<MovieAward> movieAwards;
}
等一下! 简化吗? 真? 注释不应该使我的代码更具可读性吗? 此示例具有比实际代码更多的注释。 公平地说,我不包括获取器和设置器。 同样,某些带注释的代码可以更好地压缩,但这会使代码更难阅读。 当然,这是一个极端的情况。 无论如何,我很高兴,因为我获得了“年度注释狂”称号 。 谢谢卢卡斯!
我们非常依赖注释,以至于最终滥用它们。 有趣的是,在某些情况下,批注引起了他们打算解决的相同问题。
如果?
让我们像这样重写前面的示例:
电影.java
@MovieEntity
@FindAll
@LoadWithActors
@LoadWithActorsAndAwards
public class Movie implements Serializable {@Idprivate Integer id;@Nameprivate String name;@MovieActorsprivate Set<MovieActor> movieActors;@MovieDirectorsprivate Set<MovieDirector> movieDirectors;@MovieAwardsprivate Set<MovieAward> movieAwards;
}
它肯定看起来更具可读性。 但是这些注释不存在。 他们来自哪里?
@LoadWithActors
LoadWithActors.java
@NamedEntityGraph(name = "movieWithActors",attributeNodes = {@NamedAttributeNode("movieActors")}
)
public @interface LoadWithActors {}
@LoadWithActorsAndAwards
LoadWithActorsAndAwards.java
@NamedEntityGraph(name = "movieWithActorsAndAwards",attributeNodes = {@NamedAttributeNode(value = "movieActors", subgraph = "movieActorsGraph")},subgraphs = {@NamedSubgraph(name = "movieActorsGraph",attributeNodes = {@NamedAttributeNode("movieActorAwards")})}
)
public @interface LoadWithActorsAndAwards {}
其余的依次类推。 你有感觉。 这个想法是将注释元数据提取并分组到您自己的自定义注释中。 然后,您的注释可以用于表示代码中所有带注释的数据,从而更易于理解。 就像Java 8 Lambdas一样,读起来就像问题声明一样。
只是另一个例子:
WoWBusinessBean.java
@Named
@Stateless
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
@ApplicationPath("/resources")
@Path("wowauctions")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class WoWBusinessBean extends Application implements WoWBusiness {}
改写:
WoWBusinessBean.java
@RestStatelessBean("wowauctions")
public class WoWBusinessBean extends Application implements WoWBusiness {}
@RestStatelessBean
RestStatelessBean
@Named
@Stateless
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
@ApplicationPath("/resources")
@Path("#{path}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public @interface RestStatelessBean {String value() default "#{path}";
}
通常,我只编写一次这些注释来定义POJO的行为,而不再关注它们。 如果我可以为所有无状态休息服务重用一个注释,那将有多酷?
另一个不错的效果是,注释配置元数据没有直接绑定在代码中。 相反,它被抽象为另一个注释。 在这种情况下,有可能在编译/运行时中覆盖或替换这些值。
元注释
我最初是由David Blevins听说过这个概念的。 他在“ 元注释”中写了一篇关于这些想法的很好的文章,甚至写了一个可能的实现 。
为注释继承,抽象和封装提供简单的Java SE支持会很方便。 这是所有现有技术处理注释方式的重大转变。 只有每个人都开始支持这种行为才有意义。
有人可能会问,我们真的需要此功能吗? 让我们尝试权衡一些利弊:
优点
- 简化代码。
- 注释重用。
- 注释配置不直接与代码绑定。 值可以被覆盖。
缺点
- 另一层抽象。
- 自由地创建自定义注释可以掩盖真实行为。
- 可能会遇到某种多重继承陷阱。
结论
在可预见的将来,这种元注释不太可能在Java SE中提供。 在Java 9中,大多数焦点都放在Jigsaw中。 除了值类型和泛型专业化之外,关于Java 10的信息很少。 实际上,对于纯Java SE,所有这些注释问题都不是真正的问题。
源文件中存在的批注数量正成为有关可读性和可维护性的问题。 对于Java EE和其他类似技术尤其如此。 考虑一下HTML和CSS。 如果您正在开发HTML页面,而只需要几个CSS样式,则通常将它们内联到元素中或直接将它们包括在页面中。 如果开始使用太多样式,则可以将它们提取到外部CSS文件中,然后应用样式。 我确实认为必须通过采用元注释或其他方式来完成某些工作。 你还有其他建议吗? 请分享!
资源资源
不要忘记查看David Blevins关于Meta Annotations的帖子。 他的解释比我更好,包括技术细节。
也是带有元注释的JavaOne EJB演示文稿,由David Blevins讨论了这些想法。
还有什么比听大卫·布莱文斯本人更好的呢? Java EE 7,无限可扩展性遇到无限重用 。
翻译自: https://www.javacodegeeks.com/2014/12/annotations-annotations-everywhere.html