『面试の神』为什么SpringBoot这么火?你有想过吗?

587 阅读6分钟

「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」。

前言

Hello 大家好,我是l拉不拉米,今天『面试の神』系列为大家讲一讲为什么Springboot近几年会如此火爆,成为了Java项目的首选,通过与传统的SpringMvc做对比,我们可以非常直观的了解到Springboot的巨大优势。

Spring 概述

Spring 是一个非常流行的基于 Java 的框架,用于构建 Web 和企业应用程序。与许多其他只关注一个领域的框架不同,Spring 框架通过其投资组合项目提供了广泛的特性来满足现代业务需求。

如果您是一名 Java 开发人员,那么您很有可能听说过 Spring 框架并且可能已经在您的项目中使用过它。 Spring 框架主要是作为依赖注入容器创建的,但它远不止于此。

Spring 受欢迎主要有以下几个原因:

  • Spring 的依赖注入方法鼓励编写可测试的代码
  • 使用简单但强大的数据库事务管理功能
  • Spring 简化了与其他 Java 框架的集成,如 JPA/Hibernate ORM、Struts/JSF/等网络框架
  • 用于构建 Web 应用程序的最先进的 Web MVC 框架

除了 Spring 框架之外,还有许多其他 Spring 姊妹项目可以帮助构建满足现代业务需求的应用程序。比如:

  • Spring Data:简化来自关系和 NoSQL 数据存储的数据访问。
  • Spring Batch:提供强大的批处理框架。
  • Spring Security:强大的安全框架来保护应用程序。
  • Spring Social:支持与 Facebook、Twitter、LinkedIn、GitHub 等社交网站集成。
  • Spring Integration:企业集成模式的一种实现,用于使用轻量级消息传递和声明式适配器促进与其他企业应用程序的集成。

有关更多信息,可以在Spring官网查看:Spring官网

image.png

基于 XML 的配置

最开始的版本中,Spring 框架提供了一种基于 XML 的方法来配置 bean。后来 Spring 引入了基于 XML 的 DSL、注释和基于 JavaConfig 的方法来配置 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;
    }
    ...
    ...
}

基于 JavaConfig 的配置

@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 提供了许多方法来做同样的事情,我们甚至可以混合使用这些方法并在同一个应用程序中使用基于 JavaConfig 和基于注解的配置样式。

刚接触 Spring 框架的人可能会对遵循哪种方法感到困惑。Spring 官方建议遵循基于 JavaConfig 的方法,因为它提供了更大的灵活性。但是我们不推荐一刀切的解决方案。我们应当根据自己的应用需求来选择方法。

Spring 框架提供了以 XML、Annotations 和 JavaConfig 等多种方式配置 bean 的灵活性。随着功能数量的增加,复杂性也随之增加,配置 Spring 应用程序变得乏味且容易出错。

因此,Spring 团队创建了 Spring Boot 来解决配置的复杂性。

基于Spring MVC和JPA的Web应用

在了解 Spring Boot 是什么以及它提供什么样的功能之前,让我们先来看看典型的 Spring Web 应用程序配置的组成、痛点以及 Spring Boot 如何解决这些问题。

1、配置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.rameo</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>

依赖项配置为包括 Spring MVC、Spring Data JPA、JPA/Hibernate、Thymeleaf 和 Log4j。

2、使用 JavaConfig 配置service/DAO 层 Bean

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages="com.rameo.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 Configuration 类。
  • 使用 @EnableTransactionManagement 启用基于注解的事务管理
  • 配置 @EnableJpaRepositories 以指示在哪里查找 Spring Data JPA 存储库
  • 使用 @PropertySource 注解和 PropertySourcesPlaceholderConfigurer bean 定义配置 PropertyPlaceHolder bean,它从 application.properties 文件加载属性。
  • DataSource、JPA EntityManagerFactoryJpaTransactionManager 定义的 bean。
  • 配置 DataSourceInitializer bean 通过在应用程序启动时执行 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,'zhangsan');
insert into user(id, name) values(2,'lisi');
insert into user(id, name) values(3,'wangwu');

创建具有基本配置的 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

3、配置 SpringMVC Web 层 Bean

配置 Thymeleaf ViewResolver、静态 ResourceHandlers、MessageSource 等。

@Configuration
@ComponentScan(basePackages = { "com.rameo.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 Configuration 类。
  • 使用 @EnableWebMvc 启用基于注解的 Spring MVC 配置
  • 通过注册 TemplateResolverSpringTemplateEngineThymeleafViewResolver bean 来配置 Thymeleaf ViewResolver。
  • 注册 ResourceHandlers bean 以指示对具有 URI /resources/** 的静态资源的请求将从位置 /resources/ 目录提供服务。
  • 配置 MessageSource bean 以从类路径中的 ResourceBundle messages-{country-code}.properties 加载 i18n 消息。

4、注册 SpringMVC DispatcherServlet

在 Servlet 3.x 规范之前,我们必须在 web.xml 中注册 Servlets/Filters。从 Servlet 3.x 规范开始,我们可以使用 ServletContainerInitializer 以编程方式注册 Servlets/Filters。

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 配置为 RootConfirationClasses,它将成为包含所有子 (DispatcherServlet) 上下文共享的 bean 定义的父 ApplicationContext
  • WebMvcConfig.class 配置为 ServletConfigClasses,它是包含 WebMvc bean 定义的 子 ApplicationContext
  • 将“/”配置为 ServletMapping 意味着所有请求都将由 DispatcherServlet 处理。
  • OpenEntityManagerInViewFilter 注册为 Servlet 过滤器,以便我们可以在渲染视图时延迟加载 JPA 实体延迟集合。

5、创建 JPA 实体和 Spring Data JPA Repository

@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>
{
}

6、创建 SpringMVC 控制器

@Controller
public class HomeController
{
    @Autowired UserRepository userRepo;

    @RequestMapping("/")
    public String home(Model model)
    {
        model.addAttribute("users", userRepo.findAll());
        return "index";
    }
}

7、创建一个 Thymeleaf 视图 /WEB-INF/views/index.html

<!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>

到这里,代码的开发工作已经完毕了,但是要运行我们的应用程序,还需要一个web服务容器,比如:TomcatJetty

这一套流程下来产生了两个问题:

  1. 工作量大,步骤多,漏了哪一个步骤都没办法成为一个web服务;
  2. 如果需要开发新的SpringMVC应用,最好的办法是复制粘贴,同样的事情再做一遍,缺少自动化的流程。

如果 Spring 可以自动为我做到这一点,那该多好啊!!!。

想象一下,如果 Spring 能够自动配置 bean 会怎样?如果您可以使用简单的可自定义属性来自定义自动配置会怎样?

SpringBoot 他lei了

我们再实现一遍之前构建的相同的应用程序,但这一次使用SpringBoot。

1、创建基于Maven的Spring Boot项目

<?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的依赖变小了很多。

2、在application.properties中配置DataSource / JPA属性

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

3、创建 JPA 实体和 Spring Data JPA Repository

创建User.javaUserRepository.javaHomeController.javaspringmvc-ja-demo应用程序相同。

4、创建 Thymeleaf 视图

复制springmvc-jpa-demo应用程序的/Web-inf/views/index.html,到springboot应用程序的src/main/ Resources/Templates文件夹中。

5、创建 SpringBoot 启动类

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

现在将Application.java运行为Java应用程序并再浏览器访问http//localhost:8080/

一个典型SpringBoot应用程序就完成了。是不是特别简单。

我们来看看究竟是怎样实现的。

1.简单的依赖管理

一些类似spring-boot-starter-*的依赖项,比如:SpringBoot-Starter-Web依赖关系默认情况下,它将拉取开发Spring MVC应用程序需要的所有常用的库,例如Spring-WebMVC,Jackson-Json,Validation-API和内置的Tomcat。

2.自动配置

使用默认值配置常用的bean,如DispatcherServlet,ResourceHandlers,MessageSource等Bean。

不需要定义任何数据源,EntityManagerFactory,TransactionManageretC bean,它们会自动创建。如果我们的类路径中的H2或HSQL等内存数据库驱动程序,则SpringBoot将自动创建内存数据源,然后使用默认值自动注册EnterityManagerFactory,TransactionManager Bean。如果使用MySQL,我们也只需要在Application.properties文件中提供MySQL连接详细信息。

3.嵌入式servlet容器支持 最重要的是只需创建了一个简单的Java类,其中包含一些具有主要方法的@springApplication,并且通过运行该主要方法我们能够通过 http://localhost:8080/ 访问程序。

当我们添加了 Spring-Boot-Starter-Web,它自动拉取 Spring-Boot-Starter-Tomcat,当我们运行启动类的Main()方法时,它将Tomcat启动为嵌入式容器,因此我们不用再部署我们的应用程序到外部安装的Tomcat服务器。

总结

相信看完文章,大部分人都会更加坚定的拥抱SpringBoot,SpringBoot不仅简化了应用程序的开发,也是微服务SpringCloud的项目基础。如果你还在使用传统的SpringMVC的技术栈,不妨尝试一下SpringBoot,我敢保证你会爱上他。