Spring Boot 介绍

1. 什么是Spring Boot?

是一个JavaWeb的开发框架,和SpringMVC类似,对比其他JavaWeb框架的好处,官方说是简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码开发一个http接口。

随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring、更容易的集成各种常用的中间件、开源软件。

Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。
简单来说就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring Boot整合了所有的框架 。

2. Spring Boot的主要优点

  • 为所有Spring开发者更快的入门
  • 开箱即用,提供各种默认配置来简化项目配置
  • 内嵌式容器简化Web项目
  • 没有冗余代码生成和XML配置的要求

3. Spring Boot运行原理解析

3.1 pom.xml文件

SpringBoot主要是依赖一个父项目,主要目的是管理项目的资源过滤及插件。该父依赖如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

点进去,发现上述父依赖还有一个父依赖,如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.5.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

这里才是真正管理SpringBoot应用里面所有依赖版本的地方,即SpringBoot的版本控制中心。

以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了。

3.2 启动器 spring-boot-starter

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

<-- spring-boot-starter-web:导入了web模块正常运行所依赖的组件 -->

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter(启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来,我们要用什么功能仅需导入什么样的场景启动器即可;我们未来也可以自己自定义 starter。

3.3 主启动类注解

// @SpringBootApplication 来标注一个主程序类
// 说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringbootApplication {
   public static void main(String[] args) {
     // 以为是启动了一个方法,没想到启动了一个服务
      SpringApplication.run(SpringbootApplication.class, args);
   }
}

一个简单的启动类并不简单,虽仅一个注解,可该注解的上层存在多个注解。下一步我们来分析SpringBoot主启动类的注解@SpringBootApplication及其上层注解。

主启动类注解分析

(1)@SpringBootApplication

作用:标注在某个类上说明这个类是SpringBoot的主配置类 ,SpringBoot就应该通过运行这个类的main方法来启动SpringBoot应用。

点进@SpringBootApplication 注解

import...
// 标准注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

// SpringBoot独有注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    // ......
}

(2)@ComponentScan

这个注解在Spring中很重要 ,它扫描XML配置中的类或者注解标记的类。作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中。

(3)@SpringBootConfiguration

作用:标注在某个类上,表示该类这是一个SpringBoot的配置类。

点进去:

// 点进@Configuration得到下面的 @Component
@Configuration
public @interface SpringBootConfiguration {}

@Component
public @interface Configuration {}

这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!

(4)@EnableAutoConfiguration

作用:开启自动配置功能。

以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效。

点进去:

package org.springframework.boot.autoconfigure;
import...
// 标准注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

// 独有注解
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@AutoConfigurationPackage:自动配置包

点进去:

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

// @import 是Spring底层注解
// 作用:给容器中导入一个组件。
// Registrar.class
// 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器。
// 所以 Spring Boot 程序的主启动类要放在最外层的包下

@Import({AutoConfigurationImportSelector.class}) :给容器导入AutoConfigurationImportSelector(自动配置导入选择器) 这个组件,点进这个类:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 这里的getSpringFactoriesLoaderFactoryClass()方法
        // 返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

}

// 进入SpringFactoriesLoader.loadFactoryNames方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        // 这里它又调用了 loadSpringFactories 方法
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    // 获得classLoader,我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            // 去获取一个资源 "META-INF/spring.factories"
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            // 将读取到的资源遍历,封装成为一个Properties
            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 factoryClassName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

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

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

发现一个多次出现的文件:spring.factories,全局搜索并打开它;看到了很多自动配置的文件,该文件即spring.factories就是自动配置根源所在

 找一个打开的spring.factories文件中的自动配置类,比如 :WebMvcAutoConfiguration

可以发现WebMvcAutoConfiguration是JavaConfig配置类,而且都注入了一些Bean,其余的自动配置类也个个的都是JavaConfig配置类。

结论:

自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了@Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。

3.4 SpringApplication.run(…)方法分析

// SpringbootApplication为主启动类名
@SpringBootApplication
public class SpringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }
}

该方法主要分两部分:

一是SpringApplication的实例化,即传入该主启动类SpringbootApplication.class后,再对SpringApplication实例化。

二是run方法的执行。

3.4.1 类SpringApplication作用

// 构造方法
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    // ......
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances();
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}
  • 推断应用的类型是普通的项目还是Web项目
  • 查找并加载所有可用初始化器 , 设置到initializers属性中
  • 找出所有的应用程序监听器,设置到listeners属性中
  • 推断并设置main方法的定义类,找到运行的主类

3.4.2 run方法流程分析

 

3.5 自动配置原理

SpringBoot官方文档中有大量的配置,我们不可能手动注入这些配置(过于麻烦低效),因此SpringBoot提供自动配置这些配置文件的能力,仅需在需要配置的类上加上@ConfigurationProperties(prefix = "下述配置的名字")。

3.5.1 分析自动配置原理

以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理:

// 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration 

// 启动指定类的ConfigurationProperties功能;
// 进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
// 并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({HttpProperties.class}) 

// Spring底层@Conditional注解
// 根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
// 这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
    type = Type.SERVLET
)

// 判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass({CharacterEncodingFilter.class})

// 判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
// 如果不存在,判断也是成立的
// 即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)

public class HttpEncodingAutoConfiguration {
    // 已经和SpringBoot的配置文件映射了
    private final Encoding properties;
    // 只有一个有参构造器的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(HttpProperties properties) {
        this.properties = properties.getEncoding();
    }
    
    // 给容器中添加一个组件,这个组件的某些值需要从properties中获取
    @Bean
    @ConditionalOnMissingBean // 判断容器有没有这个组件?
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name());
        filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
        return filter;
    }
    //。。。。。。。
}

总结 :

  • SpringBoot根据当前不同的条件判断,决定这个配置类是否生效。
  • 一但这个配置类生效;这个配置类就会给容器中添加各种组件;
  • 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的(xx.properties)
  • 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
  • 配置文件能配置什么就可以参照某个功能对应的这个属性类。

例如:自动配置spring.http

// 从配置文件中获取指定的值和bean的属性进行绑定
@ConfigurationProperties(prefix = "spring.http") 
public class HttpProperties {
    // .....
}

结果如下:

 

3.5.2 SpringBoot自动配置流程

1. SpringBoot启动会加载大量的自动配置类;

2. 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;

3. 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)

4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可。

xxxxAutoConfigurartion:自动配置类,给容器中添加组件。
xxxxProperties:封装配置文件中相关属性。

4. MVC自动配置原理

4.1 官方文档阅读

SpringBoot对SpringMVC到底做了哪些配置,包括如何扩展,如何定制?

Spring MVC Auto-configuration
// Spring Boot为Spring MVC提供了自动配置,它可以很好地与大多数应用程序一起工作。
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.

// 自动配置在Spring默认设置的基础上添加了以下功能:
The auto-configuration adds the following features on top of Spring’s defaults:
// 包含视图解析器
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
// 支持静态资源文件夹的路径,以及webjars
Support for serving static resources, including support for WebJars 
// 自动注册了Converter:
// 转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把"1"字符串自动转换为int类型
// Formatter:【格式化器,比如页面给我们了一个2019-8-10,它会给我们自动格式化为Date对象】
Automatic registration of Converter, GenericConverter, and Formatter beans.

// HttpMessageConverters
// SpringMVC用来转换Http请求和响应的的,比如我们要把一个User对象转换为JSON字符串,可以去看官网文档解释;
Support for HttpMessageConverters (covered later in this document).
// 定义错误代码生成规则的
Automatic registration of MessageCodesResolver (covered later in this document).
// 首页定制
Static index.html support.
// 图标定制
Custom Favicon support (covered later in this document).
// 初始化数据绑定器:帮我们把请求数据绑定到JavaBean中!
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

/*
如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己
的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。如果希望提供
RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义
实例,则可以声明WebMVCregistrationAdapter实例来提供此类组件。
*/
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration 
(interceptors, formatters, view controllers, and other features), you can add your own 
@Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide 
custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or 
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

// 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc进行注释。
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

4.2 ContentNegotiatingViewResolver 内容协商视图解析器

ContentNegotiatingViewResolver即内容协商视图解析器在上述官方文档中位列第一个功能,可见其重要性,其实它就是SpringMVC中的视图解析器,即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。

在配置类WebMvcAutoConfiguration中ContentNegotiatingViewResolver源码如下:

@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
    // ContentNegotiatingViewResolver使用所有其他视图解析器来定位视图,因此它应该具有较高的优先级
    resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return resolver;
}

// 点击进入ContentNegotiatingViewResolver类,查看其源码:
public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered, InitializingBean {
    // ......
    @Nullable
    private List<ViewResolver> viewResolvers;

    @Nullable // 注解说明:@Nullable 即参数可为null
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
        List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
        if (requestedMediaTypes != null) {
            // 获取候选的视图对象
            List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
            // 选择一个最适合的视图对象,然后把这个对象返回
            View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
            if (bestView != null) {
                return bestView;
            }
        }
        // .....
    }
}


// ContentNegotiatingViewResolver是怎样获得候选的视图的呢?
// 点击进入getCandidateViews,查看如何获得候选的视图。
Iterator var5 = this.viewResolvers.iterator(); 
// 可得出ContentNegotiatingViewResolver是把所有的视图解析器拿来,进行while循环,挨个解析!
// 结论:ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的 。


// ContentNegotiatingViewResolver类中存在一个属性为viewResolvers,查看它是在哪里进行赋值的。
protected void initServletContext(ServletContext servletContext) {
    // 这里它是从beanFactory工具中获取容器中的所有视图解析器
    // ViewRescolver.class 把所有的视图解析器来组合的
    Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(),
ViewResolver.class).values();
    ViewResolver viewResolver;
    if (this.viewResolvers == null) {
        this.viewResolvers = new ArrayList(matchingBeans.size());
    }
    // ...............
 }
// 结论:
// ContentNegotiatingViewResolver是在容器中去找视图解析器,那么我们可以自己给容器中去添加一个视图解析器
// ContentNegotiatingViewResolver就会帮我们自动的将它组合进来。 


4.3 转换器和格式化器

@Bean
@Override
public FormattingConversionService mvcConversionService() {
    // 拿到配置文件中的格式化规则
    WebConversionService conversionService = 
        new WebConversionService(this.mvcProperties.getDateFormat());
    addFormatters(conversionService);
    return conversionService;
}

// 点进getDateFormat()方法
public String getDateFormat() {
    return this.dateFormat;
}

/**
* Date format to use. For instance, `dd/MM/yyyy`. 默认的
 */
private String dateFormat;

可以看到在我们的Properties文件中,我们可以进行自动配置它!

如果配置了自己的格式化方式,就会注册到Bean中生效,我们可以在配置文件中配置日期格式化的规则:

4.4 修改SpringBoot的默认配置

SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(用户自己配置用@bean注解标识),如果有就用用户配置的,如果没有就用自动配置的,即用户自定义组件比自动配置组件优先级高。

如果某些组件可以存在多个,比如我们的视图解析器,. SpringBoot就将用户配置的组件和自己默认的组件组合起来。

扩展使用SpringMVC 官方文档如下:

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, viewcontrollers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

即我们要做的就是编写一个@Configuration注解类,并且类型要为WebMvcConfigurer,还不能标注@EnableWebMvc注解。


// 应为类型要求为WebMvcConfigurer,所以我们实现其接口
// 可以使用自定义类扩展MVC的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 浏览器发送/test , 就会跳转到test页面;
        registry.addViewController("/test").setViewName("test");
    }
}

4.5 全面接管SpringMVC

官方文档描述如下:

If you want to take complete control of Spring MVC ,
you can add your own @Configuration annotated with @EnableWebMvc.

全面接管即:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己去配置,只需在我们的配置类中要加一个@EnableWebMvc。

注意:若全面接管了SpringMVC,则所有的SpringMVC自动配置都失效,包括且不限于静态资源映射,开发中开发中不推荐使用全面接管SpringMVC。

@EnableWebMvc注解解析

// @EnableWebMvc
// 发现它导入了一个类,DelegatingWebMvcConfiguration
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

// 点进DelegatingWebMvcConfiguration
// 发现其继承WebMvcConfigurationSupport 
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
  // ......
}

// 回顾Webmvc自动配置类(WebMvcAutoConfiguration)。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
// 这个注解的意思就是:容器中没有这个组件(WebMvcConfigurationSupport)的时候,这个自动配置类才生效
// 而加入@EnableWebMvc注解导入了WebMvcConfigurationSupport的子类
// @WebMvcAutoConfiguration注解失效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
    ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    
}
// 总结一句话:@EnableWebMvc将WebMvcConfigurationSupport组件导入进来了;而导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能。

5. 补充Spring Boot 相关知识

5.1 SpringBoot加载指定的配置文件

@PropertySource :加载指定的配置文件。
@configurationProperties:默认从全局配置文件中获取值。

@Value与@ConfigurationProperties比较 

@Value,作用:直接注入某一属性的实值。
@ConfigurationProperties,作用:注入配置文件(注入配置文件中已配置的实值)。

  1. @ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加,即需要为每个属性单独注解赋值。
  2. 松散绑定,比如我的yml中写的last-name,这个和lastName是等价的, 即后面跟着的字母默认是大写的。
  3. JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性。
  4. 复杂类型封装,@ConfigurationProperties注入的yaml文件中可以封装对象 , 使用@value就不支持。

5.2 JSR303数据校验

SpringBoot中可以用注解 @validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。

@Component // 注册bean
@ConfigurationProperties(prefix = "person")
@Validated  // 数据校验
public class Person {

    @Email(message="邮箱格式错误") // name必须是邮箱格式
    private String name;
}

常见的注解:

@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;

空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.
    
Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
    
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.

日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

.......等等
除此以外,我们还可以自定义一些数据校验规则

5.3 多环境切换

参数profileSpring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境。

例如:application-test.properties 代表测试环境配置
application-dev.properties代表开发环境配置

注意:Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件。

我们需要通过一个配置来选择需要激活的环境

#比如在配置文件中指定使用dev环境,
#我们们可以通过设置不同的端口号进行测试;
#我们启动SpringBoot,就可以看到已经切换到dev下的配置了; 
 spring.profiles.active=dev 

5.4 @Conditional注解

注解作用:自动配置类必须在一定的条件下才能生效。

@Conditional派生注解

作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效。

我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效:

#开启springboot的调试类
debug=true

5.5 整合JDBC

新建一个项目测试:springboot-data-jdbc ; 引入相应的基础模块。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

HikariDataSource数据源

全局搜索一下该数据源,发现该数据源的所有自动配置都在 DataSourceAutoConfiguration文件:

@Configuration
	@Conditional(PooledDataSourceCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
			DataSourceJmxConfiguration.class })
	protected static class PooledDataSourceConfiguration {

	}
	/**
	 * Hikari DataSource configuration.
	 */
	@Configuration
	@ConditionalOnClass(HikariDataSource.class)
	@ConditionalOnMissingBean(DataSource.class)
	@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
			matchIfMissing = true)
	static class Hikari {

		@Bean
		@ConfigurationProperties(prefix = "spring.datasource.hikari")
		public HikariDataSource dataSource(DataSourceProperties properties) {
			HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
			if (StringUtils.hasText(properties.getName())) {
				dataSource.setPoolName(properties.getName());
			}
			return dataSource;
		}

	}

这里导入的类都在 DataSourceConfiguration 配置类下,可以看出 Spring Boot 2.2.5 默认使用HikariDataSource 数据源,而以前版本,如 Spring Boot 1.5 默认使用 org.apache.tomcat.jdbc.pool.DataSource 作为数据源。

HikariDataSource 号称 Java WEB 当前速度最快的数据源,相比于传统的 C3P0 、DBCP、Tomcat jdbc 等连接池更加优秀。

JDBCTemplate

1. 有了数据源(com.zaxxer.hikari.HikariDataSource),然后可以拿到数据库连接(java.sql.Connection),有了连接,就可以使用原生的 JDBC 语句来操作数据库。

2. 即使不使用第三方第数据库操作框架,如 MyBatis等,Spring 本身也对原生的JDBC 做了轻量级的封装,即JdbcTemplate。

3. 数据库操作的所有 CRUD 方法都在 JdbcTemplate 中。

4. Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,程序员只需自己注入即可使用。

5. JdbcTemplate 的自动配置是依赖org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 类

JdbcTemplate主要提供以下几类方法:

execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句。
update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句。
query方法及queryForXXX方法:用于执行查询相关语句。
call方法:用于执行存储过程、函数相关语句。

5.6 整合Druid 

  • Java程序很大一部分要操作数据库,为了提高性能操作数据库的时候,又不得不使用数据库连接池。
  • Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。
  • Druid 可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。
  • Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。
  • Spring Boot 2.0 以上默认使用 Hikari 数据源,可以说 Hikari 与 Driud 都是当前 Java Web上最优秀的数据源,我们来重点介绍 Spring Boot 如何集成 Druid 数据源,如何实现数据库监控。

基本配置参数如下:

切换数据源:

之前已经说过 Spring Boot 2.0 以上默认使用 com.zaxxer.hikari.HikariDataSource 数据源,但可以 通过 spring.datasource.type 指定数据源。

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源

配置Druid数据源监控:

Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看。

第一步需要设置 Druid 的后台管理页面,比如 登录账号、密码 等;配置后台管理;

// 配置 Druid 监控管理后台的Servlet;
// 内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

    // 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet 
    // 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
    Map<String, String> initParams = new HashMap<>();
    initParams.put("loginUsername", "admin"); //后台管理界面的登录账号
    initParams.put("loginPassword", "123456"); //后台管理界面的登录密码

    // 后台允许谁可以访问
    // initParams.put("allow", "localhost"):表示只有本机可以访问
    // initParams.put("allow", ""):为空或者为null时,表示允许所有访问
    initParams.put("allow", "");
    // deny:Druid 后台拒绝谁访问
    // initParams.put("kuangshen", "192.168.1.20");表示禁止此ip访问

    // 设置初始化参数
    bean.setInitParameters(initParams);
    return bean;
}

第二步配置 Druid web 监控 filter 过滤器

// 配置 Druid 监控 之  web 监控的 filter
// WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
@Bean
public FilterRegistrationBean webStatFilter() {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setFilter(new WebStatFilter());

    // exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
    Map<String, String> initParams = new HashMap<>();
    initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
    bean.setInitParameters(initParams);

    // "/*" 表示过滤所有请求
    bean.setUrlPatterns(Arrays.asList("/*"));
    return bean;
}

参考:SpringBoot笔记_皮皮虾男友的博客-CSDN博客_spring boot笔记 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值