Merge pull request #44 from sjsdfg/20190424

23 注解翻译更新
This commit is contained in:
LingCoder
2019-04-25 12:33:19 +08:00
committed by GitHub

View File

@@ -206,12 +206,6 @@ public @interface SimulatingNull {
### 生成外部文件
<!-- Using javac to Process Annotations -->
## 使用javac处理注解
当有些框架需要一些额外的信息才能与你的源代码协同工作,这种情况下注解就会变得十分有用。像 Enterprise JavaBeans (EJB3 之前)这样的技术,每一个 Bean 都需要需要大量的接口和部署描述文件而这些就是“样板”文件。Web Service自定义标签库以及对象/关系映射工具(例如 Toplink 和 Hibernate通常都需要 XML 描述文件,而这些文件脱离于代码之外。除了定义 Java 类,程序员还必须忍受沉闷,重复的提供某些信息,例如类名和包名等已经在原始类中已经提供的信息。每当你使用外部描述文件时,他就拥有了一个类的两个独立信息源,这经常导致代码的同步问题。同时这也要求了为项目工作的程序员在知道如何编写 Java 程序的同时,也必须知道如何编辑描述文件。
假设你想提供一些基本的对象/关系映射功能,能够自动生成数据库表。你可以使用 XML 描述文件来指明类的名字、每个成员以及数据库映射的相关信息。但是,通过使用注解,你可以把所有信息都保存在 **JavaBean** 源文件中。为此你需要一些用于定义数据库名称、数据库列以及将 SQL 类型映射到属性的注解。
@@ -277,6 +271,186 @@ public @interface SQLInteger {
另外两个 **@interface** 定义的是 SQL 类型。如果希望这个框架更有价值的话,我们应该为每个 SQL 类型都定义相应的注解。不过为为示例,两个元素足够了。
这些 SQL 类型具有 `name()` 元素和 `constraints()` 元素。后者利用了嵌套注解的功能,将数据库列的类型约束信息嵌入其中。注意 `constraints()` 元素的默认值时 **@Constraints**。由于在 **@Constraints** 注解类型之后,没有在括号中指明 **@Constraints** 元素的值,因此,**constraints()** 的默认值为所有元素都为默认值的 **@Constraints** 注解。如果要使得嵌入的 **@Constraints** 注解中的 `unique()` 元素为 true并作为 `constraints()` 元素的默认值,你可以像如下定义:
```java
// annotations/database/Uniqueness.java
// Sample of nested annotations
package annotations.database;
public @interface Uniqueness {
Constraints constraints()
default @Constraints(unique = true);
}
```
下面是一个简单的,使用了如上注解的类:
```java
// annotations/database/Member.java
package annotations.database;
@DBTable(name = "MEMBER")
public class Member {
@SQLString(30) String firstName;
@SQLString(50) String lastName;
@SQLInteger Integer age;
@SQLString(value = 30,
constraints = @Constraints(primaryKey = true))
String reference;
static int memberCount;
public String getReference() { return reference; }
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
@Override
public String toString() { return reference; }
public Integer getAge() { return age; }
}
```
类注解 **@DBTable** 注解给定了元素值 MEMBER它将会作为标的名字。类的属性 **firstName****firstName** 都被注解为 **@SQLString** 类型并且给了默认元素值分别为 30 和 50。这些注解都有两个有趣的地方首先他们都使用了嵌入的 **@Constraints** 注解的默认值;其次,它们都是用了快捷方式特性。如果你在注解中定义了名为 **value** 的元素,并且在使用该注解时,**value** 为唯一一个需要赋值的元素,你就不需要使用名—值对的语法,你只需要在括号中给出 **value** 元素的值即可。这可以应用于任何合法类型的元素。这也限制了你必须将元素命名为 **value**,不过在上面的例子中,这样的注解语句也更易于理解:
```java
@SQLString(30)
```
处理器将在穿件表的时候使用该值设置 SQL 列的大小。
默认值的语法虽然很灵巧,但是它很快就变的复杂起来。以 **reference** 字段的注解为例,上面拥有 **@SQLString** 注解,但是这个字段也将成为表的主键,因此在嵌入的 **@Constraint** 注解中设定 **primaryKey** 元素的值。这时事情就变的复杂了。你不得不为这个嵌入的注解使用很长的名—值对的形式,来指定元素名称和 **@interface** 的名称。同时,由于有特殊命名的 **value** 也不是唯一需要赋值的元素,因此不能再使用快捷方式特性。如你所见,最终结果不算清晰易懂。
### 替代方案
可以使用多种不同的方式来定义自己的注解用于上述任务。例如,你可以使用一个单一的注解类 **@TableColumn**,它拥有一个 **enum** 元素,元素值定义了 **STRING****INTEGER****FLOAT** 等类型。这消除了每个 SQL 类型都需要定义一个 **@interface** 的负担,不过也使得用额外信息修饰 SQL 类型变的不可能,这些额外的信息例如长度或精度等,都可能是非常有用的。
你也可以使用一个 **String** 类型的元素来描述实际的 SQL 类型,比如 “VARCHAR(30)” 或者 “INTEGER”。这使得你可以修饰 SQL 类型,但是这也将 Java 类型到 SQL 类型的映射绑在了一起这不是一个好的设计。你并不想在数据库更改之后重新编译你的代码如果我们只需要告诉注解处理器我们正在使用的是什么“口味favor”的 SQL然后注解助力器来为我们处理 SQL 类型的细节,那将是一个优雅的设计。
第三种可行的方案是一起使用两个注解,**@Constraints** 和相应的 SQL 类型(例如,**@SQLInteger**)去注解同一个字段。这可能会让代码有些混乱,但是编译器允许你对同一个目标使用多个注解。在 Java 8在使用多个注解的时候你可以重复使用同一个注解。
### 注解不支持继承
你不能使用 **extends** 关键字来继承 **@interfaces**。这真是一个遗憾,如果可以定义**@TableColumn** 注解(参考前面的建议),同时嵌套一个 **@SQLType** 类型的注解,讲究将成为一个优雅的设计。按照这种方式,你可以通过继承 **@SQLType** 来创造各种 SQL 类型。例如 **@SQLInteger** 和 **@SQLString**。如果支持继承,就会大大减少打字的工作量并且使得语法更整洁。在 Java 的未来版本中,似乎没有任何关于让注解支持继承的提案,所以在当前情况下,上例中的解决方案可能已经是最佳方案了。
### 实现处理器
下面是一个注解处理器的例子,他将读取一个类文件,检查上面的数据库注解,并生成用于创建数据库的 SQL 命令:
```java
// annotations/database/TableCreator.java
// Reflection-based annotation processor
// {java annotations.database.TableCreator
// annotations.database.Member}
package annotations.database;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class TableCreator {
public static void
main(String[] args) throws Exception {
if (args.length < 1) {
System.out.println(
"arguments: annotated classes");
System.exit(0);
}
for (String className : args) {
Class<?> cl = Class.forName(className);
DBTable dbTable = cl.getAnnotation(DBTable.class);
if (dbTable == null) {
System.out.println(
"No DBTable annotations in class " +
className);
continue;
}
String tableName = dbTable.name();
// If the name is empty, use the Class name:
if (tableName.length() < 1)
tableName = cl.getName().toUpperCase();
List<String> columnDefs = new ArrayList<>();
for (Field field : cl.getDeclaredFields()) {
String columnName = null;
Annotation[] anns =
field.getDeclaredAnnotations();
if (anns.length < 1)
continue; // Not a db table column
if (anns[0] instanceof SQLInteger) {
SQLInteger sInt = (SQLInteger) anns[0];
// Use field name if name not specified
if (sInt.name().length() < 1)
columnName = field.getName().toUpperCase();
else
columnName = sInt.name();
columnDefs.add(columnName + " INT" +
getConstraints(sInt.constraints()));
}
if (anns[0] instanceof SQLString) {
SQLString sString = (SQLString) anns[0];
// Use field name if name not specified.
if (sString.name().length() < 1)
columnName = field.getName().toUpperCase();
else
columnName = sString.name();
columnDefs.add(columnName + " VARCHAR(" +
sString.value() + ")" +
getConstraints(sString.constraints()));
}
StringBuilder createCommand = new StringBuilder(
"CREATE TABLE " + tableName + "(");
for (String columnDef : columnDefs)
createCommand.append(
"\n " + columnDef + ",");
// Remove trailing comma
String tableCreate = createCommand.substring(
0, createCommand.length() - 1) + ");";
System.out.println("Table Creation SQL for " +
className + " is:\n" + tableCreate);
}
}
}
private static String getConstraints(Constraints con) {
String constraints = "";
if (!con.allowNull())
constraints += " NOT NULL";
if (con.primaryKey())
constraints += " PRIMARY KEY";
if (con.unique())
constraints += " UNIQUE";
return constraints;
}
}
```
输出为:
```sql
Table Creation SQL for annotations.database.Member is:
CREATE TABLE MEMBER(
FIRSTNAME VARCHAR(30));
Table Creation SQL for annotations.database.Member is:
CREATE TABLE MEMBER(
FIRSTNAME VARCHAR(30),
LASTNAME VARCHAR(50));
Table Creation SQL for annotations.database.Member is:
CREATE TABLE MEMBER(
FIRSTNAME VARCHAR(30),
LASTNAME VARCHAR(50),
AGE INT);
Table Creation SQL for annotations.database.Member is:
CREATE TABLE MEMBER(
FIRSTNAME VARCHAR(30),
LASTNAME VARCHAR(50),
AGE INT,
REFERENCE VARCHAR(30) PRIMARY KEY);
```
<!-- Using javac to Process Annotations -->
## 使用javac处理注解