IK.AM

@making's tech note


【翻訳 Spring Boot 1.2における"Bootiful" なJava EEサポート

🗃 {Programming/Java/org/springframework/boot}
🏷 Java 🏷 Java EE 7 🏷 Spring Boot 
🗓 Updated at 2014-11-26T07:47:48Z  🗓 Created at 2014-11-26T07:47:48Z   🌎 English Page

PivotalのDeveloper AdvocateのJosh Long(@starbuxman)がSpring Boot 1.2とJava EEに関する面白い記事を書いていたので、本人の許可をとって翻訳してみました。わりとナチュラルに訳したつもりです。

元記事: "Bootiful" Java EE Support in Spring Boot 1.2


この記事では、Java EEを使っている人またはSpring Bootに移ろうとしている人の開発をより容易にするSpring Boot 1.2の_多くの_新機能を見ていきたいと思います。そしてデモしたいと思います。

これらのサポートはこれまでのSpringでも当然利用できるのですが、今回のSpring Boot 1.2を使用することでとびきり簡単になるということを言っておきたいです。

まず始めにサンプルプログラムをお見せします。説明はそれからです。


package demo;

import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.core.JmsTemplate;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSException;
import javax.persistence.*;
import javax.transaction.Transactional;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.io.Serializable;
import java.util.Collection;
import java.util.logging.Logger;

@SpringBootApplication
public class Application {

    @Named
    public static class JerseyConfig extends ResourceConfig {

        public JerseyConfig() {
            this.register(GreetingEndpoint.class);
            this.register(JacksonFeature.class);
        }
    }

    @Named
    @Transactional
    public static class GreetingService {

        @Inject
        private JmsTemplate jmsTemplate;

        @PersistenceContext
        private EntityManager entityManager;

        public void createGreeting(String name, boolean fail) {
            Greeting greeting = new Greeting(name);
            this.entityManager.persist(greeting);
            this.jmsTemplate.convertAndSend("greetings", greeting);
            if (fail) {
                throw new RuntimeException("simulated error");
            }
        }

        public void createGreeting(String name) {
            this.createGreeting(name, false);
        }

        public Collection<Greeting> findAll() {
            return this.entityManager
                    .createQuery("select g from " + Greeting.class.getName() + " g", Greeting.class)
                    .getResultList();
        }

        public Greeting find(Long id) {
            return this.entityManager.find(Greeting.class, id);
        }
    }

    @Named
    @Path("/hello")
    @Produces({MediaType.APPLICATION_JSON})
    public static class GreetingEndpoint {

        @Inject
        private GreetingService greetingService;

        @POST
        public void post(@QueryParam("name") String name) {
            this.greetingService.createGreeting(name);
        }

        @GET
        @Path("/{id}")
        public Greeting get(@PathParam("id") Long id) {
            return this.greetingService.find(id);
        }
    }

    @Entity
    public static class Greeting implements Serializable {

        @Id
        @GeneratedValue
        private Long id;

        @Override
        public String toString() {
            return "Greeting{" +
                    "id=" + id +
                    ", message='" + message + '\'' +
                    '}';
        }

        private String message;

        public String getMessage() {
            return message;
        }

        public Greeting(String name) {
            this.message = "Hi, " + name + "!";
        }

        Greeting() {
        }
    }

    @Named
    public static class GreetingServiceClient {

        @Inject
        private GreetingService greetingService;

        @PostConstruct
        public void afterPropertiesSet() throws Exception {
            greetingService.createGreeting("Phil");
            greetingService.createGreeting("Dave");
            try {
                greetingService.createGreeting("Josh", true);
            } catch (RuntimeException re) {
                Logger.getLogger(Application.class.getName()).info("caught exception...");
            }
            greetingService.findAll().forEach(System.out::println);
        }
    }

    @Named
    public static class GreetingMessageProcessor {

        @JmsListener(destination = "greetings")
        public void processGreeting(Greeting greeting) throws JMSException {
            System.out.println("received message: " + greeting);
        }
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

ソースコードは以上です。あとはちょっとしたapplication.propertiesMavenのビルドファイルがあるだけで、 これらはオンラインで参照可能です。

JerseyとJAX-RS

この例ではGreetingEndpointBootの新機能であるJAX-RS自動設定をデモしています (この場合、Jersey 2.xを使っています) 。 めっちゃ簡単に動かせている点に注目していただきたいです!唯一面倒くさいのはResourceConfigのサブクラスを作成して、Jerseyに登録するコンポーネントを知らせないといけない点ですね。

JTAによるグローバルトランザクション

新たにサポートされたJTA自動設定によるグローバルトランザクションもデモしています。JTAはX/Open XAプロトコルのためのJava APIで、(このプロトコルに :訳者注)準拠した複数のトランザクションリソース(メッセージキューやデータベース)を単一のトランザクションに参加させます。これを行うために、我々はスタンドアローンなJTAプロバイダーであるAtomikosを使用しました。同様に、Bitronixも簡単に利用できます。両方とも適切なstarterを持ってくれば自動設定されるのです。この例では、GreetingServiceの中でJMSとJPAの処理がグローバルトランザクションの一部として機能しています。3つのトランザクションを生成し、3つめの処理をわざとロールバックさせています。これにより、2つのレコードがのJDBCのjavax.sql.DataSourceから取得され、2つのレコードが組み込みJMSのjavax.jms.Destinationに受信されていることがコンソールの出力から分かるでしょう。

組み込みWebサーバーUndertow

この例ではApache Tomcat(デフォルト)の代わりにWildFly (RedHat製) アプリケーションサーバーが持つ素晴らしいUndertow 組み込みHTTPサーバーを使用しています。Undertowを使うのは簡単でTomcatの代わりにJettyを使うのと同じように、(依存関係から :訳者注)org.springframework.boot:spring-boot-starter-tomcatを除き、org.springframework.boot:spring-boot-starter-undertowを追加するだけです!この対応はサードパーティのプルリクエストが元となっています。- Ivan Sopovサンクス! GJ!

諸々

(Java標準で :訳者注)統一感を出すために、この例ではJSR 330も使用しています。JSR 330では、WebLogicのようなプロプライエタリなアプリケーションサーバーでも、Google GuiceやSpringのようなDIコンテナでも同じように使えるようにするためのアノテーション群が決められています。またJSR 250のアノテーション(Java EE 5の一部として定義されました)も使用しています。これは(Beanの :訳者注)ライフサイクルにフックするデモを見せるためです。

この例では、Spring Bootで自動設定された組み込みインメモリH2javax.sql.DataSourceとa Spring Bootで自動設定された組み込みインメモリHornetQjavax.jms.ConnectionFactoryに依存しています. 組み込みでないインスタンスに接続したければ、好きなものを直接Bean定義すればよいです。

また、この例では新しい@SpringBootApplicationアノテーションを使っています。これは@Configuration@EnableAutoConfiguration@ComponentScanを一つにしたものです。ナイス!

デプロイ

この例ではJava EEの有名どころなAPIを沢山使っていますが、結局のところただのSpring Bootアプリケーションなのです。つまりデフォルトで、java -jar ee.jarでアプリケーションを実行できますし、HerokuやCloud Foundryといったプロセス中心のplatforms-as-a-serviceに簡単にデプロイできます。 もしスタンドアローンなアプリケーションサーバー(Apache TomcaやWebSphereなど)にデプロイしたいのであれば、単純に.warに変換し、Servlet 3対応のコンテナにデプロイすれば良いです。

もしより古典的なアプリケーションサーバーにデプロイするなら、代わりにSpring Bootはサーバーの機能をうまく利用できます。例えば、極めてシンプルに、JNDIからJMS ConnectionFactory,やJDBC DataSourceJTA UserTransactionを取得できます。

Spring Boot 1.2: 選択肢 パワー

個人的には、これらの沢山のAPIには疑問をもっています。本当に、分散マルチリソーストランザクションが必要でしょうか?今日の分散化さされた世界においてグローバルトランザクションマネージャーを考えるのはアーキテクチャ的に臭いますね(リンク先の日本語訳 :訳者注)。SpringにはMVC、REST、HATEOS、OAuth、WebSocketがサポートされた、よりリッチで統合されたSpring MVCベースのスタックがあるのに、本当にJAX-RSを使いますか?JPAはSQLベースのjavax.sql.DataSourceとやり取りするためのナイスなAPIであるけれど、Spring Dataのリポジトリ(JPAサポートはもちろん、Cassandra、MongoDB、Redis、CouchBaseなどなど沢山の代替テクノロジー_も_サポートしている)は多くのボイラーポレートをなくしてくれるし、一般的なケースで利用可能なシンプルなインタフェースを提供しています。だから、本当にこれらの全てが必要なんですか?これはあなたがよくやってるであろうし、いつも通りのように、「選択はあなた次第」なのです。これがこのリリースがクールな理由です。よりパワーを、より選択肢を。

他には?

ぶっちゃけまだまだあります。 新機能がたくさんあります。全てのカバーをし始めるのもできなかったので、やりません。全てのスクープはリリースノートを確認してください!

Spring Boot 1.2はもうすぐGAになります。ちょっと試すのにちょうど良いタイミングです。issueをあげて質問してください!


という感じでJoshの記事を紹介してみました。Spring Boot 1.2面白いです。Java EEな方も是非お試しください。

Spring Boot 1.1の内容ですが、「はじめてのSpring Boot」もよろしく・・・


✒️️ Edit  ⏰ History  🗑 Delete