原文:https://dzone.com/articles/why-springboot
做者:Siva Prasad Reddy Katamreddy
译者:Oopsguyhtml
本文将介绍各类 Spring 的配置方式,帮助你了解配置 Spring 应用的复杂性。前端
Spring 是一个很是受欢迎的 Java 框架,它用于构建 Web 和企业应用。不像许多其余框架只关注一个领域,Spring 框架提供了各类功能,经过项目组合来知足当代业务需求。java
Spring 框架提供了多种灵活的方式来配置 Bean。例如 XML、注解和 Java 配置。随着功能数量的增长,复杂性也随之增长,配置 Spring 应用将变得乏味且容易出错。mysql
针对上述问题,Spring 团队建立了 Spring Boot 以解决配置复杂的问题。web
但在开始将 Spring Boot 以前,咱们将快速浏览一下 Spring 框架,看看 Spring Boot 正在决解什么样的问题。spring
在本文中,咱们将介绍:sql
若是你是一名 Java 开发人员,那你极可能据说过 Spring 框架,甚至可能已经在本身的项目中使用了它。Spring 框架主要是做为依赖注入容器,但它的做用远不止这些。数据库
连同 Spring 一块儿的,还有许多其余的 Spring 姊妹项目,能够帮助构建知足当代业务需求的应用:apache
还有许多其余有趣的项目涉及各类其余当代应用开发需求。有关更多信息,请查看 http://spring.io/projects。编程
刚开始,Spring 框架只提供了基于 XML 的方式来配置 bean。后来,Spring 引入了基于 XML 的 DSL、注解和基于 Java 配置的方式来配置 bean。
让咱们快速了解一下这些配置风格的大概模样。
<bean id="userService" class="com.sivalabs.myapp.service.UserService"> <property name="userDao" ref="userDao"/> </bean> <bean id="userDao" class="com.sivalabs.myapp.dao.JdbcUserDao"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="secret"/> </bean>
@Service public class UserService { private UserDao userDao; @Autowired public UserService(UserDao dao){ this.userDao = dao; } ... ... }
@Repository public class JdbcUserDao { private DataSource dataSource; @Autowired public JdbcUserDao(DataSource dataSource){ this.dataSource = dataSource; } ... ... }
@Configuration public class AppConfig { @Bean public UserService userService(UserDao dao){ return new UserService(dao); } @Bean public UserDao userDao(DataSource dataSource){ return new JdbcUserDao(dataSource); } @Bean public DataSource dataSource(){ BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/test"); dataSource.setUsername("root"); dataSource.setPassword("secret"); return dataSource; } }
Spring 提供了多种方式来作一样的事,咱们甚至能够混合使用,在同一个应用中使用基于 Java 配置和注解配置的方式。
这很是灵活,但它有好有坏。刚开始接触 Spring 的新人可能会困惑应该使用哪种方式。到目前为止,Spring 团队建议使用基于 Java 配置的方式,由于它更具灵活性。
没有哪种方案是万能,咱们应该根据本身的需求来选择合适的方式。
到此,你已经了解了多种 Spring Bean 配置方式的基本形式。
让咱们快速地了解一下典型的 Spring MVC+JPA/Hibernate Web 应用的配置。
在了解 Spring Boot 是什么以及它提供了什么样的功能以前,咱们先来看一下典型的 Spring Web 应用配置是怎样的,哪些是痛点,而后咱们将讨论 Spring Boot 是如何解决这些问题的。
首先须要作的是配置 pom.xml
中所需的依赖:
<?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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sivalabs</groupId> <artifactId>springmvc-jpa-demo</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>springmvc-jpa-demo</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.9.2.RELEASE</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.13</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.190</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.3.11.Final</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> <version>2.1.4.RELEASE</version> </dependency> </dependencies> </project>
咱们配置了全部的 Maven jar 依赖,包括 Spring MVC、Spring Data JPA、JPA/Hibernate、Thymeleaf 和 Log4j。
@Configuration @EnableTransactionManagement @EnableJpaRepositories(basePackages="com.sivalabs.demo") @PropertySource(value = { "classpath:application.properties" }) public class AppConfig { @Autowired private Environment env; @Bean public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } @Value("${init-db:false}") private String initDatabase; @Bean public PlatformTransactionManager transactionManager() { EntityManagerFactory factory = entityManagerFactory().getObject(); return new JpaTransactionManager(factory); } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setGenerateDdl(Boolean.TRUE); vendorAdapter.setShowSql(Boolean.TRUE); factory.setDataSource(dataSource()); factory.setJpaVendorAdapter(vendorAdapter); factory.setPackagesToScan("com.sivalabs.demo"); Properties jpaProperties = new Properties(); jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); factory.setJpaProperties(jpaProperties); factory.afterPropertiesSet(); factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver()); return factory; } @Bean public HibernateExceptionTranslator hibernateExceptionTranslator() { return new HibernateExceptionTranslator(); } @Bean public DataSource dataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName")); dataSource.setUrl(env.getProperty("jdbc.url")); dataSource.setUsername(env.getProperty("jdbc.username")); dataSource.setPassword(env.getProperty("jdbc.password")); return dataSource; } @Bean public DataSourceInitializer dataSourceInitializer(DataSource dataSource) { DataSourceInitializer dataSourceInitializer = new DataSourceInitializer(); dataSourceInitializer.setDataSource(dataSource); ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); databasePopulator.addScript(new ClassPathResource("data.sql")); dataSourceInitializer.setDatabasePopulator(databasePopulator); dataSourceInitializer.setEnabled(Boolean.parseBoolean(initDatabase)); return dataSourceInitializer; } }
在 AppConfig.java 配置类中,咱们完成了如下操做:
@Configuration
注解标记为一个 Spring 配置类@EnableTransactionManagement
开启基于注解的事务管理@EnableJpaRepositories
指定去哪查找 Spring Data JPA 资源库(repository)@PropertySource
注解和 PropertySourcesPlaceholderConfigurer
Bean 定义配置 PropertyPlaceHolder
bean 从 application.properties
文件加载配置data.sql
脚原本初始化数据库咱们须要在 application.properties
中完善配置,以下所示:
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test jdbc.username=root jdbc.password=admin init-db=true hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.show_sql=true hibernate.hbm2ddl.auto=update
咱们能够建立一个简单的 SQL 脚本 data.sql
来将演示数据填充到 USER 表中:
delete from user; insert into user(id, name) values(1,'Siva'); insert into user(id, name) values(2,'Prasad'); insert into user(id, name) values(3,'Reddy');
咱们能够建立一个附带基本配置的 log4j.properties
文件,以下所示:
log4j.rootCategory=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %t %c{2}:%L - %m%n log4j.category.org.springframework=INFO log4j.category.com.sivalabs=DEBUG
咱们必须配置 Thymleaf 的 ViewResolver、处理静态资源的 ResourceHandler 和处理 I18n 的 MessageSource 等。
@Configuration @ComponentScan(basePackages = { "com.sivalabs.demo"}) @EnableWebMvc public class WebMvcConfig extends WebMvcConfigurerAdapter { @Bean public TemplateResolver templateResolver() { TemplateResolver templateResolver = new ServletContextTemplateResolver(); templateResolver.setPrefix("/WEB-INF/views/"); templateResolver.setSuffix(".html"); templateResolver.setTemplateMode("HTML5"); templateResolver.setCacheable(false); return templateResolver; } @Bean public SpringTemplateEngine templateEngine() { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver()); return templateEngine; } @Bean public ThymeleafViewResolver viewResolver() { ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver(); thymeleafViewResolver.setTemplateEngine(templateEngine()); thymeleafViewResolver.setCharacterEncoding("UTF-8"); return thymeleafViewResolver; } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Bean(name = "messageSource") public MessageSource configureMessageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename("classpath:messages"); messageSource.setCacheSeconds(5); messageSource.setDefaultEncoding("UTF-8"); return messageSource; } }
在 WebMvcConfig.java 配置类中,咱们完成了如下操做:
@Configuration
注解标记为一个 Spring 配置类@EnableWebMvc
注解启用基于注解的 Spring MVC 配置/resource/**
的静态资源请求定位到 /resource/
目录下如今咱们没有配置任何 I18n 内容,所以须要在 src/main/resources
文件夹下建立一个空的 messages.properties
文件。
在 Servlet 3.x 规范以前,咱们必须在 web.xml 中注册 Servlet/Filter。因为当前是 Servlet 3.x 环境,咱们可使用 ServletContainerInitializer 以编程的方式注册 Servlet/Filter。
Spring MVC 提供了一个惯例类 AbstractAnnotationConfigDispatcherServletInitializer 来注册 DispatcherServlet。
public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { AppConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebMvcConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } @Override protected Filter[] getServletFilters() { return new Filter[]{ new OpenEntityManagerInViewFilter() }; } }
在 SpringWebAppInitializer.java 配置类中,咱们完成了如下操做:
AppConfig.class
配置为 RootConfigurationClass,它将成为包含全部子上下文(DispatcherServlet)共享的 Bean 定义的父 ApplicationContextWebMvcConfig.class
配置为 ServletConfigClass,它是包含了 WebMvc Bean 定义的子 ApplicationContext/
配置为 ServletMapping,这意味全部的请求将由 DispatcherServlet 处理为 User 实体建立一个 JPA 实体 User.java 和一个 Spring Data JPA 资源库。
@Entity public class User { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Integer id; private String name; //setters and getters }
public interface UserRepository extends JpaRepository<User, Integer> { }
建立一个 Spring MVC 控制器来处理 URL 为 /
,并渲染一个用户列表。
@Controller public class HomeController { @Autowired UserRepository userRepo; @RequestMapping("/") public String home(Model model) { model.addAttribute("users", userRepo.findAll()); return "index"; } }
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"/> <title>Home</title> </head> <body> <table> <thead> <tr> <th>Id</th> <th>Name</th> </tr> </thead> <tbody> <tr th:each="user : ${users}"> <td th:text="${user.id}">Id</td> <td th:text="${user.name}">Name</td> </tr> </tbody> </table> </body> </html>
全部都配置好了,能够启动应用了。但在此以前,咱们须要在 IDE 中下载并配置像 Tomcat、Jetty 或者 Wildfly 等服务器。
你能够下载 Tomcat 8 并配置在你喜欢的 IDE 中,以后运行应用并将浏览器指向 http://localhost:8080/springmvc-jpa-demo
。你应该看到一个以表格形式展现的用户详细信息列表。
真激动,咱们作到了!
可是等等,作了那么多的工做仅仅是为了从数据库中获取用户信息而后展现一个列表?
让咱们坦诚公平地来看待,全部的这些配置不只仅是为了此次示例,这些配置也是其余应用的基础。
但我仍是想说,若是你想早点起床跑步,那对不起,你还有太多的工做要作。
另外一个问题是,假设你想要开发另外一个 Spring MVC 应用,你会使用相似的技术栈?
好,你要作的就是复制粘贴配置并调整它。对么?请记住一件事:若是你一次又一次地作一样的事情,你应该寻找一种自动化的方式来完成它。
除了一遍又一遍地编写相同的配置,你还能发现其余问题么?
这样吧,让我列出我从中发现的问题。
若是 Spring 能够自动帮我作这些事情,那真的是非!常!棒!
想象一下,若是 Spring 可以自动配置 bean 呢?若是你可使用简单的自定义配置方式来定义自动配置又将会怎么?
例如,你能够将 DispatcherServlet 的 url-pattern
映射到 /app/
,而不是 /
。你能够将 Theymeleaf 视图放在 /WEB-INF/template/
文件夹下,而不是 /WEB-INF/views
中。
因此基本上你但愿 Spring 能自动执行这些操做,Spring 它有没有提供一个简单灵活的方式来覆盖掉默认配置呢?
很好,你即将踏进入 Spring Boot 的世界,你将梦想成真!
欢迎来到 Spring Boot 世界!Spring Boot 正是你一直在寻找的。它能够自动为你完成某些事情,但若是有必要,你能够覆盖掉这些默认配置。
与其夸夸而谈,不如来点案例实战。
建立一个 Maven 项目并配置以下依赖:
<?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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sivalabs</groupId> <artifactId>hello-springboot</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>hello-springboot</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.2.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> </project>
太神奇了,咱们的 pom.xml 文件一会儿变小了许多!
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=admin spring.datasource.initialize=true spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true
你能够将相同的 data.sql 文件拷贝到 src/main/resources
文件加中。
与 springmvc-jpa-demo
应用同样,建立 User.java、UserRepository.java 和 HomeController.java。
从 springmvc-jpa-demo
项目中复制以前建立的 /WEB-INF/views/index.html
到 src/main/resources/template
文件夹中。
建立一个含有 main
方法的 Java 类 Application.java,以下所示:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
如今把 Application.java
看成一个 Java 应用运行,并将你的浏览其指向 http://localhost:8080/
。
不出意外,你将看到一个以表格的形式展现的用户列表,真的很酷!
我仿佛听到你在喊:“这到底发生了什么事?”。
让我解释刚刚所发生的事情。
简单的依赖管理
首先要注意的是咱们使用了一些名为 spring-boot-start-*
的依赖。记住我说过我花费 95% 的时间来配置一样的配置。当你在开发 Spring MVC 应用时添加了 spring-boot-start-web
依赖,它已经包含了一些经常使用的类库,好比 spring-webmvc、jackson-json、validation-api 和 tomcat 等。
咱们添加了 spring-boot-starter-data-jpa
依赖。它包含了全部的 spring-data-jpa
依赖,而且还添加了 Hibernate 库,由于不少应用使用 Hibernate 做为 JPA 实现。
自动配置
spring-boot-starter-web
不只添加了上面所说的这些库,还配置了常常被注册的 bean,好比 DispatcherServlet、ResourceHandler 和 MessageSource 等 bean,而且应用了合适的默认配置。
咱们还添加了 spring-boot-starter-Thymeleaf
,它不只添加了 Thymeleaf 的依赖,还自动配置了 ThymeleafViewResolver bean。
虽然咱们没有定义任何 DataSource、EntityManagerFactory 和 TransactionManager 等 bean,但它们能够被自动建立。怎么样?若是在 classpath 下没有任何内存数据库驱动,如 H2 或者 HSQL,那么 Spring Boot 将自动建立一个内存数据库的 DataSource,而后应用合适的默认配置自动注册 EntityManagerFactory 和 TransactionManager 等 bean。可是咱们使用的是 MySQL,所以咱们须要明确提供 MySQL 的链接信息。咱们已经在 application.properties
文件中配置了 MySQL 链接信息,Spring Boot 将应用这些配置来建立 DataSource。
支持嵌入式 Servlet 容器
@SpringApplication
,它有一个 main
方法。经过运行 main
方法,咱们能够启动这个应用,并可经过 http://localhost:8080/
来访问。咱们添加了 spring-boot-starter-web
,它会自动引入 spring-boot-starter-tomcat
。当咱们运行 main()
方法时,它将 tomcat 做为一个嵌入式容器启动,咱们不须要部署本身的应用到外部安装好的 tomcat 上。
顺便说一句,你看到咱们在 pom.xml 中配置的打包类型是 jar 而不是 war,真有趣!
很是好,但若是我想使用 jetty 服务器而不是 tomcat 呢?很简单,只须要从 spring-boot-starter-web
中排除掉 sprig-boot-starter-tomcat
,并包含 spring-boot-starter-jetty
依赖便可。
就是这样。
但这看起来真的很神奇!
我能够想象此时你在想什么。你正在感叹 Spring Boot 真的很酷,它为你自动完成了不少事情。可是,你还没了彻底明白它幕后是怎样工做的,对不对?
我能够理解,就像观看魔术表演,过程很是有趣,但你不知道魔术师是如何作到的。软件开发则不同,你不用担忧,将来咱们还将看到各类新奇的东西,并在之后的文章中详细地解释它们幕后的工做原理。很遗憾的是,我不能在这篇文章中把全部的东西都教给你。
在本文中,咱们快速介绍了 Spring 的各类配置风格,并了解了配置 Spring 应用的复杂性。此外,咱们经过建立一个简单的 Web 应用来快速了解 Spring Boot。
在下一篇文章中,咱们将深刻了解 Spring Boot,了解它的工做原理。