提升自己
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 以及越来越多的其他技术)将大部分样板代码简化为常见情况下的简单接口定义。所以,你真的需要这一切吗?很可能你需要,而且 - 一如既往 - 选择权在你。这就是为什么这个版本如此出色!更强的能力,更多的选择。
实际上,内容非常多。有大量新特性。我甚至无法在这里一一涵盖。所以我不会尝试。请查阅发布说明以获取完整详情!