SpringBoot(十二)

springBoot

一、概述

1.什么是spring boot

Spring Boot是Spring项目中的一个子工程,与我们所熟知的Spring-framework 同属于spring的产品:

其最主要作用就是帮助开发人员快速的构建庞大的spring项目,并且尽可能的减少一切xml配置,做到开箱即用,迅速上手,让开发人员关注业务而非配置。

2.spring boot的特点

1.自动配置:不需要再关注各个框架的整合配置,springboot全部都已经配置好了。

2.起步依赖:我们在需要使用某个框架的时候,直接添加这个框架的启动器依赖即可,不需要在关注jar包的冲突和整合。

3.目的

用来简化spring应用的初始搭建以及开发过程。

  1. 为所有spring开发提供一个更快更广泛的入门体验。
  2. 零配置。没有冗余代码生成和xml强制配置,遵循“约定大于配置”
  3. 集成了大量常用的第三方库的配置,spring boot应用为这些第三方库提供了几乎可以零配置的开箱即用的能力。
  4. 提供一系列大型项目常用的非功能性,如嵌入服务器等。

4.好处

简单、快速、方便

5.优势

使用Java开发程序 , 一直困扰我们的就是臃肿、麻烦。搭建项目的过程相当复杂 , 我们需要考虑很多问题 , 主要的问题有如下两点 :

  1. 复杂的配置
  2. 混乱的依赖管理

Spring Boot帮我们解决了这个些, 我们在使用Spring Boot开发时, 不需要关注各种复杂的整合配置 , 也不用关注各个库之间的依赖及冲突问题 , Spring Boot已经默认帮我们整合配置好了 !

二、springboot之helloworld

1.需求

访问 http://localhost:8080/hello输出 “Hello Spring Boot”

2.步骤

  1. 创建Maven工程

  2. 加依赖(springboot父工程依赖 , web启动器依赖)

    扫描二维码关注公众号,回复: 13435198 查看本文章
    <!--spring boot 父工程依赖-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>
    <!--web 启动器-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    复制代码

    配置jdk版本

    <properties>
        <java.version>1.8</java.version>
    </properties>
    复制代码

    思考: 为什么我们这里仅仅配置了这么一个变量 , 项目的JDK版本就会改变呢 ?

    因为jdk插件已经在父工程中定义好了 , 默认会读取${java.version}变量值

  3. 写启动引导类(springboot项目运行的入口)

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class,args);
        }
    }
    
    复制代码
  4. 编写处理器Controller

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    public class HelloController {
        @RequestMapping("/hello")
        public String sayHello(){
            return "hello spring boot!!" ;
        }
    }
    
    复制代码
  5. 启动项目

    运行启动类的main方法

3.问题

1.为什么我们在添加启动器的时候不需要在启动器的坐标中指定版本?

答案:因为我们指定了项目的父工程,在spring-boot-starter-parent中已经通过Maven的版本锁定了Jar包的版本,所以就不需要再指定了。

2.为什么我们就添加一个启动器依赖,项目就可以运行起来了,运行项目所需要的Jar包从何而来?

答案:因为我们添加了这个启动器的依赖,它已经把自己运行所需要的必要包集成在这个启动器中,通过Maven的依赖传递性,将这些包都依赖到咱们的项目里了。

4.配置文件详解

springboot支持两种类型的配置文件

  • properties属性配置文件
  • yaml配置文件

配置文件必须放在项目的类加载目录下, 并且名字必须是application开头

springboot项目在运行的时候会自动加载这些配置文件。

可以在spring-boot-autoconfigure-2.2.2.RELLEASE.jar的META-INF目录下找到additional-spring-configuration-metadata.json搜索server.port,显示默认的端口是8080.

为什么可以在resources下创建application.properties文件?

我们查看springboot的启动依赖

按住ctrl键点击spring-boot-starter-parent

可以在父工程中看到

<build>
    <resources>
    	<resource>
            <filtering>true</filtering>
            <directory>${basedir}/src/main/resources</directory>
            <include>**/application*.yml</include>
            <include>**/application*.yaml</include>
            <include>**/application*.properties</include>
        </resource>
    </resources>
</build>
复制代码

5.如何主动读取配置文件到类变量

要使用@Value("${属性名}")

如下:

resource 文件夹下面新建 application.properties 配置文件

spring.jdbc.datasource.driverClassName=com.mysql.jdbc.driver
spring.jdbc.datasource.url=jdbc:mysql:///springboot_01
spring.jdbc.datasource.username=root
spring.jdbc.datasource.password=123456
复制代码

创建类 DataSourceProperties


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class DataSourceProperties {

    @Value("${spring.jdbc.datasource.driverClassName}")
    private String driverClassName;
    @Value("${spring.jdbc.datasource.url}")
    private String url;
    @Value("${spring.jdbc.datasource.username}")
    private String username;
    @Value("${spring.jdbc.datasource.password}")
    private String password;

    // 生成get set 和 toString方法
}
复制代码

三、YAML配置文件

1.什么是YAML?

YAML是一种配置文件格式

2.语法

1.数据结构用树形结构呈现,通过缩进来表示层级,

2.连续的项目通过减号 ” - ” 来表示

3.键值结构里面的key/value对用冒号 ” : ” 来分隔,冒号后要加上一个空格才能跟属性值。

4.YAML配置文件的扩展名是yaml 或 yml

如果要修改前面那个properties文件为yaml文件

spring:
  jdbc:
    datasource:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql:///springboot_01
      username: root
      password: 123456
复制代码

yml配置文件的特征

  1. 树状层级结构展示配置项
  2. 配置项之间如果有关系的话需要分行,空两格
  3. 配置项如果有值的话,那么需要在:之后空一格再写配置项值;

yaml与peoperties配置文件处理展示像是不相同以外,其他功能和作用都是一样的。

3.多环境profile切换配置

在实际开发中,因为开发环境的变化,我们需要修改配置文件中某一个配置项的值(比如之前himysql数据库,切换成oracle数据库)

项目开发完成需要上线时,需要把一些环境改成正式环境(开发,测试,上线,多环境切换)

解决方案:使用profiles拆分配置

spring boot项目中允许使用多个yaml配置文件。

这些文件名称必须为**application-***.yml**,并且在application.yml中激活。

具体实施

创建application-dev.yml文件如下:

spring:
  jdbc:
    datasource:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql:///springboot
      username: root
      password: 123456
复制代码

创建application-pro.yml文件如下:

spring:
  jdbc:
    datasource:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql:///business
      username: business
      password: business
复制代码

在application.yml文件中添加如下配置:

spring:
  profiles:
    active: dev
复制代码

此时生效的文件就是application-dev.yml配置文件。

注意:

如果properties和yml文件都存在,不存在spring.profiles.active设置,如果有重叠属性,默认以properties文件优先.

如果设置了spring.profiles.active,并且有重叠属性,以active设置优先。

可以在两种文件中分别增加server.port属性指定不同的端口,启动项目查看控制台端口号进行测试。

四、Spring Boot 的自动配置原理

1.@SpringBootApplication注解

①@SpringBootConfiguration

含义:代表这个类就是一个配置类,本质上就是一个@Configuration注解

②ComponentScan

组件扫描,默认扫描启动类所在包以及子包的类身上的注解。

③EnableAutoConfiguration

自动配置注解,添加了此注解会自动去读取spring.factories配置文件中的自动配置类。

2.@ConfigurationProperties注解

@ConfigurationProperties是SpringBoot提供的重要注解,他可以将一些配置属性批量注入到bean对象

注入配置属性

方式一:使用@Value一个个注入

这种方式,如果属性特别多,一个一个注入非常麻烦。

方式二:使用@ConfigurationProperties批量注入【和@EnableConfigurationProperties配合使用】

@Component
@ConfigurationProperties(prefix = "spring.jdbc.datasource")
public class DataSourceProperties2 {

    private String driverClassName;
    private String url;
    private String username;
    private String password;
    
     // 省略getter和setter.....
}    
复制代码
  1. 在类上通过@ConfigurationProperties注解声明该类要读取属性配置。
  2. prefix="spring.jdbc.datasource"读取属性文件中前缀为spring.jdbc.datasource的值。前缀和属性名称和配置文件中的key必须要一致才能注入成功
  3. Spring Boot默认读取application.properties属性文件

开启@ConfigurationProperties注解使用

@Controller
@EnableConfigurationProperties(DataSourceProperties2.class)
public class HelloController {
    
    @Autowired
    private DataSourceProperties2 dataSourceProperties2 ;

    @RequestMapping(path = "/hello")
    @ResponseBody
    public String sayHello(){
        System.out.println(dataSourceProperties2);
        return "hello spring boot";
    }
}
使用@EnableConfi
复制代码

使用@EnableConfigurationProperties(DataSourceProperties2.class),开启DataSoutceProperties2身上的@ConfigurationProperties注解,他就会生效了,就能够帮我们注入数据了。

可能出现错误Spring Boot Configuration Annotation Processor not found in classpath

解决方法:

在pom文件中加入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
复制代码

3.条件化配置注解

​ 我们看到自动配置类上有一些ConditionalXxx注解,这些注解的作用就是进行条件化选择。

​ 所谓条件化选择就是如果满足条件,该配置类就生效,如果不满足该配置类就不生效。

常用的条件化选座注解如下:

注解 作用
@ConditionalOnBean 如果存在某个Bean, 配置类生效
@ConditionalOnMissingBean 如果不存在某个Bean, 配置类生效
@ConditionalOnClass 如果存在某个类, 配置类生效
@ConditionalOnMissingClass 如果不存在某个类, 配置类生效
@ConditionalOnProperty 如果存在某个属性配置, 配置类生效
@ConditionalOnWebApplication 如果是一个web应用, 配置类生效
@ConditionalOnNotWebApplication 如果不是一个web应用, 配置类生效

因为我们配置了DispatcherServlet满足上面定义的条件,所以WebMvcAutoConfiguration会生效,那么WebMvcAutoConfiguration自动配置类中帮我们配置了什么?

  1. 视图解析器(触发public InternalResourceViewResolver defaultViewResolver()public BeanNameViewResolver beanNameViewResolver()方法)
  2. 处理器适配器(public RequestMappingHandlerAdapter requestMappingHandlerAdapter()方法)

这些配置都是我们之前在学习SpringMVC时需要自己配置的,现在Spring Boot框架都已经帮我们提前配置好了,所以我们才能使用的那么方便。

4.自动配置原理

1.加载spring.factories

在SpringApplication类构建的时候,有这样一段初始化代码:

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
复制代码

注意 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));点击进入getSpringFactoriesInstances方法

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }
复制代码

紧接着跟入getSpringFactoriesInstances方法

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
复制代码

可以看到Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));这条代码。能够发现loadFactoryNames()尝试加载一些FactoryName,然后利用createSpringFactoriesInstances将这些加载到的类名进行实例化。然后继续跟进loadFactoryNames方法:

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }
复制代码

观察返回时调用的loadSpringFactories方法

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }
复制代码

发现此处会利用类加载器加载一个文件:META-INF/factories。我们知道,ClassLoader默认是从classpath下读取的文件,因此,SpringBoot会在初始化的时候,加载所有classpath:META-INF/spring.factories文件,包括jar包当中的。而在Spring的一个依赖包:spring-boot-autoconfigure中,就有这样一个文件。

image-20211029163735112

springboot-1.png 我们引入的任何第三方启动器,只要实现自动配置,也都会有类似文件。

2.读取自动配置类

打开这个spring.factories

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
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.CloudServiceConnectorsAutoConfiguration,\
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.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
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.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
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.RedisReactiveAutoConfiguration,\
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.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
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.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
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.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
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.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
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.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.flyway.FlywayMigrationScriptMissingFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
复制代码

可以发现以EnableAutoConfigutation接口为key的一系列配置,key所对应的值,就是所有的自动配置类,可以在当前的jar包中找到这些自动配置类:

springboot-2.png 几乎涵盖了现在主流的开源框架,我们可以从这里查看SpringMVC的自动配置类。

springboot-3.png 我们就找到了springmvc的自动配置类

3.默认属性配置

配置类找到了,那么这些默认配置的属性来自哪里?

例如:我们配置视图解析器的时候需要配置前缀后后缀,那么这些配置在哪里?


        @Bean
        @ConditionalOnMissingBean
        public InternalResourceViewResolver defaultViewResolver() {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix(this.mvcProperties.getView().getPrefix());
            resolver.setSuffix(this.mvcProperties.getView().getSuffix());
            return resolver;
        }
复制代码

通过这段源码发现,这个配置是this.mvcProperties.getView()中获取的。查看这个方法。

public WebMvcProperties.View getView() {
    return this.view;
}
复制代码

发现读取的是类变量

    private final WebMvcProperties.View view;
复制代码

这个View类型封装着前缀和后缀

public static class View {
        private String prefix;
        private String suffix;

        public View() {
        }

        public String getPrefix() {
            return this.prefix;
        }

        public void setPrefix(String prefix) {
            this.prefix = prefix;
        }

        public String getSuffix() {
            return this.suffix;
        }

        public void setSuffix(String suffix) {
            this.suffix = suffix;
        }
    }
复制代码

可以看到,前缀和后缀变量的值默认是null。

4.覆盖默认属性配置

如果我们想要自己指定视图的前缀和后缀?

我们可以再看WebMvcAutoConfiguration这个类的静态内部类WebMvcAutoConfigurationAdapter上一个声明,发现这个类上面有这样一个注解@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})

@Configuration(
        proxyBeanMethods = false
    )
    @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
    @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
        private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
        private final ResourceProperties resourceProperties;
        private final WebMvcProperties mvcProperties;
        private final ListableBeanFactory beanFactory;
        private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;
        final WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;

        public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
            this.resourceProperties = resourceProperties;
            this.mvcProperties = mvcProperties;
            this.beanFactory = beanFactory;
            this.messageConvertersProvider = messageConvertersProvider;
            this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
        }

        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            this.messageConvertersProvider.ifAvailable((customConverters) -> {
                converters.addAll(customConverters.getConverters());
            });
        }

        public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
            if (this.beanFactory.containsBean("applicationTaskExecutor")) {
                Object taskExecutor = this.beanFactory.getBean("applicationTaskExecutor");
                if (taskExecutor instanceof AsyncTaskExecutor) {
                    configurer.setTaskExecutor((AsyncTaskExecutor)taskExecutor);
                }
            }

            Duration timeout = this.mvcProperties.getAsync().getRequestTimeout();
            if (timeout != null) {
                configurer.setDefaultTimeout(timeout.toMillis());
            }

        }

        public void configurePathMatch(PathMatchConfigurer configurer) {
            configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());
            configurer.setUseRegisteredSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
        }

        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            Contentnegotiation contentnegotiation = this.mvcProperties.getContentnegotiation();
            configurer.favorPathExtension(contentnegotiation.isFavorPathExtension());
            configurer.favorParameter(contentnegotiation.isFavorParameter());
            if (contentnegotiation.getParameterName() != null) {
                configurer.parameterName(contentnegotiation.getParameterName());
            }

            Map<String, MediaType> mediaTypes = this.mvcProperties.getContentnegotiation().getMediaTypes();
            mediaTypes.forEach(configurer::mediaType);
        }

        @Bean
        @ConditionalOnMissingBean
        public InternalResourceViewResolver defaultViewResolver() {
            InternalResourceViewResolver resolver = new InternalResourceViewResolver();
            resolver.setPrefix(this.mvcProperties.getView().getPrefix());
            resolver.setSuffix(this.mvcProperties.getView().getSuffix());
            return resolver;
        }

        @Bean
        @ConditionalOnBean({View.class})
        @ConditionalOnMissingBean
        public BeanNameViewResolver beanNameViewResolver() {
            BeanNameViewResolver resolver = new BeanNameViewResolver();
            resolver.setOrder(2147483637);
            return resolver;
        }

        @Bean
        @ConditionalOnBean({ViewResolver.class})
        @ConditionalOnMissingBean(
            name = {"viewResolver"},
            value = {ContentNegotiatingViewResolver.class}
        )
        public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
            ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
            resolver.setContentNegotiationManager((ContentNegotiationManager)beanFactory.getBean(ContentNegotiationManager.class));
            resolver.setOrder(-2147483648);
            return resolver;
        }

        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnProperty(
            prefix = "spring.mvc",
            name = {"locale"}
        )
        public LocaleResolver localeResolver() {
            if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) {
                return new FixedLocaleResolver(this.mvcProperties.getLocale());
            } else {
                AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
                localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
                return localeResolver;
            }
        }

        public MessageCodesResolver getMessageCodesResolver() {
            if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
                DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
                resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());
                return resolver;
            } else {
                return null;
            }
        }

        public void addFormatters(FormatterRegistry registry) {
            ApplicationConversionService.addBeans(registry, this.beanFactory);
        }

        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            if (!this.resourceProperties.isAddMappings()) {
                logger.debug("Default resource handling disabled");
            } else {
                Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
                CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
                if (!registry.hasMappingForPattern("/webjars/**")) {
                    this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
                }

                String staticPathPattern = this.mvcProperties.getStaticPathPattern();
                if (!registry.hasMappingForPattern(staticPathPattern)) {
                    this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
                }

            }
        }

        private Integer getSeconds(Duration cachePeriod) {
            return cachePeriod != null ? (int)cachePeriod.getSeconds() : null;
        }

        private void customizeResourceHandlerRegistration(ResourceHandlerRegistration registration) {
            if (this.resourceHandlerRegistrationCustomizer != null) {
                this.resourceHandlerRegistrationCustomizer.customize(registration);
            }

        }

        @Bean
        @ConditionalOnMissingBean({RequestContextListener.class, RequestContextFilter.class})
        @ConditionalOnMissingFilterBean({RequestContextFilter.class})
        public static RequestContextFilter requestContextFilter() {
            return new OrderedRequestContextFilter();
        }
    }
复制代码

再点进去看这两个配置类WebMvcProperties.class, 配置类身上使用ConfigurationProperties读取配置,前缀是spring.mvc

@ConfigurationProperties(
    prefix = "spring.mvc"
)
public class WebMvcProperties {
    //...
}
复制代码

而ResourceProperties.class读取的配置前缀是spring.resources

@ConfigurationProperties(
    prefix = "spring.resources",
    ignoreUnknownFields = false
)
public class ResourceProperties {
//...
}
复制代码

所以如果我们再配置文件中配置spring.mvc前缀开头的配置,就可以将自己配置的数据注入到这个对象的属性中。注意,由于@ConfigurationProperties是批量注入,我们在写配置属性时,要和这个类的前缀+属性名完全一致,否则注入不成功。

例如

spring:
  mvc:
    view:
      prefix: /WEB-INF/
      suffix: .jsp
复制代码

或者修改端口号

server:
  port: 10000
复制代码

5.自定义启动器

1.需求介绍

定义一个连接池启动器,当用户引入了连接池启动依赖之后,项目中就已经自动配置了连接池。

2.步骤分析

  1. 建启动器项目

  2. 添加启动器相关依赖

  3. 创建属性配置类

  4. 创建自动配置类

  5. 编写自动配置文件(spring.factories)

  6. 使用自定义的启动器

3.代码实现

1.创建项目,引入依赖

创建项目spring-boot-jdbc-starter

pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <!--引入spring‐boot‐starter;所有starter的基本配置-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
</dependency>

    <!--自动配置连接池-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.12</version>
</dependency>

    <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
</dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
复制代码
2.创建属性配置类
import org.springframework.boot.context.properties.ConfigurationProperties;

@Component
@ConfigurationProperties(prefix = "spring.jdbc.datasource")
public class DataSourceProperties {
    private String driverClassName ;
    private String url;
    private String username;
    private String password;
    // 生成set get toString方法   
}   
复制代码
3.创建自动配置类
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@SpringBootConfiguration
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

    @Autowired
    private DataSourceProperties dataSourceProperties ;

    @Bean
    public DataSource createDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        dataSource.setUrl(dataSourceProperties.getUrl());
        dataSource.setUsername(dataSourceProperties.getUsername());
        dataSource.setPassword(dataSourceProperties.getPassword());
        return dataSource;
    }
}
复制代码
4.编写自动配置属性文件

在resources文件夹下面新建META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.dyy.autoconfig.DataSourceAutoConfiguration
复制代码

做完之后要注意在Maven中执行install,安装项目。

5.使用自定义启动器

在新建的项目中引入我们自己的自定义启动器依赖

<dependency>
    <groupId>com.dyy</groupId>
    <artifactId>spring-boot-jdbc-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

复制代码

配置连接池信息。

新建application-datasource.yml

spring:
  jdbc:
    datasource:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql:///springboot_01
      username: root
      password: 123456
复制代码

激活配置文件application.yml

spring:
  profiles:
    active: datasource
复制代码

进入连接池,查看连接池属性


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.sql.DataSource;

@RestController
public class HelloController { 
    @Autowired
    private DataSource dataSource ;

    @RequestMapping(path = "/hello")
    public String sayHello() {
        System.out.println(dataSource.getClass());//打印DruidDataSource数据源
        return "Hello Spring Boot ! " ;
    }
 }   

复制代码
6.多种数据源

如果想让我们的启动器支持多种数据源。例如C3P0和Druid,根据配置进行选择,就可以使用条件选择进行实现。例如:如下配置中,有两个创建连接池的配置,一个是C3p0,一个是Druid,如何能够根据配置文件自动选择呢?

修改配置文件application-datasource.yml

spring:
  jdbc:
    datasource:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql:///springboot_01
      username: root
      password: root
      type: druid   # 数据源类型
复制代码
  • 如果配置文件中配置了spring.jdbc.datasource.type=c3p0使用c3p0数据源
  • 如果配置文件中配置了spring.jdbc.datasource.type=druid使用druid数据源。

在项目 spring-boot-jdbc-starter , 添加创建 c3p0 方法

package com.dyy.autoconfig;

import com.alibaba.druid.pool.DruidDataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguratioin {

    @Autowired
    private DataSourceProperties dataSourceProperties ;

@Bean
    @ConditionalOnProperty(value = "spring.jdbc.datasource.type",havingValue = "druid")
    public DataSource createDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        dataSource.setUrl(dataSourceProperties.getUrl());
        dataSource.setUsername(dataSourceProperties.getUsername());
        dataSource.setPassword(dataSourceProperties.getPassword());
        return dataSource;
    }

@Bean
    @ConditionalOnProperty(value = "spring.jdbc.datasource.type",havingValue = "c3p0")
    public DataSource createC3P0DataSource() throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(dataSourceProperties.getDriverClassName());
        dataSource.setJdbcUrl(dataSourceProperties.getUrl());
        dataSource.setUser(dataSourceProperties.getUsername());
        dataSource.setPassword(dataSourceProperties.getPassword());
        return dataSource;
    }
}

复制代码

我们可以使用条件选择实现 , 如下图所示

@ConditionalOnProperty(value = "spring.jdbc.datasource.type",havingValue = "druid") install安装spring-boot-jdbc-starter, 运行springboot_01

修改配置文件,重新install,再次请求。

五、spring boot常用启动器

1.SpringBoot整合MVC

创建项目 springboot_02_mvc

1.依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

复制代码

2.新建入口程序类

package com.dyy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

复制代码

3.新建javabean

public class User {
    private String username ;
    private String password ;
    private Integer age ;
private String sex ;
…
}
复制代码

4.新建UserController

import com.dyy.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping(path = "/user")
public class UserController {

    @RequestMapping(path = "/findAll")
    @ResponseBody
    public List<User> findAll(){
        //查询所有
        List<User> users = new ArrayList<User>();

        User user1 = new User();
        user1.setUsername("杨过");
        user1.setPassword("123456");
        user1.setAge(18);
        user1.setSex("男");

        User user2 = new User();
        user2.setUsername("杨过");
        user2.setPassword("123456");
        user2.setAge(18);
        user2.setSex("男");

        User user3 = new User();
        user3.setUsername("杨过");
        user3.setPassword("123456");
        user3.setAge(18);
        user3.setSex("男");

        users.add(user1);
        users.add(user2);
        users.add(user3);

        return users ;
    }
}
复制代码

运行程序。

2.静态资源目录

之前,开发web项目,如果是普通的项目金泰资源可以放在项目的webapp目录下。

现在使用Spring Boot做开发,项目中没有webapp目录,我们的项目是一个jar工程,那么就没有webapp,我么的静态资源该放哪里?

在springboot 中有一个叫做ResourceProperties得类,里面就定义了静态资源的默认查找路径。

@ConfigurationProperties(
    prefix = "spring.resources",
    ignoreUnknownFields = false
)
public class ResourceProperties {
    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
    private String[] staticLocations;
    private boolean addMappings;
    private final ResourceProperties.Chain chain;
    private final ResourceProperties.Cache cache;

    public ResourceProperties() {
        this.staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
        this.addMappings = true;
        this.chain = new ResourceProperties.Chain();
        this.cache = new ResourceProperties.Cache();
    }
    //...
    }
复制代码

可以看到默认的静态资源路径为:

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public

只要静态资源放在这些目录中的任何一个,SpringMVC都会帮我们处理。我们习惯会把静态资源放在classpath:/static/目录下。在resources目录下创建index.html文件。

打开浏览器输入 : http://localhost:8080/index.html

如果想要修改默认的静态资源路径,需要在application.yml中添加

spring:
  resources:
    static-locations: classpath:/webapp/
复制代码

重新安装启动后生效。

3.自定义的拦截器

web开发中的拦截器也是我们经常需要使用的组件,可以帮我们完成一些日志记录,数据过滤,请求过滤等等很多功能,那么在SpringBoot中该怎么配置?

在SpringMVC中配置拦截器的步骤:

  1. 编写一个拦截器(需要实现HandlerInterceptor接口)

  2. 注册拦截器(xml配置mvc:interceptors)

    ml
       <!--配置拦截器-->
       <mvc:interceptors>
           <mvc:interceptor>
               <!--配置拦截路径-->
               <mvc:mapping path="/user/**"/>
               <!--配置不拦截路径:不拦截路径是指从拦截路径中排除-->
               <mvc:exclude-mapping path="/user/sayByby"></mvc:exclude-mapping>
               <!--配置拦截器bean-->
               <bean class="com.dyy.interceptor.LogInterceptor2"></bean>
           </mvc:interceptor>
       </mvc:interceptors>
    复制代码

因为Spring Boot没有XML配置文件了,所以在SpringBoot中使用拦截器注册的方式就不太一样了,需要借助一个WebMvcConfigurer类帮助我们注册拦截器,实现拦截器的具体步骤如下:

  1. 编写一个拦截器(实现HandlerInterceptor接口)
  2. 通过WebMvcConfigurer注册拦截器(自定义类是实现WebMvcConfigurer接口中public void addInterceptors(InterceptorRegistry registry)方法)

具体步骤如下

1.编写拦截器


import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor拦截器的preHandle方法执行....");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor拦截器的postHandle方法执行....");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor拦截器的afterCompletion方法执行....");
    }
}
复制代码

2.注册拦截器


import com.dyy.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Autowired
    private MyInterceptor myInterceptor ;

    /**
     * /**  拦截当前目录及子目录下的所有路径 /user/**   /user/findAll  /user/order/findAll
     * /*   拦截当前目录下的以及子路径   /user/*     /user/findAll
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).addPathPatterns("/**");
    }
}
复制代码

打开浏览器输入 http://localhost:8888/user/findAll观察控制台。

4.整合Spring Data JPA

1.新建项目 springboot_jpa

2.添加Spring Data JPA的起步依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- springBoot JPA的起步依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- 配置使用redis启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

    </dependencies>

复制代码

3.在application.yml中配置数据库和jpa的相关属性

logging:
  level:
    com.dyy.dao: debug # 配置日志
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/springboot?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    database: mysql
    show-sql: true
    generate-ddl: true
    hibernate:
      ddl-auto: update
      naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
server:
  port: 18081

复制代码

注意:Mysql8.x版本,连接时url需要指定时区,并且驱动类包名发生了变化。

4.创建实体类配置属性


import javax.persistence.*;

@Entity
@Table(name = "user")
public class User{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;
    @Column(name = "username")
    private String username;
    @Column(name = "password")
    private String password;
    @Column(name = "name")
    private String name;
 
    //此处省略setter和getter方法... ...
}

复制代码

5.编写UserRepository

import com.dyy.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserDao extends JpaRepository<User,Integer> {
}

复制代码

6.编写service类


import com.dyy.domain.User;
import java.util.List;

public interface UserService {
    List<User> findUsers();

    User findUserById(Integer id);

    void saveUser(User user);

    void updateUser(User user);

    void deleteUserById(Integer id);
}

复制代码

7.service实现类


import com.dyy.dao.UserDao;
import com.dyy.domain.User;
import com.dyy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    /**
     * 查询所有
     * @return
     */
    @Override
    public List<User> findUsers() {
        return userDao.findAll();
    }
    /**
     * 根据id查询
     * @return
     */
    @Override
    public User findUserById(Integer id) {
        return userDao.findById(id).get();
    }
    /**
     * 保存
     * @return
     */
    @Override
    public void saveUser(User user) {
        userDao.save(user);
    }
    /**
     * 更新
     * @return
     */
    @Override
    public void updateUser(User user) {
        userDao.save(user);
    }
    /**
     * 根据id删除
     * @return
     */
    @Override
    public void deleteUserById(Integer id) {
        userDao.deleteById(id);
    }
}

复制代码

8.编写Controller类


import com.dyy.domain.User;
import com.dyy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/findAll")
    public List<User> findAll(){
        return userService.findUsers();
    }
}

复制代码

六、SpringBoot应用

1.环境搭建

1.数据库准备

create database springboot character set utf8 ;

use springboot ; 

CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `gender` varchar(5) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `address` varchar(32) DEFAULT NULL,
  `qq` varchar(20) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  `username` varchar(20) NOT NULL,
  `phone` varchar(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_username_uindex` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

INSERT INTO `tb_user` VALUES (1,'黄蓉','女',38,'桃花岛','212223390222','[email protected]','huangrong','15600003333'),(2,'黄老邪','男',58,'湖北省武汉市','212223390','[email protected]','huanglaoxie','15872320405'),(3,'小龙女','男',18,'湖北省荆门市','212223390','[email protected]','xiaolongnv','15600004444'),(7,'杨过','男',30,'扬州','212223390','[email protected]','yangguo','15600005555');

复制代码

2.创建项目以及包结构

创建项目springboot_case

3.导入依赖

<?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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>

    <dependencies>
        <!--单元测试启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!--通用mapper启动器依赖-->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.5</version>
        </dependency>
        <!--JDBC启动器依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--druid启动器依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--web启动器依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--spring boot actuator依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--编码工具包-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!—热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <!--spring boot maven插件 , 可以将项目运行依赖的jar包打到我们的项目中-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
</project>

复制代码

4.创建启动类


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@MapperScan(basePackages = "com.dyy.dao")
@EnableTransactionManagement
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

复制代码

2.数据访问层

1.编写配置文件application.yml

server:
  port: 10001
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///springboot
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
mybatis:
  type-aliases-package: com.dyy.pojo

复制代码

2.编写实体类User


import java.io.Serializable;

@Entity
@Table(name = "tb_user")
public class User implements Serializable {
    private Integer id;
    private String name;
    private String gender;
    private Integer age;
    private String address;
    private String qq;
    private String email;
    private String username;
    private String phone;
//getter setter toString…
}
复制代码

3.mapper接口和映射配置


import tk.mybatis.mapper.common.Mapper;
import java.util.List;

public interface UserMapper extends Mapper<User> {
}

复制代码

4.编写测试代码

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper ;

    @Test
    public void findAll() {
        List<User> users = userMapper.selectAll();
        System.out.println(users);
    }
}

复制代码

3.业务层

1.编写接口

public interface UserService {

    /**
     * 查询所有用户信息
     * @return
     */
    public List<User> findAll();
}

复制代码

2.编写实现类


import com.dyy.dao.UserMapper;
import com.dyy.pojo.User;
import com.dyy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
private UserMapper userMapper;

@Override
@Transactional(readOnly = true ,propagation = Propagation.SUPPORTS)
    public List<User> findAll() {
        return userMapper.selectAll();
    }
}

复制代码

3.编写测试代码

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void findAll() {
        List<User> users = userService.findAll();
        System.out.println(users);
    }
}

复制代码

Spring Boot整合单元测试,需要在测试类上添加两个注解:

  1. @RunWith(SpringRunner.class)指定Junit核心运行类
  2. @SpringBootTest指定这是一个Spring Boot的测试类,运行时会自动加载Spring Boot运行环境

4.表现层

1.引入依赖

<!--Web起步依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--编码工具包-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

复制代码

2.新建工具类


import java.io.Serializable;

public class Result implements Serializable {
    private boolean status ; //响应状态  true  false
    private String msg ;  // 响应信息
    private Object data ;  //处理成功的响应数据

    public static Result ok(Object data){
        Result result = new Result();
        result.setStatus(true);
        result.setData(data);
        return  result ;
    }

    public static Result error(String msg){
        Result result = new Result();
        result.setStatus(false);
        result.setMsg(msg);
        return  result ;
    }
    
    // 生成set get tostring方法
    
}  

复制代码

3.编写表现层代码

@Controller
@RequestMapping(path = "/user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 查询所有用户信息
     * @return
     */
    @RequestMapping(path = "/findAll")
    @ResponseBody
    public Result findAll() {
        List<User> users = userService.findAll();
        return Result.ok(users);
    }
}

复制代码

4.代码测试

使用postman进行测试

5.页面展示

resources目录下创建static目录 , 创建页面展示、list.html:

  • 页面异步请求的端口和服务器端口一致
  • 页面异步请求访问的路径和对应的表现层控制方法路径要致
  • 页面异步请求参数名称和和对应的表现层控制方法参数一致

修改之后, 访问页面即可 : localhost:10001/list.html

6.缓存优化

1.缓存需求

问题:用户数据是不经常变化的数据,如果这些数据每次都去数据库中进行查询,效率比较低,对数据库造成很大的压力。

解决:缓存,用户第一次查询数据的时候,京数据存入到缓存中,之后再查询数据直接从缓存中获取即可,不需要再查询数据库。【此处使用redis缓存数据库存储数据】

2.依赖

<!--springboot整合redis启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

复制代码

3.编写配置文件

spring:
  redis: # 配置redis
    host: localhost
    port: 6379
复制代码

4.修改业务层实现类代码


import com.dyy.mapper.UserMapper;
import com.dyy.pojo.User;
import com.dyy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper ;
    @Autowired
    private RedisTemplate redisTemplate ;

    @Override
    @Transactional(readOnly = true ,propagation = Propagation.SUPPORTS)
    public List<User> findAll() {
        //从缓存中查询数据  规定存储用户信息使用string类型进行存储, 存储的key就是userList
        List<User> userList = (List<User>) redisTemplate.boundValueOps("userList").get();
        //如果缓存中没有数据, 查询数据库 , 将查询到的数据放入缓存
        if(userList==null){
            userList = userMapper.findAll();
            redisTemplate.boundValueOps("userList").set(userList);
            System.out.println("从数据库中查询...");
        }else {
            System.out.println("从缓存中查询.....");
        }

        //如果缓存中有数据, 直接返回
        return userList ;
    }
}

复制代码

七、SpringBoot其他组件

1.SpringBoot Actuator组件

作用

Spring Boot Actuator是SpringBoot自带的一个组件 , 可以帮助我们监控和管理Spring Boot应用,比如健康检查、审计、统计和HTTP追踪等。

如何使用

1.引入SpringBoot Actuator起步依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

复制代码
2.配置SpringBoot Actuator参数
management:
  endpoints:
    web:
      exposure:
        include: '*'  # 对外暴露的访问入口 , 默认是/health和/info
      base-path: /monitor # 默认是actuator
  endpoint:
    health:
      show-details: ALWAYS	# 显示所有健康状态
  server:
    port: 9999

复制代码
3.启动项目获取系统信息

目启动之后就可以通过发送http请求获取系统健康数据了 , 例如 : http://localhost:9999/monitor/health , 返回数据如下 :

{
    "status": "UP",
    "details": {
        "db": {
            "status": "UP",
            "details": {
                "database": "MySQL",
                "hello": 1
            }
        },
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 355816562688,
                "free": 129251151872,
                "threshold": 10485760
            }
        },
        "redis": {
            "status": "UP",
            "details": {
                "version": "2.8.9"
            }
        }
    }
}

复制代码

常用的访问路径

HTTP 方法 路径 描述
GET /autoconfig 提供了一份自动配置报告,记录哪些自动配置条件通过了,哪些没通过
GET /configprops 描述配置属性(包含默认值)如何注入Bean
GET /beans 描述应用程序上下文里全部的Bean,以及它们的关系
GET /dump 获取线程活动的快照
GET /env 获取全部环境属性
GET /env/{name} 根据名称获取特定的环境属性值
GET /health 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供
GET /info 获取应用程序的定制信息,这些信息由info打头的属性提供
GET /mappings 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系
GET /metrics 报告各种应用程序度量信息,比如内存用量和HTTP请求计数
GET /metrics/{name} 报告指定名称的应用程序度量值
POST /shutdown 关闭应用程序,要求endpoints.shutdown.enabled设置为true
GET /trace 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等)

我们可以通过发送这些请求,获取系统状态信息。

2.SpringBoot Admin组件

上面我们讲了Spring Boot Actuator , 可以通过http协议获取系统状态信息 , 但是返回的是JSON格式数据, 看起来不太方面, 而且还需要记忆路径, 比较麻烦 , Spring Boot Admin给我们提供了更加友好的可视化界面来查看这些信息 !

Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。 应用程序作为Spring Boot Admin ClientSpring Boot Admin Server注册 , Client会定时向Server发送数据, Server使用友好的界面展示数据。

1.SpringBoot Admin服务端

1.创建项目springboot-admin-server
2.依赖
<?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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>

    <groupId>com.dyy</groupId>
    <artifactId>springboot-admin-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>2.2.0</version>
        </dependency>
    </dependencies>
</project>

复制代码
3.配置application.yml
spring:
  application:
    name: admin-server
server:
  port: 8769

复制代码

我们将Spring Boot Admin端口号设置为8769.

4.启动类
@SpringBootApplication
@EnableAdminServer
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}
复制代码

@EnableAdminServer的作用是开启管理服务。

2.SpringBoot Admin客户端

1.创建springboot-admin-client
2.依赖
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.2.0</version>
</dependency>

复制代码
3.配置

向admin-server注册的地址为http://localhost:8769,最后暴漏自己的actuator所有的端口信息,具体配置如下:

server:
  port: 9999
spring:
  application:
    name: admin-client
  boot:
    admin:
      client:
        url: http://localhost:8769   # 指定注册地址 , Spring Boot Admin Server地址
management:
  endpoints:
    web:
      exposure:
        include: '*' 
  endpoint:
    health:
      show-details: ALWAYS
复制代码

这个注册的地址一定要和SpringBoot Admin Server地址匹配。

4.启动测试

分别开启客户端(应用程序)和服务端,访问http://localhost:8769,可以看到这样一个界面。

springboot-4.png

八、Spring Boot项目打包部署

1.项目打包

1.在pom中配置Spring Boot 项目的maven插件

<build>
    <plugins>
        <!-- 打jar包时如果不配置该插件,打出来的jar包没有清单文件 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

复制代码

2.运行maven的打包命令

3.打包之前我们需要跳过测试

如果不跳过测试,那么我们编写的测试类都会被maven自动执行,可能会出现错误,导致打包不成功。

springboot-5.png

4.执行之后可以在控制台看到打包的日志信息,其中有生成的包的位置

打开指定目录就可以发现有一个jar包存在 , 仔细观察其实我们会发现 , 在target目录下其实会存在二个jar包 , 一个是springboot_02-1.0-SNAPSHOT.jar一个是springboot_02-1.0-SNAPSHOT.jar.original , 那么这两个jar包有什么区别呢?

我们如果是普通项目打包那么就只会得到一个jar包 , 这个jar包中不包含项目的一些依赖jar包

但是我们现在是一个Spring Boot项目 , 我们希望打完的包能够直接运行, 所以项目中就必须包含他的依赖jar包 , 我们之前在pom.xml中配置一个Spring Boot的maven插件可以在普通包的基础上将我们项目的一些运行及依赖信息打进jar包里面 , 打完包之后将原来的普通包改名为xxx.jar.original , 新打的包为xxx.jar .

也就是说

  1. .jar.original 是普通jar包,不包含依赖
  2. .jar 是可执行jar包,包含了pom中的所有依赖,可以直接用java -jar 命令执行
  3. 如果是部署,就用.jar , 如果是给别的项目用,就要给.jar.original这个包

2.项目运行

打开命令行运行打出来的包:使用命令:java -jar包全名

java -jar springboot_02-1.0-SNAPSHOT.jar
复制代码

猜你喜欢

转载自juejin.im/post/7035519297287880734
今日推荐