如果某一个 API 特别复杂,您可以通过多个 Java 类来实现它。如需让不同的类属于同一个 API,您必须执行以下操作:
- 在每个类的
@Api
注释中为其指定相同的name
和version
字符串。 - 在
web.xml
中用英文逗号分隔的列表添加类 API。
例如,以下两个类都属于 tictactoe
API:
@Api(name = "tictactoe", version = "v1")
class TicTacToeA { … }
@Api(name = "tictactoe", version = "v1")
class TicTacToeB { … }
API 配置通过 @Api
注释属性指定。但是,对于同一 API 中的多个类,@Api
要求不仅仅是在每个类的 @Api
注释中指定相同的 name
和 version
字符串。实际上,如果类的 @Api
属性中指定的 API 配置存在任何差异,后端 API 将无法运行。多类 API 中类的 @Api
属性存在任何差异都会导致 API 配置“有歧义”,这样的配置在 App Engine 的 Cloud Endpoints Frameworks 中无法运行。
有几种方法可以创建无歧义的多类 API:
- 手动确保单个 API 中的所有类具有完全相同的
@Api
注释属性。 - 通过 Java 继承使用注释继承。
在此继承情况下,单个 API 中的所有类都从由
@Api
注释的公共基类继承相同的 API 配置。 - 通过
@ApiReference
注释在单个 API 中的所有类上使用注释继承,使它们引用由@Api
注释的公共类的同一 API 配置。
为类之间可以不同的属性使用 @ApiClass
如需使用此功能,您需要执行以下导入:
import com.google.api.server.spi.config.ApiClass;
虽然一个 API 中所有类的 @Api
注释中的属性必须匹配,但您能够使用 @ApiClass
注释来提供类之间可以不同的属性。例如:
// API methods implemented in this class allow only "clientIdA".
@Api(name = "tictactoe", version = "v1")
@ApiClass(clientIds = { "clientIdA" })
class TicTacToeA { … }
// API methods implemented in this class provide unauthenticated access.
@Api(name = "tictactoe", version = "v1")
class TicTacToeB { … }
其中 TicTacToeA
使用包含允许的客户端 ID 的白名单来限制访问权限,TicTacToeB
不限制访问权限。
由 @ApiClass
注释提供的所有属性必须在 @Api
注释中包含等效属性。请注意,@Api
等效属性充当 API 范围的默认值。如果在 @Api
中指定的同一属性存在 API 范围的默认值,则特定于类的 @ApiClass
属性将替换 API 范围的默认值。
以下示例演示了特定于类的 @ApiClass
等效属性替换 @Api
属性:
// For this class "boards" overrides "games".
@Api(name = "tictactoe", version = "v1", resource = "games")
@ApiClass(resource = "boards")
class TicTacToeBoards { … }
// For this class "scores" overrides "games".
@Api(name = "tictactoe", version = "v1", resource = "games")
@ApiClass(resource = "scores")
class TicTacToeScores { … }
// For this class, the API-wide default "games" is used as the resource.
@Api(name = "tictactoe", version = "v1", resource = "games")
class TicTacToeGames { … }
注释继承
@Api
和 @ApiClass
注释属性可从其他类继承,并且可以通过 Java 继承或 @ApiReference
继承替换各个属性。
使用 Java 继承
如果一个类继承另一个具有 @Api
或 @ApiClass
注释的类,其行为与本身使用相同属性进行注释一样。例如:
@Api(name = "tictactoe", version = "v1")
class TicTacToeBase { … }
// TicTacToeA and TicTacToeB both behave as if they have the same @Api annotation as
// TicTacToeBase
class TicTacToeA extends TicTacToeBase { … }
class TicTacToeB extends TicTacToeBase { … }
注释仅可通过 Java 子类继承,而不能通过接口实现继承。例如:
@Api(name = "tictactoe", version = "v1")
interface TicTacToeBase { … }
// Does *not* behave as if annotated.
class TicTacToeA implements TicTacToeBase { … }
因此,框架注释不支持任何类型的多重继承。
继承也适用于 @ApiClass
:
@ApiClass(resource = "boards")
class BoardsBase { … }
// TicTacToeBoards behaves as if annotated with the @ApiClass from BoardsBase.
// Thus, the "resource" property will be "boards".
@Api(name = "tictactoe", version = "v1", resource = "scores")
class TicTacToeBoards extends BoardsBase { … }
其中 TicTacToeBoards
从 BoardsBase
继承 resource
属性值 boards
,这会替换其 @Api
注释中的 resource
属性设置 (scores
)。请记住,如果任何类在 @Api
注释中指定了 resource 属性,则所有类都需要在 @Api
注释中指定相同的设置;这种继承方法允许您替换该 @Api
属性。
使用 @ApiReference
继承
如需使用此功能,您需要执行以下导入:
import com.google.api.server.spi.config.ApiReference;
@ApiReference
注释提供了另一种指定注释继承的方法。如果一个类使用 @ApiReference
指定另一个具有 @Api
或 @ApiClass
注释的类,其行为与本身使用相同属性进行注释一样。例如:
@Api(name = "tictactoe", version = "v1")
class TicTacToeBase { … }
// TicTacToeA behaves as if it has the same @Api annotation as TicTacToeBase
@ApiReference(TicTacToeBase.class)
class TicTacToeA { … }
如果同时使用 Java 继承和 @ApiReference
,则注释仅通过 @ApiReference
注释继承。该类中通过 Java 继承获得的 @Api
和 @ApiClass
注释会被忽略。例如:
@Api(name = "tictactoe", version = "v1")
class TicTacToeBaseA { … }
@Api(name = "tictactoe", version = "v2")
class TicTacToeBaseB { … }
// TicTacToe will behave as if annotated the same as TicTacToeBaseA, not TicTacToeBaseB.
// The value of the "version" property will be "v1".
@ApiReference(TicTacToeBaseA.class)
class TicTacToe extends TicTacToeBaseB { … }
替换继承的配置
无论是使用 Java 继承还是 @ApiReference
继承配置,您都可以使用新 @Api
或 @ApiClass
注释替换继承的配置。只有新注释中指定的配置属性会被替换。未指定的属性仍会继承。
例如:
@Api(name = "tictactoe", version = "v2")
class TicTacToe { … }
// Checkers will behave as if annotated with name = "checkers" and version = "v2"
@Api(name = "checkers")
class Checkers extends TicTacToe { … }
替换继承也适用于 @ApiClass
:
@Api(name = "tictactoe", version = "v1")
@ApiClass(resource = "boards", clientIds = { "c1" })
class Boards { … }
// Scores will behave as if annotated with resource = "scores" and clientIds = { "c1" }
@ApiClass(resource = "scores")
class Scores { … }
通过 @ApiReference
继承时,替换也有效:
@Api(name = "tictactoe", version = "v2")
class TicTacToe { … }
// Checkers will behave as if annotated with name = "checkers" and version = "v2"
@ApiReference(TicTacToe.class)
@Api(name = "checkers")
class Checkers { … }
继承 @ApiMethod
注释
@ApiMethod
注释可以从已替换的方法继承。例如:
class TicTacToeBase {
@ApiMethod(httpMethod = "POST")
public Game setGame(Game game) { … }
}
@Api(name = "tictactoe", version = "v1")
class TicTacToe extends TicTacToeBase {
// setGame behaves as if annotated with the @ApiMethod from TicTacToeBase.setGame.
// Thus the "httpMethod" property will be "POST".
@Override
public Game setGame(Game game) { … }
}
与 @Api
和 @ApiClass
注释继承类似,如果多个相互替换的方法包含 @ApiMethod
注释,则可以替换单个的属性。例如:
class TicTacToeBase {
@ApiMethod(httpMethod = "POST", clientIds = { "c1" })
public Game setGame(Game game) { … }
}
@Api(name = "tictactoe", version = "v1")
class TicTacToe extends TicTacToeBase {
// setGame behaves as if annotated with httpMethod = "GET" and clientIds = { "c1"}.
@ApiMethod(httpMethod = "GET")
@Override
public Game setGame(Game game) { … }
}
方法没有 @ApiReference
注释或等效注释,因此 @ApiMethod
始终通过 Java 继承方式继承,而不是通过 @ApiReference
继承。
继承规则和优先规则
下表总结介绍了继承规则和优先顺序。
注释/继承 | 规则 |
---|---|
@Api |
所有类必须完全相同。 |
@ApiClass |
为要用于替换 @Api 属性的类指定。 |
Java 继承 | 类继承基类的 @Api 和 @ApiClass 。 |
@ApiReference |
类继承引用的类的 @Api 和 @ApiClass 。 |
在从基类继承的类 (Java) 中使用 @ApiReference |
类继承引用的类的 @Api 和 @ApiClass ,而不是从基类继承。 |
注释继承的常见使用场景
以下是继承的典型使用场景示例:
用于 API 版本控制:
@Api(name = "tictactoe", version = "v1")
class TicTacToeV1 { … }
@Api(version = "v2")
class TicTacToeV2 extends TicTacToeV1 { … }
用于多类 API:
@Api(name = "tictactoe", version = "v1")
class TicTacToeBase {}
@ApiClass(resource = "boards")
class TicTacToeBoards extends TicTacToeBase { … }
@ApiClass(resource = "scores")
class TicTacToeScores extends TicTacToeBase { … }
用于测试同一 API 的不同版本:
@Api(name = "tictactoe", version = "v1")
class TicTacToe {
protected Foo someMethod() {
// Do something real;
}
public Foo getFoo() { … }
}
@Api(version="v1test")
class TicTacToeTest extends TicTacToe {
protected Foo someMethod() {
// Stub out real action;
}
}
其中 someMethod
可能会返回预先确定的响应,避免执行带有副作用的调用、跳过网络或数据存储区请求,等等。
将类添加到 web.xml
注释类后,必须将它们添加到 web.xml
文件中。以下示例显示了单个类:
要添加多个类,请执行以下操作:
将
<param-value>com.example.skeleton.MyApi</param-value>
替换为您自己的 API 类名称。将各个类添加到同一
<param-value>
字段中,用英文逗号分隔,例如:<param-value>com.example-company.example-api.Hello,com.example-company.example-api.Goodbye</param-value>