提升自己
VMware 提供培訓和認證,加速你的發展。
瞭解更多在這篇部落格中,我想探討並展示一下 Spring Boot 1.2 中的一些 *眾多* 新特性,這些特性讓那些來自或正在基於 Java EE 構建的開發者生活更輕鬆。
值得一提的是,當然,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.properties
檔案和 Maven 構建,都可以線上獲取。
該示例展示了 Boot 新的 JAX-RS 自動配置(此處使用 Jersey 2.x),體現在 GreetingEndpoint
中。注意讓這一切工作起來是多麼方便!唯一需要留意的是,你需要指定一個 ResourceConfig
的子類來告訴 Jersey 註冊哪些元件。
它展示瞭如何使用新的自動配置的 JTA 支援來實現全域性事務。JTA 是一個用於 X/Open XA 協議的 Java API,它允許多個相容的事務資源(如訊息佇列和資料庫)參與到同一個事務中。為此,我們使用了 Atomikos 獨立的 JTA 提供者。使用 Bitronix 也同樣容易;如果你引入了相應的 Starter,兩者都會被自動配置。在此示例中,在 GreetingService
中,JMS 和 JPA 工作作為全域性事務的一部分執行。我們透過建立 3 個事務並模擬對第三個事務進行回滾來演示這一點。你應該在控制檯看到,從 JDBC javax.sql.DataSource
資料來源返回了兩條記錄,並從嵌入式 JMS javax.jms.Destination
目標接收到了兩條記錄。
此示例還使用了 Wildfly(來自 RedHat)應用伺服器的出色的 Undertow 嵌入式 HTTP 伺服器,而不是(預設的)Apache Tomcat。使用 Undertow 和使用 Jetty 或 Tomcat 一樣簡單 - 只需排除 org.springframework.boot:spring-boot-starter-tomcat
並新增 org.springframework.boot:spring-boot-starter-undertow
即可!這項貢獻源自一個第三方 PR - 感謝 Ivan Sopov!太棒了。
為了保持一致性,此示例還使用了 JSR 330。JSR 330 描述了一組註解,你可以在 WebLogic 等專有應用伺服器中使用,也可以在 Google Guice 或 Spring 等依賴注入容器中以可移植的方式使用。我還使用了一個 JSR 250 註解(定義為 Java EE 5 的一部分)來演示生命週期鉤子。
此示例依賴於 Spring Boot 自動配置並嵌入的、記憶體中的 H2 javax.sql.DataSource
和 - Spring Boot 自動配置並嵌入的、記憶體中的 HornetQ javax.jms.ConnectionFactory
。如果你想連線到傳統的、非嵌入式例項,可以直接在 application.yml
或 application.properties
中指定屬性,例如 spring.hornetq.host
,或者直接定義相應型別的 @Bean
。
此示例還使用了新的 @SpringBootApplication
註解,它結合了 @Configuration
、@EnableAutoConfiguration
和 @ComponentScan
。很棒!
雖然這個示例使用了許多相當熟悉的 Java EE API,但這仍然是典型的 Spring Boot 應用,因此預設情況下你可以使用 java -jar ee.jar
執行此應用,或者輕鬆將其部署到以程序為中心的平臺即服務 (PaaS) 產品上,例如 Heroku 或 Cloud Foundry。如果你想將其部署到獨立的應用程式伺服器(如 Apache Tomcat、Websphere 或介於兩者之間的任何伺服器),可以很直接地將構建轉換為 .war
檔案,並相應地部署到任何 Servlet 3 容器中。
如果你將應用部署到更傳統的應用伺服器,Spring Boot 可以利用應用伺服器提供的功能。例如,使用 JNDI 繫結的JMS ConnectionFactory
、JDBC DataSource
或JTA UserTransaction
非常簡單。
我個人會對許多這些 API 提出疑問。你真的需要分散式、多資源事務嗎?在當今的分散式世界中,考慮全域性事務管理器是一種架構異味。當 Spring 提供了更豐富、整合的基於 Spring MVC 的技術棧,包含了 MVC、REST、HATEOAS、OAuth 和 websockets 支援時,你真的需要使用 JAX-RS 嗎?JPA 是一個很好的 API,用於與基於 SQL 的 javax.sql.DataSource
進行互動,但 Spring Data Repository(當然包含對 JPA 的支援,但也支援 Cassandra、MongoDB、Redis、CouchBase 以及越來越多的其他技術)將大部分樣板程式碼簡化為常見情況下的簡單介面定義。所以,你真的需要這一切嗎?很可能你需要,而且 - 一如既往 - 選擇權在你。這就是為什麼這個版本如此出色!更強的能力,更多的選擇。
實際上,內容非常多。有大量新特性。我甚至無法在這裡一一涵蓋。所以我不會嘗試。請查閱釋出說明以獲取完整詳情!