3時間弱あってなかなか見れなかったけどようやく見た。おもしろかった。
しばらく緊張させたあと解決して歌と踊り、みたいな流れが何度かある。ひとつのことを無駄にひっぱったりしないので、ずっと緊張させられることはない。割とショッキングなシーンもあるけど、最終的にうまくいってすっきりする。
序盤から細かい伏線がちりばめられていて、それらが終盤に向けて次々と解決していく感じが気持ちよい。最近あんまりこういう伏線回収が気持ちいいストーリー見てなかった。
とにかく良かった。
3時間弱あってなかなか見れなかったけどようやく見た。おもしろかった。
しばらく緊張させたあと解決して歌と踊り、みたいな流れが何度かある。ひとつのことを無駄にひっぱったりしないので、ずっと緊張させられることはない。割とショッキングなシーンもあるけど、最終的にうまくいってすっきりする。
序盤から細かい伏線がちりばめられていて、それらが終盤に向けて次々と解決していく感じが気持ちよい。最近あんまりこういう伏線回収が気持ちいいストーリー見てなかった。
とにかく良かった。
タイムスリップ異世界転生で、設定自体はものすごくありふれてるけど、扱っているのが際どい。
基本的にはコメディなのか?と思って前半笑って見ていると、徐々にドキュメンタリが入ってくる。実際のドイツ国民へのインタビューや極右団体との絡みなんかが入ってくる。映画の中のヒトラーは、ヒトラーに似た人間が演じているものだと思われている。この設定がそのまま現実に通用するため、ドキュメンタリとフィクションが混ざっても全く違和感がなく、目線が入っていない部分はフィクションなのかどうなのかよくわからない。
前半は異世界転生ものとして面白いのに、うまいグラデーションで見終わるころには怖くなっている。現実の合法的な議席獲得の最初の段階もこうだったのだろうか?と思わせる。
大井町の夏劇場で見てきた。妻がチケットをとらなかったら一生見なかったであろう (ミュージカルに興味がないので……)。
箇条書き
@RequestScope、これは @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) と同じ意味。この Bean はスレッドローカルにひもづけられているHttpServletRequestに保持されている。
@Async は別スレッドで処理を実行するため、間接的にでも @RequestScope な Bean を使っていると以下のような実行時エラーとなる。処理中のスレッドには該当する Bean が保持されていないという旨。
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
ScopedProxyMode.TARGET_CLASS なのでスタックトレースがややこしい。cglib を使ってクラスのプロキシを作って、プロキシクラス内で Bean をどこかからとってきて呼ぶという動きになっている。おかげでスコープに関わらずコード上は Singleton のように扱えているが、黒魔術はひっかかったとき大変です。
https://stackoverflow.com/questions/23732089/how-to-enable-request-scope-in-async-task-executor に良い解決方法が書いてあって、これを使えばよい。やってることは単純で @Async で別スレッドで実行しようとする際に、呼び出し元スレッドのBeanを渡してあげるという感じ。
ただし、どうもコピペでは動かなくて、以下のようにかえてうまくいった。結局 Executor インターフェイスのメソッドだけオーバーライドしたら良い。
@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
// Default task executor for @Async annotation
@Override
@Bean
public Executor getAsyncExecutor() {
val executor = new ContextAwarePoolExecutor();
executor.setCorePoolSize(25);
executor.setQueueCapacity(25);
executor.setMaxPoolSize(25);
executor.setThreadNamePrefix("AsyncTask-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
@SuppressWarnings("serial")
public static class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {
/**
* Override Executor interface
*/
@Override
public void execute(Runnable task) {
super.execute(new ContextAwareRunnable(task, RequestContextHolder.currentRequestAttributes()));
}
}
public static class ContextAwareRunnable implements Runnable {
private final Runnable task;
private final RequestAttributes context;
public ContextAwareRunnable(Runnable task, RequestAttributes context) {
this.task = task;
this.context = context;
}
@Override
public void run() {
if (context != null) {
RequestContextHolder.setRequestAttributes(context);
}
try {
task.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}