英文原文地址
https://playframework.com/documentation/2.6.x/JavaActionsComposition
Action composition
action是一个返回play.mvc.Result
对象的方法。实际上play在内部将actions当作functions管理(Actually, Play manages internally actions as functions),在Java API中action为一个play.mvc.action
实例。Play创建了一个root action保证我们能够调到合适的action方法,这也允许我们进行action的组合。
Composing actions
public class VerboseAction extends play.mvc.Action.Simple {
public CompletionStage<Result> call(Http.Context ctx) {
Logger.info("Calling action for {}", ctx);
return delegate.call(ctx);
}
}
可以通过@With
注解将该action与其他acion组合起来
//controller中的方法都为一个action,controller可以视为action的集合
@With(VerboseAction.class)
public Result verboseIndex() {
return ok("It works!");
}
如果需要代理到被封装的action,可以使用delegate.call(...)
(At one point you need to delegate to the wrapped action using delegate.call(...)
)
也可以通过自定义的注解来混合多个action
@Security.Authenticated
@Cached(key = "index.result")
public Result authenticatedCachedIndex() {
return ok("It works!");
}
注意:
- 每个请求都要由不同的play.mvc.Action
实例来处理。如果使用单例模式在复杂情况下可能会出错。如果使用Spring作为DI容器,要将action设置为prototype
- play.mvc.Security.Authenticated
和 play.cache.Cached
都是Play预定义的注解和Action类
在controller上进行注解
@Security.Authenticated
public class Admin extends Controller {
...
}
这中情况下contorller中所有的action方法会和Security.Authenticated混合
将action中的对象传递给controller
public class PassArgAction extends play.mvc.Action.Simple {
public CompletionStage<Result> call(Http.Context ctx) {
ctx.args.put("user", User.findById(1234));
return delegate.call(ctx);
}
}
@With(PassArgAction.class)
public static Result passArgIndex() {
Object user = ctx().args.get("user");
return ok(Json.toJson(user));
}
查看action调用顺序
在logback.xml中加入以下信息
<logger name="play.mvc.Action" level="DEBUG" />
可以在日志信息中看到以下结果
[debug] play.mvc.Action - ### Start of action order
[debug] play.mvc.Action - 1. ...
[debug] play.mvc.Action - 2. ...
[debug] play.mvc.Action - ### End of action order
使用依赖注入
运行时
通过一个缓存的例子来说明,首先定义一个注解
@With(MyOwnCachedAction.class)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface WithCache {
String key();
}
然后通过依赖注入的方式定义action
public class MyOwnCachedAction extends Action<WithCache> {
private final AsyncCacheApi cacheApi;
@Inject
public MyOwnCachedAction(AsyncCacheApi cacheApi) {
this.cacheApi = cacheApi;
}
@Override
public CompletionStage<Result> call(Http.Context ctx) {
return cacheApi.getOrElseUpdate(configuration.key(), () -> delegate.call(ctx));
}
}
编译期依赖注入
需要覆盖BuiltInComponents
的javaHandlerComponents
方法,将自己的action添加到JavaHandlerComponents中
public class MyComponents extends BuiltInComponentsFromContext
implements NoHttpFiltersComponents, EhCacheComponents {
public MyComponents(ApplicationLoader.Context context) {
super(context);
}
@Override
public Router router() {
return Router.empty();
}
@Override
public MappedJavaHandlerComponents javaHandlerComponents() {
return super.javaHandlerComponents()
// Add action that does not depends on any other component
.addAction(VerboseAction.class, VerboseAction::new)
// Add action that depends on the cache api
.addAction(MyOwnCachedAction.class, () -> new MyOwnCachedAction(defaultCacheApi()));
}
}