SpongeプラグインでSpringのDIコンテナを使う
MinecraftのSpongeプラグインでSpringを使ってみたのでその時のメモです。
Springを使うメリット ・テストがやりやすくなる ・AOPが使える←個人的にかなり重要
なぜAOPを使いたいか 継承では解決できない同じような処理がサーバーのプラグインでは結構出てくる(ロギングとか権限周り) その辺をAOPで解決したい
プラグインのエントリポイント
package net.orekyuu.spring; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.game.state.GameConstructionEvent; import org.spongepowered.api.plugin.Plugin; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @Plugin( id = "net.orekyuu.spring", name = "SpringDemo", version = "1.0-SNAPSHOT" ) public class SpringDemo { private ApplicationContext context; private static SpringDemo INSTANCE; @Listener public void onServerStart(GameConstructionEvent event) { INSTANCE = this; //net.orekyuu.spring以下をComponentScanしてる context = new AnnotationConfigApplicationContext("net.orekyuu.spring"); } public static SpringDemo getInstance() { return INSTANCE; } }
ゲーム起動時にApplicationContextを作るだけです。 プラグイン以下パッケージをComponentScanするようにしておくと、アノテーションベースの設定ができるようになります。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.orekyuu</groupId> <artifactId>spring</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>Spring</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <profiles> <profile> <id>release</id> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> <version>2.4</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar-no-fork</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> <version>2.10.3</version> <executions> <execution> <id>attach-javadocs</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> <version>1.6</version> <executions> <execution> <id>sign-artifacts</id> <phase>verify</phase> <goals> <goal>sign</goal> </goals> </execution> </executions> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build> </profile> </profiles> <build> <resources> <resource> <directory>\${project.basedir}/src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>templating-maven-plugin</artifactId> <version>1.0-alpha-3</version> <executions> <execution> <id>filter-src</id> <goals> <goal>filter-sources</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.3</version> <dependencies> <dependency> <groupId>net.trajano.wagon</groupId> <artifactId>wagon-git</artifactId> <version>2.0.3</version> </dependency> <dependency> <groupId>org.apache.maven.doxia</groupId> <artifactId>doxia-module-markdown</artifactId> <version>1.6</version> </dependency> </dependencies> </plugin> <plugin> <artifactId>maven-release-plugin</artifactId> <version>2.5.1</version> <configuration> <autoVersionSubmodules>true</autoVersionSubmodules> <tagNameFormat>@{project.version}</tagNameFormat> <scmCommentPrefix xml:space="preserve">[RELEASE] </scmCommentPrefix> <goals>install deploy site-deploy </goals> <!-- install is here to fix javadoc generation in multi-module projects --> <releaseProfiles>release</releaseProfiles> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <artifactSet> <excludes> <exclude>junit:junit</exclude> </excludes> </artifactSet> </configuration> </execution> </executions> <configuration> <relocations> <relocation> <pattern>org.apache</pattern> <!--org.apacheパッケージのクラスをforgeが読み込んでくれないっぽいので回避--> <shadedPattern>org.aapache</shadedPattern> </relocation> </relocations> </configuration> </plugin> </plugins> </build> <repositories> <repository> <id>spongepowered-repo</id> <url>http://repo.spongepowered.org/maven/</url> </repository> </repositories> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.spongepowered</groupId> <artifactId>spongeapi</artifactId> <version>4.1.0-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.2.RELEASE</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.0.13</version> </dependency> <!--AOP--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>RELEASE</version> </dependency> </dependencies> <reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> <version>2.10.3</version> </plugin> </plugins> </reporting> </project>
これがpom.xmlです。 注意としてはそのままflat-jarにしてしまうとClassNotFoundExceptionが発生してしまいます。 原因はMinecraftのLaunchClassLoaderにあります。このClassLoaderでModが読み込まれるのですが、org.apache.logging以下パッケージが無視されています。正確にはLaunchClassLoader.class.getClassLoader()でロードされるのですが、LaunchClassLoaderとは親子関係がなくて見つからないみたいな流れ?
これ当たりっぽそう? pic.twitter.com/n7YiahyZ0Y
— 俺九番 (@orekyuu) 2016年9月9日
そのため解決策としてmaven-shade-pluginを使ってorg.apacheパッケージをリネームすることで回避しました。
イベントをSpringで Spongeで発生するイベントをSpringのイベントに置き換えてみます。
@Component public class EventConverter { @Autowired private ApplicationEventPublisher publisher; @Listener public void onSpongeEvent(Event event) { //SpongeのイベントをSpringに流す publisher.publishEvent(new SpongeEvent<>(SpringDemo.getInstance(), event)); } }
このComponentはSpongeのイベントをすべて受け取り、Springのイベントへ変換してイベントを通知します。
public class SpongeEvent<T extends Event> extends ApplicationEvent { private final T event; public SpongeEvent(Object source, T event) { super(source); //とりあえず適当に渡しておく Objects.requireNonNull(event); this.event = event; } public T getEvent() { return event; } }
Spongeのイベントを持つだけのラッパークラスを作りました。ここは特に解説は必要ないと思います。
次に受け取り側です。
//プレイヤーがサーバーに入った時のイベントを受け取ってなにかする @Component public class PlayerJoinEventListener { @Autowired public PlayerMessageService playerMessageService; @EventListener public void handleOnPlayerJoin(SpongeEvent<ClientConnectionEvent.Join> event) { playerMessageService.sendHelloMessage(event.getEvent().getTargetEntity()); } }
Playerが入ってきた時のイベントを取ってみました。 受け取りたいイベントを引数にとって、EventListenerアノテーションをつけるだけです。
Spongeのイベント以外にも独自のイベントを作りたい場合はApplicationEventを継承したクラスを作り、ApplicationEventPublisherへ流すだけです。 詳しく知りたい方はこちらを見るのが良いかと思います。 https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2
最後に設定です。
@Configuration public class EventConfig { @Autowired private EventConverter eventConverter; //EventConverterをEventManagerに登録する @Bean public EventManager eventManager() { EventManager manager = Sponge.getEventManager(); manager.registerListeners(SpringDemo.getInstance(), eventConverter); return manager; } }
SpongeのEventManagerにEventConverterを登録しているだけです。
SpringAOPでロギング AOPの定番ネタですがロギングです。
まずは設定から
@Configuration @EnableAspectJAutoProxy public class AopConfig { }
はい。EnableAspectJAutoProxyしてるだけです。
次にロギングのインターセプタを作ります。
@Component @Aspect public class LogInterceptor { private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class); @AfterReturning("execution(public * net.orekyuu.spring.service..*.*(..))") public void printLog(JoinPoint joinPoint) { logger.info("ServiceLog: " + joinPoint.getSignature()); } }
net.orekyuu.spring.serviceパッケージ以下にある全てのpublicなメソッドのどれかが正常終了した時にログを出力しています。 ポイントカット式とかの書き方は以下を見ればよいかと。 http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
やりたい設計とか Controller→Service→Repositoryの構造になんか近いような・・・? EventListener→Service→Repositoryって結構よさ気では!?
テストとかDBに関してはまた後日
痛IntelliJを作ろう
IntelliJを痛くしろという神の啓示を受けたので、痛くしてみました。
今回は上のようなエディタを作ってみます。
今まではSexy Editorというプラグインを使う方法がありましたが、2016.2からは"Set Background Image"というアクションが追加され、そこから設定画面を開いて背景画像を設定できるようになりました。
このアクションは上のメニューバーには見つからなかったので、Find Action(WinならCtrl+Shift+A)からSet Background Imageというアクションを検索して呼び出します。
明るい写真だと文字が見づらいので透明度を下げて調整します。 これで設定は完了です。
それでは良い痛IntelliJライフを!
JJUG CCC 2016 springに行ってきた
Type safe Annotation #ccc_gh1
桜庭さんのタイプアノテーションの話からスタート JSR-305はずっと通っているものだと思っていたんだけど、ステータスが休止中になっているのをその時知って1年ぶりの衝撃を受けました。 そこで気づいて急いで発表予定のスライドを修正するなんてことになったり。 あとChecker Frameworkなんてものがあるの知らなかったので使ってみたいと思います。
Thymeleaf3を使ってみよう! #ccc_f2
Spring祭りその1。 去年からSpringBootでThymeleafを使っていて、次作るWebアプリでも使う予定なので聞きに行きました。 今までWebデザイナの人にhtml書いてもらうとThymeleafで「brタグが閉じられてない」みたいなエラーが出ていたので、それが次のバージョンからなくなるって話がすごい嬉しかったです。 逆に言えばそれ以外はめぼしい機能はないのかな?という印象。
SpringBootでBootした後に作るWebアプリケーション基盤 #ccc_e3
Spring祭りその2。 SpringBootを入門読んでHelloWorldした後に読むべき資料!という感じでうまくまとまっていて良いセッションでした。 学校の人に見せたい資料です!
テストゼロからイチへ進むための戦略と戦術 #ccc_e4
テストゼロの状態からテストを書くまでの話。 テストほぼ書いていない人間だったので結構耳が痛くなる話でした。テスト書かねば・・・! コードのゴミ掃除で1コミット1.7万行削除には笑いましたw
Spring Framework/Bootの最新情報とPivotalがすすめるクラウドネイティブなアプローチ #ccc_gh5
Spring祭りその3。 バナーがついに画像に対応! 絶対にアピールする点が間違っている気がwww SpringBoot1.4から@GetMapping@PostMappingのようなアノテーションが増えてちょっとだけ楽になりました。いいぞ〜!
OpenJDK コミュニティに参加してみよう #ccc_i62
OpenJDKのコミュニティに参加する方法の解説。 プロジェクトが大量にあったり、どうやり取りすればいいんや!みたいな一歩目の敷居が高そうに感じるところをうまく解説されてました。 このセッション終わったあとnokoに「明日にはコミッタになってるんだよね?ん?ん?」みたいな不当な煽りを受けました。
古のJavaを使うということ #ccc_m71
J2SE 1.4の環境で働いている方のセッション。 テストコードを書こうと思っても上司が納得しなかったり、不要なコメントを削除するのにも苦労するという話を聞いて辛さが溢れてました。 環境が改善されると良いですね・・・。
SpringBoot+Kotlin 劇的ビフォーアフター #ccc_i72
僕のセッションです。 緊張で結構カミカミになりましたけど。大体良い時間で話しきれて良かったです。 cccの当日にイケてないところの解決策が出てきたりして資料修正したりしてましたが、解決策を知れるJJUG CCC最高!というかんじでした。