初探SpringBoot

简介

什么Spring

Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。

Spring是如何简化Java开发的

1、基于POJO的轻量级和最小侵入性编程,所有东西都是bean;

2、通过IOC,依赖注入(DI)和面向接口实现松耦合;

3、基于切面(AOP)和惯例进行声明式编程;

4、通过切面和模版减少样式代码,xxxTemplate;

初识SpringBoot

SpringBoot就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置。
you can "just run",能迅速的开发web应用,几行代码开发一个http接口。
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。
也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。
Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。

Spring Boot的主要优点:

  • 为所有Spring开发者更快的入门

  • 开箱即用,提供各种默认配置来简化项目配置

  • 内嵌式容器简化Web项目

  • 没有冗余代码生成和XML配置的要求

HelloWorld!

项目创建方式:
1、创建一个新项目
2、选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现
3、填写项目信息
4、选择初始化的组件(初学勾选 Web 即可)
5、填写项目路径
6、等待项目构建成功

pom.xml

<!--有一个父项目-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencies>
        <!--web依赖:tomcat,dispatcherServlet.xml-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--spring-boot-starter所有的springboot依赖都是使用这个开头的-->

        <!--单元测试-->
        <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>

    <build>
        <!--打包插件-->
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

http接口

1、在主程序的同级目录下,新建一个controller包,一定要在同级目录下,否则识别不到
2、在包中新建一个HelloController类

//自动装配
@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello(){
        //调用业务,接收前端参数
        return "helloworld!";
    }
}

测试

将项目打成jar包,点击 maven的 package

打成了jar包后,就可以在任何地方运行了!OK

原理初探

自动配置:

pom.xml

  • spring-boot-dependencies:核心依赖在父工程中!
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

点进去发现还有一个父依赖

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

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

  • 在以后写或者引入一些springboot依赖的时候,不需要指定版本,因为有这些版本仓库;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;

启动器:

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

springboot-boot-starter-xxx:说白了就是springboot的场景启动器
spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;

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

主启动类

//SpringBootApplication: 标注这个类是一个springboot的应用
@SpringBootApplication
public class Springboot01HelloworldApplication {

    public static void main(String[] args) {
        //将springboot应用启动
        SpringApplication.run(Springboot01HelloworldApplication.class, args);
    }

}

@SpringBootApplication

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

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    ....
}
@SpringBootConfiguration: springboot的配置
      @Configuration:说明是spring的一个配置类
            @Component:说明这也是spring的一个组件
@EnableAutoConfiguration:自动配置
@AutoConfigurationPackage:自动配置包
@Import(AutoConfigurationPackages.Registrar.class):自动配置`包注册`
@Import(AutoConfigurationImportSelector.class):自动配置导入选择
@ComponentScan :自动扫描并加载符合条件的组件或者bean 

//获取所有的配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

获取候选的配置

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				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;
	}

@ComponentScan

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

@SpringBootConfiguration

作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;
点进去的话:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration      //<--在这,在点这个注解的话会得到下面这个接口
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;
}

/*-------------------------------------*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
      @AliasFor(annotation = Component.class)
	String value() default "";
      boolean proxyBeanMethods() default true;
}

这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!
再次回到 SpringBootApplication 注解中继续看。

@EnableAutoConfiguration

@EnableAutoConfiguration :开启自动配置功能
以前得需要自己配置的东西,而现在SpringBoot可以自动帮助配置 ;
@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
点注解继续看:
@AutoConfigurationPackage :自动配置包

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
                  ...
}

**@import **:Spring底层注解@import , 给容器中导入一个组件;
Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;

返回@EnableAutoConfiguration会发现这么个注解:

@Import({AutoConfigurationImportSelector.class}):给容器导入组件;
AutoConfigurationImportSelector :自动配置导入选择器;
1、点进去这个注解发现有这么个方法

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
                  //看下面这个方法-->getCandidateConfigurations,点进去
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

有这么个方法

// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
                  //这里的getSpringFactoriesLoaderFactoryClass()方法
                      //返回的就是最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				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;
	}

2、这个方法又调用了 ** SpringFactoriesLoader** 类的静态方法!点进去
SpringFactoriesLoader类loadFactoryNames()方法

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

3、继续点击查看** loadSpringFactories **方法

 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 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);
            }
        }
    }

4、发现一个多次出现的文件:spring.factories(自动配置的核心文件),全局搜索它发现了这么个东西

1.# Initializers :初始化的
2.# Application Listeners :监听的
3.# Auto Configuration Import Filters : 自动选择导入的包
4.# Auto Configure :自动配置

SpringFactoriesLoader

WebMvcAutoConfiguration

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

总结一下:
springboot所有自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面;
但不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,自动装配就会生效,然后启动成功;

1.SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
2.将这些值作为自动配置类导入容器,自动配置类就生效,帮我进行自动配置工作;
3.整合JavaEE的整体解决方案和自动配置都在springboot-autoconfigure-2.3.3.RELEASE.jar这个包下;
4.它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器;
5.容器中会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件;并自动装配,@Configuration,JavaConfig..
6.有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

SpringApplication

这个main方法相当于开启了一个服务

//SpringBootApplication: 标注这个类是一个springboot的应用 :启动类下的所有资源被导入
@SpringBootApplication
public class Springboot01HelloworldApplication {

    public static void main(String[] args) {
        //将springboot应用启动
        SpringApplication.run(Springboot01HelloworldApplication.class, args);
    }
}

SpringApplication.run : 该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;

SpringApplication

springboot究竟做了哪些事

总结:springboot所有的自动配置都是在启动的时候扫描并加载:spring.factories 所有的自动配置类都在这里面,但是不一定生效,
要判断条件是否成立,只要导入了对应的start,就有对应的启动器,有了这个启动器,我们的自动配置就会生效,然后就配置成功!

  • springboot在启动的时候,从类路径下/META-INF/spring.factories 下获取指定的值;
  • 将这些自动配置的类导入容器,自动配置类就会生效,帮我们自己进行配置!
  • 以前需要自动配置的东西,现在springboot帮我们做了。
  • 整合javaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.3.3.RELEASE.jar这个包下
  • 它会将所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器;
  • 容器中也会存在非常多的XxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件并自动配置,@Configuration,JavaConfig!
  • 有了自动配置类,就免去了我们手动配置文件的工作!

谈谈关于Springboot的认识?

  • 自动装配
  • run()
    • 判断应用类型是普通的项目还是Web项目
    • 查找并加载所有可用初始化器,设置到initializers属性中
    • 找出所有的应用程序监听器,设置到listeners属性中
    • 推断并设置main方法的定义类,找到运行的主类

SpringBoot配置

yaml配置注入

配置文件

SpringBoot使用一个全局的配置文件 , 配置文件名称是固定的

  • application.properties
    • 语法结构 :key=value
  • application.yml
    • 语法结构 :key:空格 value

配置文件的作用 :修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;

yaml基础语法

说明:语法要求严格!
1、空格不能省略
2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
3、属性和值的大小写都是十分敏感的。

字面量:普通的值 [ 数字,布尔值,字符串 ]
字面量直接写在后面就可以 , 字符串默认不用加上双引号或者单引号;

k: v

注意:

  • “ ” 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;
    比如 :name: "zhang san" 输出 :zhang 换行 san
  • '' 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出
    比如 :name: ‘zhang san’ 输出 :zhang san

对象、Map(键值对)

#对象、Map格式
k: 
    v1:
    v2:

在下一行来写对象的属性和值得关系,注意缩进;比如:

student:
    name: zhangsan
    age: 22

行内写法

student: {name: qinjiang,age: 3}

数组( List、set )
用 - 值表示数组中的一个元素,比如:

list:
 - zhangsan
 - lisi
 - wangwu

行内写法

list: [zhangsan,lisi,wangwu]

修改SpringBoot的默认端口号
配置文件中添加,端口号的参数,就可以切换端口;

server:
  port: 8082

注入配置文件

yaml注入配置文件

1、在springboot项目中的resources目录下新建一个文件 application.yml
2、编写一个实体类 Dog;

@Component  //注册bean到容器中
public class Dog {
    private String name;
    private Integer age;
    
    //有参无参构造、get、set方法、toString()方法  
}

3、我们原来是这样给bean注入属性值的!@Value,给这个类测试一下:


@Component //注册bean
public class Dog {
    @Value("旺财")
    private String name;
    @Value("3")
    private Integer age;
}

4、在SpringBoot的测试类下注入狗输出一下:

@RunWith(SpringRunner.class)
@SpringBootTest
class Springboot02ConfigApplicationTests {

    @Autowired
    Dog dog;

    @Test
    void contextLoads() {
        System.out.println(dog);
    }
}

结果成功输出,@Value注入成功;

5、复杂的实体类:Person 类

@Component //注册bean到容器中
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
    //有参无参构造、get、set方法、toString()方法  
}

6、我们来使用yaml配置的方式进行注入!

person:
  name: zhangsan
  age: 3
  happy: false
  birth: 2020/05/03
  maps: {k1: v1,k2: v2}
  lists:
    - code
    - music
    - girl

  dog:
    name: 旺财
    age: 3

7、我们刚才已经把person这个对象的所有值都写好了,我们现在来注入到我们的类中!

/*
@ConfigurationProperties作用:
将配置文件中配置的每一个属性的值,映射到这个组件中;
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应
*/
@Component //注册bean
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

8、IDEA 提示,springboot配置注解处理器没有找到查看官方文档后可以把这个依赖导入pom文件!

导入pom文件

        <!-- 导入配置文件处理器,配置文件进行绑定就会有提示,需要重启 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

9、再次测试一下:

@RunWith(SpringRunner.class)
@SpringBootTest
class Springboot02ConfigApplicationTests {

    @Autowired
    Person person;

    @Test
    void contextLoads() {
        System.out.println(person);
    }

}

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

1、在resources目录下新建一个person.properties文件

name=zhangsan

2、在代码中指定加载person.properties文件


@PropertySource(value = "classpath:person.properties")
@Component //注册bean
public class Person {

    @Value("${name}")
    private String name;

    ...
}

3、测试输出

配置文件占位符
配置文件(.yml)还可以编写占位符生成随机数

person:
    name: zhangsan${random.uuid} # 随机uuid
    age: ${random.int}  # 随机int
    happy: false
    birth: 2020/05/03
    maps: {k1: v1,k2: v2}
    lists:
      - code
      - girl
      - music
    dog:
      name: ${person.hello:other}_旺财
      age: 3

properties配置的话

【需要注意】properties配置文件在写中文的时候,会有乱码 , 我们需要去IDEA中设置编码格式为UTF-8;
settings-->FileEncodings 中配置;

总结一下:
1、如果在业务中,只需要获取配置文件中的某个值,可以使用一下 @value;
2、如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties;

感谢狂神老师一路陪伴!-->狂神老师B站课程链接

频频回头的人注定走不了远路。
原文地址:https://www.cnblogs.com/CHENFANgY/p/13599097.html