抢先一步
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;如果您使用相应的启动器,这两个程序都会自动配置。在本例中,在 GreetingService
中,JMS 和 JPA 工作作为全局事务的一部分完成。我们通过创建 3 个事务并在第三个事务上模拟回滚来演示这一点。您应该会看到控制台打印出 JDBC javax.sql.DataSource
数据源返回的两条记录和从嵌入式 JMS javax.jms.Destination
目标接收到的两条记录。
此示例还使用来自 RedHat 的 Wildfly 应用服务器的出色 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
运行此应用程序,或者轻松地将其部署到以流程为中心的 平台即服务 产品,例如 Heroku 或 Cloud Foundry。如果您想将其部署到独立的应用服务器(例如 Apache Tomcat、Websphere 或介于两者之间的任何服务器),则可以轻松地将构建转换为 .war
并相应地将其部署到任何 Servlet 3 容器。
如果您将应用程序部署到更经典的应用程序服务器,Spring Boot 可以利用 AS 的功能。例如,使用 JNDI 绑定 JMS ConnectionFactory
、JDBC DataSource
或 JTA UserTransaction
非常简单。
我个人会质疑很多这些 API。你真的需要分布式、多资源事务吗?在当今 分布式世界中,全局事务管理器被认为是一种架构上的气味。当 Spring 提供更丰富、集成的基于 Spring MVC 的堆栈(包括 MVC、REST、HATEOAS、OAuth 和 Websocket 支持)时,你真的需要使用 JAX-RS 吗?JPA 是一个与基于 SQL 的 javax.sql.DataSource
交互的不错的 API,但是 Spring Data 存储库(当然包括对 JPA 的支持,但也支持 Cassandra、MongoDB、Redis、CouchBase 和越来越多的替代技术)将许多样板代码简化为常见情况下的简单接口定义。那么,你真的需要所有这些吗?你很可能需要,并且——一如既往——选择权在你手中。这就是这个版本如此酷的原因!更强大的功能,更多的选择。
实际上,很多。有很多新特性。我甚至无法在这里全部介绍它们。因此我不会尝试。查看 发行说明 以了解全部内容!