Springboot基础(一)

SpringBoot

学习目标

1.掌握原理,自己手写starter

2.yaml语法,操作数据库

3.连接前端

4.补充技能,前后端分离,swagger, 异步,富文本编辑器

5.异步任务,定时任务,邮件任务等等

  1. src 路径下的文件 在编译后都会放到 WEB-INF/classes 路径下。默认classpath 就是指这里。
  2. 用maven构建 项目时,resources 目录就是默认的classpath

什么是spring

现代化的java开发主要就是面向Spring开发:

Spring是一个开源框架,2003年起来的,可以简化我们的企业级开发

Spring是如何简化开发的

1.java bean 代表原来要一个个new的对象

2.IOC DI 控制反转,所有的对象都是从容器中获取的

3.AOP 面向切面编程,降低入侵性

4.xxxTemplate: 原来手动需要编写步骤,比如jdbcTemplate ,现在直接使用注解即可

什么是SpringBoot

SpringBoot: 自动配置简化开发

​ 1.自动配置

​ 2.不是一个新东西,是spring的增强版

​ 3.约定大于配置(
开发人员仅需规定应用中不符合约定的部分
在没有规定配置的地方,采用默认配置,以力求最简配置为核心思想上面
两条都遵循了推荐默认配置的思想。当存在特殊需求的时候,自定义配置即可。这样可以大大的减少配置工作,这就是所谓的“约定”。)

​ 4.继承了市面上所有常见的依赖,而且帮你自动管理版本依赖(可以从maven依赖库中寻找,地址是:https://mvnrepository.com/)

​ 5.内置web容器

构建一个springBoot应用

1.new Project–Spring initialize --> next等

2.启动测试 有一个默认请求error

对比一下传统的开发

SSM框架:

​ 1.tomcat

​ 2.web.xml中注册springmvc

​ 3.添加或者扫描我们需要的依赖是否存在,不存在就需要配置

​ 4.编写spring springmvc mybatis等的配置

​ 5.编写无关的业务代码 控制器等

​ 6.整体框架搭建完毕之后,才开始梳理编写业务

​ 7.测试,调整

SpringBoot:

​ 1.构建项目 一键生成

​ 2.编写业务

​ 3.启动测试

如何发布

1.使用package打包即可 需要maven依赖

初探原理

分析思路: 只要是maven项目,都需要先分析pom.xml

pom.xml中,parent是父依赖,然后下一个是gav坐标 然后下面是具体的依赖 build是插件

1.父依赖:

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

点击进去之后就可以看到:z这就是springboot的配置文件

<includes>
          <include>**/application*.yml</include>
          <include>**/application*.yaml</include>
          <include>**/application*.properties</include>
        </includes>

我们发现这个还有一个父依赖 这个是版本控制和依赖控制中心,以后导入的依赖都不需要编写版本号了,如果自动依赖配置中不存在的话,物品,恶魔才需要手动导入

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

2.spring-boot-starter启动器

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

作用:

​ 1.导入依赖

​ 2.存在哪些启动器:https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/html/using-spring-boot.html#using-boot-starter

3.插件

<!-- 这个插件,可以将应用打包成一个可执行的jar包;-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

启动类

@SpringBootApplication
public class SpringbootFirstApplication {

    public static void main(String[] args) {

        SpringApplication.run(SpringbootFirstApplication.class, args);
        System.out.println("hello wprld");
    }

核心:一个注解,一个方法 是使用图来画

@SpringBootApplication注解

作用:标注了这个主角,就代表这是我们的一个程序的启动类, 是一个配置类,一个组件

java中元注解有四个: @Retention @Target @Document @Inherited;

@Retention:注解的保留位置

@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含

@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,

@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Target:注解的作用目标

@Target(ElementType.TYPE) //接口、类、枚举

@Target(ElementType.FIELD) //字段、枚举的常量

@Target(ElementType.METHOD) //方法

@Target(ElementType.PARAMETER) //方法参数

@Target(ElementType.CONSTRUCTOR) //构造函数

@Target(ElementType.LOCAL_VARIABLE)//局部变量

@Target(ElementType.ANNOTATION_TYPE)//注解

@Target(ElementType.PACKAGE) ///包

 @Document:说明该注解将被包含在javadoc中    @Inherited:说明子类可以继承父类中的该注解
@Target({ElementType.TYPE})  //接口、类、枚举
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到@Target:注解的作用目标   
@Documented  说明该注解将被包含在javadoc中
@Inherited  说明子类可以继承父类中的该注解
@SpringBootConfiguration  是一个配置类
@EnableAutoConfiguration
@ComponentScan(//扫描当前类所在的包中的下面的所有的bean,然后将这些bean注入到容器中
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
  1. @SpringBootConfiguration 是一个配置类 是一个组件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

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

2.@EnableAutoConfiguration 自动配置的注解 是一个重点

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
 
    
AutoConfigurationPackage 注解:
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }
		//注册了一个包名,表示的是主要是在这个包名下面就会被注册到容器中
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
        }
    }
    
    
@Import({AutoConfigurationImportSelector.class})

   public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
       
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            //重点
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

如何分析一个类: 构造器和初始化常用方法 ,如果有继承,就重点查看重写的方法

在这里插入图片描述

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        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;
    }

在这里插入图片描述

META-INF/spring.factories

我们可以找到这个文件在
在这里插入图片描述

然后打开这个文件,就可以i看到自动配置的类

在这里插入图片描述

我们可以点击一个配置类:

在这里插入图片描述

那么这个文件是如何作用的呢?

主配置类中的run方法

首先找到SpringApplication这个类,然后看这个类的构造方法:

构造器

 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //推断当前的应用是否是web应用
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
     //加载初始化器
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
       //设置监听器
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //推断main方法
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

SpringApplication:

​ 1.推断当前的应用是否是web应用

​ 2.加载初始化器

​ 3.设置监听器

​ 4.推断main方法

/**
 2  * Run the Spring application, creating and refreshing a new
 3  * {@link ApplicationContext}.
 4  *
 5  * @param args the application arguments (usually passed from a Java main method)
 6  * @return a running {@link ApplicationContext}
 7  *
 8  * 运行spring应用,并刷新一个新的 ApplicationContext(Spring的上下文)
 9  * ConfigurableApplicationContext 是 ApplicationContext 接口的子接口。在 ApplicationContext
10  * 基础上增加了配置上下文的工具。 ConfigurableApplicationContext是容器的高级接口
11  */
12 public ConfigurableApplicationContext run(String... args) {
13     //记录程序运行时间
14     StopWatch stopWatch = new StopWatch();
15     stopWatch.start();
16     // ConfigurableApplicationContext Spring 的上下文
17     ConfigurableApplicationContext context = null;
18     Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
19     configureHeadlessProperty();
20     //从META-INF/spring.factories中获取监听器
21     //1、获取并启动监听器
22     SpringApplicationRunListeners listeners = getRunListeners(args);
23     listeners.starting();
24     try {
25         ApplicationArguments applicationArguments = new DefaultApplicationArguments(
26                 args);
27         //2、构造应用上下文环境
28         ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
29         //处理需要忽略的Bean
30         configureIgnoreBeanInfo(environment);
31         //打印banner  也可以自定义启动logo,比如在resources路径下创建一个banner.txt文件,将你想打印的图标放入其中
32         Banner printedBanner = printBanner(environment);
33         ///3、初始化应用上下文
34         context = createApplicationContext();
35         //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
36         exceptionReporters = getSpringFactoriesInstances(
37                 SpringBootExceptionReporter.class,
38                 new Class[]{ConfigurableApplicationContext.class}, context);
39         //4、刷新应用上下文前的准备阶段
40         prepareContext(context, environment, listeners, applicationArguments, printedBanner);
41         //5、刷新应用上下文
42         refreshContext(context);
43         //刷新应用上下文后的扩展接口
44         afterRefresh(context, applicationArguments);
45         //时间记录停止
46         stopWatch.stop();
47         if (this.logStartupInfo) {
48             new StartupInfoLogger(this.mainApplicationClass)
49                     .logStarted(getApplicationLog(), stopWatch);
50         }
51         //发布容器启动完成事件
52         listeners.started(context);
53         callRunners(context, applicationArguments);
54     } catch (Throwable ex) {
55         handleRunFailure(context, ex, exceptionReporters, listeners);
56         throw new IllegalStateException(ex);
57     }
58 
59     try {
60         listeners.running(context);
61     } catch (Throwable ex) {
62         handleRunFailure(context, ex, exceptionReporters, null);
63         throw new IllegalStateException(ex);
64     }
65     return context;
66 }

具体的每一行代码的含义请看注释,我们在这先总结一下启动过程中的重要步骤:

第一步:获取并启动监听器
第二步:构造应用上下文环境
第三步:初始化应用上下文
第四步:刷新应用上下文前的准备阶段
第五步:刷新应用上下文
第六步:刷新应用上下文后的扩展接口

第一步:获取并启动监听器

事件机制在Spring是很重要的一部分内容,通过事件机制我们可以监听Spring容器中正在发生的一些事件,同样也可以自定义监听事件。Spring的事件为Bean和Bean之间的消息传递提供支持。当一个对象处理完某种任务后,通知另外的对象进行某些处理,常用的场景有进行某些操作后发送通知,消息、邮件等情况。

1 private SpringApplicationRunListeners getRunListeners(String[] args) {
2     Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
3     return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
4             SpringApplicationRunListener.class, types, this, args));
5 }

在这里面是不是看到一个熟悉的方法:getSpringFactoriesInstances(),可以看下下面的注释,前面的博文我们已经详细介绍过该方法是怎么一步步的获取到META-INF/spring.factories中的指定的key的value,获取到以后怎么实例化类的。

(javascript:void(0)😉在这里插入图片描述

 1 /**
 2  * 通过指定的classloader 从META-INF/spring.factories获取指定的Spring的工厂实例
 3  * @param type
 4  * @param parameterTypes
 5  * @param args
 6  * @param <T>
 7  * @return
 8  */
 9 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
10                                                       Class<?>[] parameterTypes, Object... args) {
11     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
12     // Use names and ensure unique to protect against duplicates
13     //通过指定的classLoader从 META-INF/spring.factories 的资源文件中,
14     //读取 key 为 type.getName() 的 value
15     Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
16     //创建Spring工厂实例
17     List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
18             classLoader, args, names);
19     //对Spring工厂实例排序(org.springframework.core.annotation.Order注解指定的顺序)
20     AnnotationAwareOrderComparator.sort(instances);
21     return instances;
22 }

[复制代码](javascript:void(0)😉

回到run方法,debug这个代码 SpringApplicationRunListeners listeners = getRunListeners(args); 看一下获取的是哪个监听器:

在这里插入图片描述

EventPublishingRunListener监听器是Spring容器的启动监听器。

listeners.starting(); 开启了监听事件。

四、第二步:构造应用上下文环境

应用上下文环境包括什么呢?包括计算机的环境,Java环境,Spring的运行环境,Spring项目的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等等。

首先看一下prepareEnvironment()方法。

[复制代码](javascript:void(0)😉

 1 private ConfigurableEnvironment prepareEnvironment(
 2         SpringApplicationRunListeners listeners,
 3         ApplicationArguments applicationArguments) {
 4     // Create and configure the environment
 5     //创建并配置相应的环境
 6     ConfigurableEnvironment environment = getOrCreateEnvironment();
 7     //根据用户配置,配置 environment系统环境
 8     configureEnvironment(environment, applicationArguments.getSourceArgs());
 9     // 启动相应的监听器,其中一个重要的监听器 ConfigFileApplicationListener 就是加载项目配置文件的监听器。
10     listeners.environmentPrepared(environment);
11     bindToSpringApplication(environment);
12     if (this.webApplicationType == WebApplicationType.NONE) {
13         environment = new EnvironmentConverter(getClassLoader())
14                 .convertToStandardEnvironmentIfNecessary(environment);
15     }
16     ConfigurationPropertySources.attach(environment);
17     return environment;
18 }

[复制代码](javascript:void(0)😉

看上面的注释,方法中主要完成的工作,首先是创建并按照相应的应用类型配置相应的环境,然后根据用户的配置,配置系统环境,然后启动监听器,并加载系统配置文件。

4.1、 ConfigurableEnvironment environment = getOrCreateEnvironment();

看看getOrCreateEnvironment()干了些什么。

[复制代码](javascript:void(0)😉

 1 private ConfigurableEnvironment getOrCreateEnvironment() {
 2     if (this.environment != null) {
 3         return this.environment;
 4     }
 5     //如果应用类型是 SERVLET 则实例化 StandardServletEnvironment
 6     if (this.webApplicationType == WebApplicationType.SERVLET) {
 7         return new StandardServletEnvironment();
 8     }
 9     return new StandardEnvironment();
10 }

[复制代码](javascript:void(0)😉

通过代码可以看到根据不同的应用类型初始化不同的系统环境实例。前面咱们已经说过应用类型是怎么判断的了,这里就不在赘述了。

在这里插入图片描述

从上面的继承关系可以看出,StandardServletEnvironment是StandardEnvironment的子类。这两个对象也没什么好讲的,当是web项目的时候,环境上会多一些关于web环境的配置。

4.2、 configureEnvironment(environment, applicationArguments.getSourceArgs());

[复制代码](javascript:void(0)😉

1 protected void configureEnvironment(ConfigurableEnvironment environment,
2                                     String[] args) {
3     // 将main 函数的args封装成 SimpleCommandLinePropertySource 加入环境中。
4     configurePropertySources(environment, args);
5     // 激活相应的配置文件
6     configureProfiles(environment, args);
7 }

[复制代码](javascript:void(0)😉

在执行完方法中的两行代码后,debug的截图如下

在这里插入图片描述

如下图所示,我在spring的启动参数中指定了参数:–spring.profiles.active=prod(关于这个参数的用法,点我,其实就是启动多个实例用的)

在这里插入图片描述

在configurePropertySources(environment, args);中将args封装成了SimpleCommandLinePropertySource并加入到了environment中。

configureProfiles(environment, args);根据启动参数激活了相应的配置文件。

话不多说,debug一遍就明白了。

4.3、 listeners.environmentPrepared(environment);

进入到方法一路跟下去就到了SimpleApplicationEventMulticaster类的multicastEvent()方法。

在这里插入图片描述

在这里插入图片描述

查看getApplicationListeners(event, type)执行结果,发现一个重要的监听器ConfigFileApplicationListener。

先看看这个类的注释

[复制代码](javascript:void(0)😉

 1 /**
 2  * {@link EnvironmentPostProcessor} that configures the context environment by loading
 3  * properties from well known file locations. By default properties will be loaded from
 4  * 'application.properties' and/or 'application.yml' files in the following locations:
 5  * <ul>
 6  * <li>classpath:</li>
 7  * <li>file:./</li>
 8  * <li>classpath:config/</li>
 9  * <li>file:./config/:</li>
10  * </ul>
11  * <p>
12  * Alternative search locations and names can be specified using
13  * {@link #setSearchLocations(String)} and {@link #setSearchNames(String)}.
14  * <p>
15  * Additional files will also be loaded based on active profiles. For example if a 'web'
16  * profile is active 'application-web.properties' and 'application-web.yml' will be
17  * considered.
18  * <p>
19  * The 'spring.config.name' property can be used to specify an alternative name to load
20  * and the 'spring.config.location' property can be used to specify alternative search
21  * locations or specific files.
22  * <p>
23  * 从默认的位置加载配置文件,并将其加入 上下文的 environment变量中
24  */

[复制代码](javascript:void(0)😉

这个监听器默认的从注释中

  • 标签所示的几个位置加载配置文件,并将其加入 上下文的 environment变量中。当然也可以通过配置指定。

debug跳过 listeners.environmentPrepared(environment); 这一行,查看environment属性,果真如上面所说的,配置文件的配置信息已经添加上来了。

在这里插入图片描述

五、第三步:初始化应用上下文

在SpringBoot工程中,应用类型分为三种,如下代码所示。

[复制代码](javascript:void(0)😉

 1 public enum WebApplicationType {
 2     /**
 3      * 应用程序不是web应用,也不应该用web服务器去启动
 4      */
 5     NONE,
 6     /**
 7      * 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。
 8      */
 9     SERVLET,
10     /**
11      * 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。
12      */
13     REACTIVE
14 }

[复制代码](javascript:void(0)😉

对应三种应用类型,SpringBoot项目有三种对应的应用上下文,我们以web工程为例,即其上下文为AnnotationConfigServletWebServerApplicationContext。

[复制代码](javascript:void(0)😉

 1 public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
 2         + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
 3 public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
 4         + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
 5 public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
 6         + "annotation.AnnotationConfigApplicationContext";
 7         
 8 protected ConfigurableApplicationContext createApplicationContext() {
 9     Class<?> contextClass = this.applicationContextClass;
10     if (contextClass == null) {
11         try {
12             switch (this.webApplicationType) {
13                 case SERVLET:
14                     contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
15                     break;
16                 case REACTIVE:
17                     contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
18                     break;
19                 default:
20                     contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
21             }
22         } catch (ClassNotFoundException ex) {
23             throw new IllegalStateException(
24                     "Unable create a default ApplicationContext, "
25                             + "please specify an ApplicationContextClass",
26                     ex);
27         }
28     }
29     return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
30 }

[复制代码](javascript:void(0)😉

我们先看一下AnnotationConfigServletWebServerApplicationContext的设计。

在这里插入图片描述

关于他的继承体系,我们在前面的博客中<Spring IoC容器与应用上下文的设计与实现>已经详细介绍了,在此不再赘述。

应用上下文可以理解成IoC容器的高级表现形式,应用上下文确实是在IoC容器的基础上丰富了一些高级功能。

应用上下文对IoC容器是持有的关系。他的一个属性beanFactory就是IoC容器(DefaultListableBeanFactory)。所以他们之间是持有,和扩展的关系。

接下来看GenericApplicationContext类

[复制代码](javascript:void(0)😉

1 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
2     private final DefaultListableBeanFactory beanFactory;
3     ...
4     public GenericApplicationContext() {
5         this.beanFactory = new DefaultListableBeanFactory();
6     }
7     ...
8 }

[复制代码](javascript:void(0)😉

beanFactory正是在AnnotationConfigServletWebServerApplicationContext实现的接口GenericApplicationContext中定义的。在上面createApplicationContext()方法中的, BeanUtils.instantiateClass(contextClass) 这个方法中,不但初始化了AnnotationConfigServletWebServerApplicationContext类,也就是我们的上下文context,同样也触发了GenericApplicationContext类的构造函数,从而IoC容器也创建了。仔细看他的构造函数,有没有发现一个很熟悉的类DefaultListableBeanFactory,没错,DefaultListableBeanFactory就是IoC容器真实面目了。在后面的refresh()方法分析中,DefaultListableBeanFactory是无处不在的存在感。

debug跳过createApplicationContext()方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OLX7pbFe-1586245906728)(D:\艾编程的高级课程\资料\3.14 SpringBoot入门\springboot基础.assets\1635748-20190613154908779-1708037422.png)]

如上图所示,context就是我们熟悉的上下文(也有人称之为容器,都可以,看个人爱好和理解),beanFactory就是我们所说的IoC容器的真实面孔了。细细感受下上下文和容器的联系和区别,对于我们理解源码有很大的帮助。在系列文章中,我们也是将上下文和容器严格区分开来的。

YAML学习

Springboot默认使用全局的配置文件:

​ application*.properties key=value

​ application*.yaml/application*.yml key: 空格 value

配置文件的作用,修改springboot自动配置的默认值,但是其实很多配置的springboot都已经配置好了

yaml语法

YAML是"YAML Ain’t a Markup Language"(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。

注入配置文件

加载指定的配置文件

@PropertySource("classpath:application.yaml") //加载资源的注解

yaml配置文件可以编写一些随机数, 占位符

person:
  lastName: 张三${random.int}
  
三元运算符也是支持的
dog:
     name: ${person.boss:true}12  如果存在这个值就是这个值,如果不存在的话就是true

在springboot底层所有的配置类都使用的是@ConfigurationProperties

最佳实践

1.假设配置类十分简单,就是使用@Value 如果只获取个别值,那么@Value 依旧是最高效的

2.配置繁琐的话,使用@ConfigurationProperties

多环境配置

什么是多环境配置

在真是开发的时候,配置文件一定不止一个,比如:

application-dev.yaml

application-test.yaml

application-prod.yaml

传统的多环境配置

spring.profiles.active=dev   表示启动的是application-dev.properties文件 
spring.profiles.active=test   表示启动的是application-test.properties文件 

使用yaml多环境配置

不需要要多个配置文件,只需要多文档快 使用—来代表一块 当然也可以使用多个文档来配置的,同上

---
server:
	port: 8080
spring:
	profiles:
		active:   什么也不写表示的是默认的,协商dev就代表启动的是dev文档快

# 使用---来分割文档快
---
server:
	port: 8081
spring:
	profiles:
		active:dev
---
server:
	port: 8082
spring:
	profiles:
		active:test

自动配置原理

我们可以配置的文件内容十分之多,到底是怎么配置的呢?

原理

@Configuration
@EnableConfigurationProperties({HttpProperties.class}) 对应我们编写的配置文件 假设配置文件中有就用配置文件中的值,没有就默认的
@ConditionalOnWebApplication( spring的注解 判断类型是不是servlet
    type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class}) 判断是否存在这个类
@ConditionalOnProperty(
    prefix = "spring.http.encoding",
    value = {"enabled"},
    matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {

精髓

1.springboot启动会加载大量的自动配置类,从spring.factories中读取出来的

2.需要判断我们的类是否存在这里面,如如果不存在的话需要手动导入,如果存在导入启动器就可以了

3.我们的配置文件之所以可以自动配置生效:

​ xxxAutoConfiguration 自动配置类

​ xxxProperties封装配置文件中的相关属性

4.我们给容器中自动配置类配置属性的时候,会通过xxxProperties类来获取某些用户配置文件中的属性,如果没有的话则使用默认的,如果有则使用自动配置的

提高:自己定义一个starter

我们分析了源码以及自动装配的过程,我们可以尝试来自己开发一个启动器玩玩

说明

启动器:jar包

命名规则:

​ 官方命名: spring-boot-starter-xxx.jar

自己命名:

​ xxx-spring-boot-starter

mybatis-spring-boot-starter

自己开发一个starter步骤

1.启动器其实是两个相互依赖的项目,一个是控制版本的(基础设施),一个是提供服务的

2.构建一个empty项目,里面有两个module,一个是maven项目,表示的是starter,一个是springboot项目表示的是autoconfigure,当然了两个项目既可以都是maven项目,也可以是springboot项目,一个maven,一个springboot也是可以的。

第一步:创建一个新项目

在这里插入图片描述

第二步:创建两个module

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这样就创建好了两个module

第三步:将第二个的autoconfigure的pom/xml中的自己的标识放进第一个starter中,表示这个starter的配置依赖是autoconfigure.

在这里插入图片描述

注意

在configure项目中pom.xml中需要剔除一个打包用的插件


<build>
   <plugins>
      <plugin>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
   </plugins>

我们不能使用springboot的方式打包,而是使用maven原始的方式打包,因为我们现在做的是依赖,而不是sprinboot项目,所以我们需要遵照maven的规则来。

在configure项目中删掉test目录及其下面所有的东西,这个我们不需要的

configure项目的pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>hl-spring-boot-starter-autoconfigure</groupId>
	<artifactId>hl-spring-boot-starter-autoconfigure</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>hl-spring-boot-starter-autoconfigure</name>
	<description>Demo project for Spring Boot</description>

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

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>


</project>

starter项目的pom.xml文件

?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>hl-spring-boot-starter</groupId>
    <artifactId>hl-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>hl-spring-boot-starter-autoconfigure</groupId>
            <artifactId>hl-spring-boot-starter-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

starter文件里面需要改的地方就是pom.xml文件,只需要将configure项目当作依赖导入进来即可。

第四步:编写相应的功能组件

编写UserInfoProperties类 这个类来和配置文件相关联:

@ConfigurationProperties(
        prefix = "user.info"
)
public class UserInfoProperties {
    private String name;
    private int age;
    private String role;

   ....setter和getter方法
}

编写Service类:

@Component
public class UserInfoService {
    private UserInfoProperties userInfoProperties;

    public UserInfoService(UserInfoProperties userInfoProperties){
        this.userInfoProperties = userInfoProperties;
    }

    public String getName(){
        return userInfoProperties.getName();
    }
    public String getRole(){
        return userInfoProperties.getRole();
    }
    public int getAge(){
        return userInfoProperties.getAge();
    }
}

编写autoConfigure类:

@Configuration
@ConditionalOnClass({UserInfoService.class})  当类路径classpath下有指定的类的情况下进行自动配置
@EnableConfigurationProperties({UserInfoProperties.class}) 将配置类关联进来
public class UserInfoAutoConfiguration {
    @Autowired
    UserInfoProperties userInfoProperties;

    @Bean
    @ConditionalOnMissingBean(UserInfoService.class)
    public UserInfoService getLogonInfo(){
        UserInfoService u = new UserInfoService(userInfoProperties);
            return u;
    }

}

然后在autoconfigure项目的resource中新建一个META-INF文件夹,创建spring.factories文件,在该文件中配置自己的自动配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=hlspringboot.hlspringbootstarterautoconfigure.test.UserInfoAutoConfiguration   就是xxxAutoConfiguration的路径

然后打开Maven菜单:将这两个module导报一下:

在这里插入图片描述

在这里插入图片描述

然后我们打开自己的仓库就可以看到这两个jar包了:

在这里插入图片描述

如果想要其他的项目访问这两个jar包的话,那么需要有一个前提就是这两个的maven仓库是一样的,就是需要在idea中配置maven的信息: 红框中的地址需要一致,但是如果别人使用的,那么你可以创建一个maven私库,然后你们两个都去访问这个私库就可以访问的到了,前提是你将自己写的starter上传上去了

在这里插入图片描述

我们新建一个sprinboot项目然后在pom.xml中引入我们的依赖:

 <dependency>
            <groupId>hl-spring-boot-starter</groupId>
            <artifactId>hl-spring-boot-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

然后编写一个controller来测试一下:

@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    UserInfoService userInfoService;
    @RequestMapping("/getUserInfo")
    public String getUserInfo(){
    return "现在登陆的人名称:"+userInfoService.getName()+",年龄是:"+userInfoService.getAge()+"岁,角色是:"+userInfoService.getRole();
    }
}

然后在application.yaml中添加上自己需要的东西:

user:
  info:
     name: zhangsan
     age: 12
     role: 管理员

然后启动项目,然后访问http://localhost:8080/test/getUserInfo得到结果:

在这里插入图片描述

这样就可以了,完美完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值