SpringBoot初体验及原理解析

1、前言

​  上篇文章,咱们聊到了SpringBoot得以实现的幕后推手,此次咱们来用SpringBoot开始HelloWorld之旅。SpringBoot是Spring框架对“约定大于配置(Convention over Configuration)”理念的最佳实践。SpringBoot应用本质上就是一个基于Spring框架的应用。咱们大多数程序猿已经对Spring特别熟悉了,那随着咱们的深刻挖掘,会发现SpringBoot中并无什么新鲜事,若是你不信,那就一块儿走着瞧呗!html

2、SpringBoot初体验

首先,咱们按照下图中的步骤生成一个SpringBoot项目:vue

  

解压后的项目文件在idea中打开之后,咱们会看到以下的项目结构:java

  

这时候,咱们在com.hafiz.springbootdemo包下新建controller包,而后再在该包下面新建DemoController.java文件,内容以下:react

package com.hafiz.springbootdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/** * @author hafiz.zhang * @description: first spring boot controller * @date Created in 2018/6/3 16:49. */
@RestController
public class DemoController {

    @GetMapping("/hello")
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

而后咱们运行SpringbootDemoApplication.java这个main方法,启动成功后,在控制台中会出现如下日志,证实项目启动成功:web

  

咱们从日志中能够看出,已经注册了get方式的/hello的路径以及名为/error的路径,而且项目在tomat中运行在8080端口,而后咱们在浏览器中访问localhost:8080/hello?name=hafiz.zhang会看到以下信息:redis

这样咱们就完成了demo项目的开发,先不用这里面到底发生了啥,我就想问问你:是否是很爽?彻底没有传统项目的繁琐的xml配置,爽到爆有木有?搭建一个项目骨架只要几秒钟!这就是SpringBoot带给咱们的便利~ 就是这么神奇!就是这么牛逼!spring

3、项目简单解析

首先咱们来看,生成好的项目中的SpringbootDemoApplication.java文件,内容以下:json

package com.hafiz.springbootdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootDemoApplication {

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

代码很简单,但最耀眼的就是@SpringBootApplication注解以及在main方法中运行的SpringAppliation.run()了,那咱们要揭开SpringBoot应用的奥秘,很明显就要拿这二位开刀了!浏览器

@SpringBootApplication注解解析

先看看@SpringBootApplication注解的源码:springboot

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    ...
}

咱们看到@SpringBootApplication实际上是一个复合的注解,起主要做用的就是@SpringBootConfiguration@EnableAutoConfiguration以及@ComponentScan 三个注解组成,因此若是咱们把SpringBoot启动类改写成以下方式,整个SpringBoot应用依然能够与以前的启动类功能同样:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public class SpringbootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }
}

由于咱们每次新建项目时都要写上三个注解来完成配置,这显然太繁琐了,SpringBoot就为咱们提供了@SpringBootApplication这样一个复合注解来简化咱们的操做。简直不能再贴心一点了!

@SpringBootConfiguration注解解析

接着咱们来看@SpringBootConfiguration注解的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

咱们能够看到,这里面依旧没有什么新东西,它SpringBoot为了区别@Configuration而新提供的专属于SpringBoot的注解,功能和@Configuration如出一辙。而这里的@Configuration注解对于咱们来讲并不陌生,咱们在上篇文章中进行了详细的介绍。SpringBoot的启动类标注了这个注解,毫无疑问,它自己也是个IoC容器的配置类。了解到了这里,咱们其实能够把SpringBoot的启动类来拆成两个类,拆完之后就很是清楚明了了:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class DemoConfiguration {

}
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoConfiguration.class, args);
    }
}

因此呢,启动类DemoApplication其实就是一个标准的Standalone类型的Java程序的main函数启动类,也并无什么特殊的东西。

@EnableAutoConfiguration 的神奇之处

看到这货,咱们不只联想出Spring 中不少以“@Enable”开头的注解,好比:@EnableScheduling@EnableCaching以及@EnableMBeanExport等,@EnableAutoConfiguration注解的理念和工做原理和它们其实一脉相承。简单的来讲,就是该注解借助@Import注解的支持,Spring的IoC容器收集和注册特定场景相关的Bean定义:

  • @EnableScheduling是经过@Import将Spring调度框架相关的bean都加载到IoC容器。
  • @EnableMBeanExport是经过@Import将JMX相关的bean定义加载到IoC容器。

@EnableAutoConfiguration注解也是借助@Import将全部复合配置条件的bean定义加载到IoC容器,仅此而已!而@EnableAutoConfiguration注解的源码以下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}

这其中最关键的就是@Import(EnableAutoConfigurationImportSelector.class)了,它借助EnableAutoConfigurationImportSelector.class能够帮助SpringBoot应用将全部符合条件的@Configuration配置类都加载到当前SpringBoot建立并使用的IoC容器,就像一个“八爪鱼”同样。

  

下面咱们给出EnableAutoConfigurationImportSelector.java的父类AutoConfigurationImportSelector.java的部分源码,来解释和验证上图:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
                this.beanClassLoader);
    }
    protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class,
                this.beanClassLoader);
    }
}

以上源码能够看出,@EnableAutoConfiguration正是借助SpringFactoriesLoader的支持,才能完成如此伟大的壮举!

幕后英雄SpringFactoriesLoader详解

SpringFactoriesLoader属于Spring框架专属的一种扩展方案(其功能和使用方式相似于Java的SPI方案:java.util.ServiceLoader),它的主要功能就是从指定的配置文件META-INF/spring.factories中加载配置,spring.factories是一个很是经典的java properties文件,内容格式是Key=Value形式,只不过这Key以及Value都很是特殊,为Java类的完整类名(Fully qualified name),好比:

com.hafiz.service.DemoService=com.hafiz.service.impl.DemoServiceImpl,com.hafiz.service.impl.DemoServiceImpl2

而后Spring框架就能够根据某个类型做为Key来查找对应的类型名称列表了,SpringFactories源码以下:

public abstract class SpringFactoriesLoader {

    private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader){
        ...
    }

    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        ...
    }
    // ...
}

对于@EnableAutoConfiguraion来讲,SpringFactoriesLoader的用途和其本意稍微不一样,它本意是为了提供SPI扩展,而在@EnableAutoConfiguration这个场景下,它更多的是提供了一种配置查找的功能的支持,也就是根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration做为Key来获取一组对应的@Configuration类:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

在SpringBoot的autoconfigure依赖包中的META-INF文件下的spring.factories文件中,咱们能够找到以上内容,这就很好的解释了为何。

总结来讲,@EnableAutoConfiguration能实现自动配置的原理就是:SpringFactoriesLoader从classpath中搜寻全部META-INF/spring.fatories文件,并将其中Key[org.springframework.boot.autoconfigure.EnableAutoConfiguration]对应的Value配置项经过反射的方式实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,而后汇总到当前使用的IoC容器中。

非必须的@ComponentScan解析

为何说这个注解是非必需的呢?由于咱们知道做为Spring框架里的老成员,@ComponentScan的功能就是自动扫描并加载复合条件的组件或Bean定义,最终将这些bean定义加载到当前使用的容器中。这个过程,咱们能够手工单个进行注册,不是必定要经过这个注解批量扫描和注册,因此说@ComponentScan是非必需的。

因此,若是咱们当前应用没有任何bean定义须要经过@ComponentScan加载到当前SpringBoot应用对应的IoC容器,那么,去掉@ComponentScan注解,当前的SpringBoot应用依旧能够完美运行!那是否是到目前为止,依旧没有什么新鲜的事物出现?

关于咱们用到的@RestController以及@GetMapping

@RestController注解也是一个复合注解,没啥新东西,就是自动返回json类型的数据,源码以下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    String value() default "";
}

@GetMapping注解也是一个复合注解,依旧没啥新东西,就是用来定义只支持GET请求的URL,源码以下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
    ...
}

4、总结

  刚开始没有深刻接触SpringBoot的时候,感受这货真的好神奇,居然能够省去那么多搭建步骤。这特么简直就是开发人员的福音啊,再回头想一想这货为啥会火呢?咱们都知道近几年微服务的概念很火,不少企业也开始把本身的项目作成微服务了,那随着项目愈来愈多,按照咱们之前那种方式配置项目,很繁琐,光建立项目就要耗费很大的精力和时间,这个时候拯救世界的SpringBoot出现了,它简化了配置方式,实乃应运而生,顺势而为啊!再不火,没天理不是。下篇文章咱们来聊一聊SpringBoot的启动流程。期待的小伙伴,评论扣1.

相关文章
相关标签/搜索