SpringBoot(1)

1.Spring与SpringBoot

1.1.Spring能做什么

1.1.1.Spring的能力

1.1.2.Spring的生态

https://spring.io/projects/spring-boot

覆盖了:

web开发

数据访问

安全控制

分布式

消息服务

移动开发

批处理

开发应用项目,功能太多就会带来复杂繁琐的配置问题,SpringBoot能简化配置,SpringBoot底层是Spring Framework,所以Spring Boot可以整合spring的所有技术栈,以避免繁杂的开发。

1.1.3.Spring5重大升级

1.1.3.1.响应式编程
1.1.3.2.内部源码设计

基于Java8的一些新特性,如:接口默认实现。重新设计源码架构。

1.2.为什么用SpringBoot

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”。

能快速创建出生产级别的Spring应用

1.2.1.SpringBoot优点

  • Create stand-alone Spring applications

    创建独立Spring应用

  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)

    内嵌web服务器

  • Provide opinionated ‘starter’ dependencies to simplify your build configuration

    自动starter依赖,简化构建配置

  • Automatically configure Spring and 3rd party libraries whenever possible

    自动配置Spring以及第三方功能,自动完成第三方功能配置。例如MySQL,Redis

  • Provide production-ready features such as metrics, health checks, and externalized configuration

    提供生产级别的监控、健康检查及外部化配置

  • Absolutely no code generation and no requirement for XML configuration

    无代码生成、无需编写XML

SpringBoot是整合Spring技术栈的一站式框架

SpringBoot是简化Spring技术栈的快速开发脚手架

1.2.2.SpringBoot缺点

  • 人称版本帝,迭代快,需要时刻关注变化
  • 封装太深,内部原理复杂,不容易精通

1.3.时代背景

1.3.1.微服务

James Lewis and Martin Fowler (2014) 提出微服务完整概念。https://martinfowler.com/microservices/

In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.-- James Lewis and Martin Fowler (2014)

  • 微服务是一种架构风格

  • 一个应用拆分为一组小型服务

  • 每个服务运行在自己的进程内,也就是可独立部署和升级

  • 服务之间使用轻量级HTTP交互(主要就是指REST API)

  • 服务围绕业务功能拆分

  • 可以由全自动部署机制独立部署

  • 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术

1.3.2.分布式

微服务通过类似于大脑神经元网络结构,节点就是功能元素,节点之间的实现通信。

由于系统过于复杂故会出现问题

  • 远程调用:一般使用http进行服务交互

  • 服务发现:就是看哪些服务是可用的

  • 负载均衡

  • 服务容错:各种错误情况下的处理方式

  • 配置管理:配置中心,修改配置让服务们自己同步

  • 服务监控:多个服务以及云平台的资源消耗和健康状况

  • 链路追踪:一个复杂的业务流程可能需要连续调用多个微服务,我们需要记录一个完整业务逻辑涉及的每一个微服务的运行状态,再通过可视化链路图展现,帮助软件工程师在系统出错时分析解决问题,常见的解决方案有Zipkin,SkyWalking。

  • 日志管理

  • 任务调度

解决方案:

SpringBoot + SpringCloud

1.3.3.云原生

原生应用如何上云。 Cloud Native

上云的困难

  • 服务自愈:服务出现错误怎么复原

  • 弹性伸缩:根据不同的性能需求分配更多的服务器

  • 服务隔离:服务之间不相互影响

  • 自动化部署:自动化部署

  • 灰度发布:同样的服务有多台服务器运行,先把服务部署在其中一两台上看运行效果,没有问题了再慢慢全部升级

  • 流量治理:控制流量

上云的解决

1.4.如何学习SpringBoot

1.4.1.官网文档架构

https://docs.spring.io/spring-boot/docs/current/reference/html/

  • Spring Boot模态划分架构

  • Spring Boot资源清单

  • 查看版本新特性;

    https://github.com/spring-projects/spring-boot/wiki#release-notes

2.SpringBoot2入门

Spring Boot入门可以参考:https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/index.html

2.1.开发环境要求

2.1.1.JDK

Java 8 & 兼容java14

2.1.2.Maven版本

mavan设置,settings.xml文件格式一定对齐

<mirrors> 
  <mirror> 
    <id>nexus-aliyun</id>  
    <mirrorOf>central</mirrorOf>  
    <name>Nexus aliyun</name>  
    <url>http://maven.aliyun.com/nexus/content/groups/public</url> 
  </mirror> 
</mirrors>
 
<profiles> 
  <profile> 
    <id>jdk-1.8</id>  
    <activation> 
      <activeByDefault>true</activeByDefault>  
      <jdk>1.8</jdk> 
    </activation>  
    <properties> 
      <maven.compiler.source>1.8</maven.compiler.source>  
      <maven.compiler.target>1.8</maven.compiler.target>  
      <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> 
    </properties> 
  </profile> 
</profiles>

2.2.HelloWorld

需求:浏览发送/hello请求,响应Hello,Spring Boot 2

2.2.1.创建maven工程

2.2.2.pom文件引入依赖

<!-- 如果要进行spring boot开发必须导入父工程 spring-boot-starter-parent-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>
<!-- Spring Boot的web开发场景启动器-->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

2.2.3.创建主程序

/**
 * 主程序类,启动的入口
 *
 * @SpringBootApplication:这是一个SpringBoot应用
 */
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

注意:Spring Boot项目中的Bean必须在MainApplication主启动类的所在包的类或者子包的中,才能被扫描到。

2.2.4.编写业务

@Controller
public class HelloController {
    @RequestMapping(value = "hello")
    @ResponseBody
    public String helloWord() {
        return "Hello World";
    }
}

2.2.5.测试

直接运行主程序类中的main方法

2.2.6.发起请求

2.2.7.简化配置

spring boot为了简化配置,将所有配置都可以抽取在resources/application.properties(或者application.yaml)一个文件中,所有的配置都有默认值

server.port=8888

所有的配置属性可以参考:https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/appendix-application-properties.html#common-application-properties-server

2.2.8.简化部署

  1. maven配置文件引入打包插件

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
  2. 完成打包

  3. 运行生成的jar文件

    把项目打成jar包,直接在目标服务器执行即可。

    java -jar springboot-01-helloword-1.0-SNAPSHOT.jar
    

注意点:

  • 打包过程中可能会出现Process terminated错误,观察报错原因,基本都是maven的配置文件xml格式错误

  • 取消掉cmd的快速编辑模式

3.了解自动配置原理

3.1.Spring Boot特点

3.1.1.依赖管理

在pom文件中引入一个Spring Boot的父项目,如果进行web开发,只需求导入一个spring-boot-starter-web即可,无需担心引入哪些依赖以及对应的版本问题。

原因:maven中父项目做依赖管理,父项目中会声明很多依赖的种类和版本,子项目只需要继承了父项目,子项目写依赖就不需要版本号了。

  • Spring Boot项目中引入的父项目spring-boot-starter-parent

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>
    
  • spring-boot-starter-parent的父项目是spring-boot-dependencies

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

    在spring-boot-dependencies里面声明了几乎声明了所有开发中常用的依赖的版本号,这就是自动版本仲裁机制,用户不需要写版本号。

    <properties>
        <activemq.version>5.16.1</activemq.version>
        <antlr2.version>2.7.7</antlr2.version>
        <appengine-sdk.version>1.9.87</appengine-sdk.version>
        <artemis.version>2.15.0</artemis.version>
        <aspectj.version>1.9.6</aspectj.version>
        <assertj.version>3.18.1</assertj.version>
        <atomikos.version>4.0.6</atomikos.version>
        <awaitility.version>4.0.3</awaitility.version>
        <bitronix.version>2.1.4</bitronix.version>
        <build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
        <byte-buddy.version>1.10.22</byte-buddy.version>
        <caffeine.version>2.8.8</caffeine.version>
    	……
    </properties>
    

    如果根据自己的需求修改依赖版本(例如MySQL驱动),数据库驱动没有对应的场景启动器,需要手动添加依赖才会引入。

    1. 查看spring-boot-dependencies里面规定当前依赖的版本用的key。

      <mysql.version>8.0.23</mysql.version>
      
    2. 在当前项目的pom.xml里面重写依赖版本即可

      <properties>
          <mysql.version>5.1.47</mysql.version>
      </properties>
      

3.1.2.starter场景启动器

https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter

Spring Boot中有很多以spring-boot-starter-*命名的场景启动器,官方的starter是以spring-boot-starter-*格式命名的。starter是一组依赖的集合描述,只需要引入一个starter这个场景的所有常规需要的依赖我们都自动引入(原因:maven的特性,因为定义starter的时候定义了需要引入的依赖)

1、见到很多 spring-boot-starter-* : *就某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
4、见到的  *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>

例如spring-boot-starter-web的依赖树

所有的场景启动器(starter)最基本的依赖spring-boot-starter(Spring Boot的核心依赖)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.3.4.RELEASE</version>
    <scope>compile</scope>
</dependency>

3.1.3.自动配置

在spring-boot-starter-web场景启动器中配置了

  • 自动配好Tomcat

    1. 引入Tomcat依赖

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
          <version>2.3.4.RELEASE</version>
          <scope>compile</scope>
      </dependency>
      
    2. 配置Tomcat

  • 自动配好SpringMVC

    1. 引入SpringMVC全套组件

      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
          <version>5.2.9.RELEASE</version>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.2.9.RELEASE</version>
          <scope>compile</scope>
      </dependency>
      
    2. 自动配好SpringMVC常用组件(功能)

  • 自动配好Web常见功能,如:字符编码问题、dispatcherServlet

    1. SpringBoot帮我们配置好了所有web开发的常见场景
  • 默认的包结构

    1. 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
    2. 无需以前的包扫描配置
    3. 想要改变扫描路径,@SpringBootApplication(scanBasePackages=“com.stonebridge”)或者@ComponentScan 指定扫描路径
    @SpringBootApplication
    等同于
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan("com.stonebridge.boot")  // com.stonebridge.boot为主启动类所在的包
    
  • 各种配置拥有默认值

    1. 默认配置最终都是映射到某个类上,如:MultipartProperties。可以在application.properties进行修改。

    2. 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象

  • 按需加载所有自动配置项

    1. 非常多的starter
    2. 引入了哪些场景这个场景的自动配置才会开启
    3. SpringBoot所有的自动配置功能都在spring-boot-starter --> spring-boot-autoconfigure包里面
  • ……

3.2.容器功能

3.2.1.组件添加

3.2.1.1.@Configuration
  • 源码

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
    	@AliasFor(annotation = Component.class)
        
    	String value() default "";   
    }
    
  • 说明

    1. 作用:

      它是在spring3.0版本之后加入的。此注解是spring支持注解驱动开发的一个标志。表明当前类是spring的一个配置类,作用是替代spring的applicationContext.xml。但其本质就是@Component注解,被此注解修饰的类,会被注册到spring的IOC容器。

    2. 属性:

      value:用于存入spring的Ioc容器中Bean的id。

    3. 使用场景:

      在注解驱动开发时,用于编写配置的类,通常可以使用此注解。一般情况下,我们的配置也会分为主从配置,@Configuration一般出现在主配置类上位置。注意的是,如果我们在注解驱动开发时,构建ioc容器使用的是传入字节码的构造函数,此注解可以省略。但是如果传入的是一个包,此注解则不能省略。

  • 示例:

    在注解驱动的开发中,由于没有了applicationContext.xml,就没法在xml中配置spring创建容器要扫描的包了。那么,我们自己写的一些类,通过注解配置到ioc容器中也无法实现了。此时就可以使用此注解来代替spring的配置文件,并通过@ComponentScan注解完成扫描。

    @Configuration
    @Import(JdbcConfig.class)
    @PropertySource("classpath:jdbc.properties")
    @ComponentScan("com.stonebrdige")
    public class SpringConfiguration {
        
    }
    
  • 新特性:proxyBeanMethods(代理bean的方法)的默认值是true

    • Full模式(proxyBeanMethods=true):每次外部调用配置类中获取组件注册方法(例如user()),都会先检查容器中是否存在,如果存在都会返回之前容器中的单例对象。
    • Lite模式(proxyBeanMethods=false):每次外部调用配置类中获取组件注册方法(例如user()),都会直接创建新的对象。类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断。

    示例:

    @Configuration(proxyBeanMethods = true)
    public class MyConfig {
        @Bean //给容器添加组件,方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的实例
        public User user() {
            User user = new User("stonebridge", 19);
            user.setCat(cat());
            return user;
        }
        @Bean("tom")
        public Cat cat() {
            return new Cat("tomcat");
        }
    }
    
    ###################################################################################################
    @SpringBootApplication
    public class MainApplication {
        public static void main(String[] args) {
            //1.返回IOC容器
            ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
            MyConfig bean = run.getBean(MyConfig.class);
            //proxyBeanMethods = true: 
            //打印结果:com.stonebridge.boot.config.MyConfig$$EnhancerBySpringCGLIB$$ed52f9a1@2d140a7
            //proxyBeanMethods = false: 
            //打印结果:com.stonebridge.boot.config.MyConfig@325f7fa9
            System.out.println(bean);
            User user01 = bean.user();
            //proxyBeanMethods = true:  打印结果:true
            //proxyBeanMethods = false: 打印结果:false
            System.out.println(user01.getCat() == bean.cat());
        }
    }
    
3.2.1.2.@Bean

源码:

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {

	@AliasFor("name")
	String[] value() default {};

	@AliasFor("value")
	String[] name() default {};

	@Deprecated
	Autowire autowire() default Autowire.NO;

	boolean autowireCandidate() default true;

	String initMethod() default "";

	String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}

作用:

​ 它写在方法上,表示把当前方法的返回值存入spring的ioc容器。

​ 同时还可以出现在注解上,作为元注解来使用。

属性:

  • name:用于指定存入spring容器中bean的标识。支持指定多个标识。当不指定name属性时,默认值是当前方法名。

  • value:此属性是在4.3.3版本之后加入的。它和name属性的作用是一样的。

  • autowireCandidate:用于指定是否支持自动按类型注入到其他bean中。只影响@Autowired注解的使用。不影响@Resource注解注入。默认值为true,意为允许使用自动按类型注入。

  • initMethod:用于指定初始化方法。

  • destroyMethod:用于指定销毁方法。

使用场景:

通常情况下,在基于注解的配置中,我们用于把一个类注入spring的ioc容器中,首先考虑的是使用 @Component以及他的衍生注解。但是如果遇到要存入容器的Bean对象不是我们写的类(例如:数据源),此时无法在类上添加 @Component注解,此时就需要该注解了。

示例:

例如,在我们配置JdbcTemplate使用Spring内置数据源DriverManagerDataSource时,数据源类是spring-jdbc这个jar包中类,此时我们无法编辑,在上面加注解,此时就可以使用@Bean注解配置。

@Configuration
public class SpringConfiguration {
    @Bean(name = "dataSource")
    public DataSource crateDataSource() {
        return new DriverManagerDataSource();
    }

    public JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}
3.2.1.3.@Component、@Controller、@Service、@Repository

3.2.1.4.@ComponentScan

用于指定创建容器时要扫描的包。该注解在指定扫描的位置时,可以指定包名,也可以指定扫描的类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义Bean的命名规则。

3.2.1.5.@Import

**作用:**该注解是写在类上的,通常都是和注解驱动的配置类一起使用的。其作用是引入其他的配置类。使用了此注解之后,可以使我们的注解驱动开发和早期xml配置一样,分别配置不同的内容,使配置更加清晰。同时指定了此注解之后,被引入的类上可以不再使用@Configuration,@Component等注解。

value属性:

  1. 用于指定其他配置类的字节码。它支持指定多个配置类。
  2. 关于ImportSelector和ImportBeanDefinitionRegistrar请参考第五章第7小节@Import 注解的高级分析。

**使用场景:**当我们在使用注解驱动开发时,由于配置项过多,如果都写在一个类里面,配置结构和内容将杂乱不堪, 此时使用此注解可以把配置项进行分门别类进行配置。

示例:

  1. 从配置类JdbcConfig.java

    @Component
    public class JdbcConfig {
        @Bean("dataSource")
        public DataSource createDataSource(){
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/spring_ioc");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            return dataSource;
        }
    }
    

    @Component可不写

  2. 主配置类MainApplication.java

    通过@Import引入从配置类

    @SpringBootApplication
    @Import(value = {JdbcConfig.class})
    public class MainApplication {
        public static void main(String[] args) {
            //1.返回IOC容器
            ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
            
            JdbcConfig jdbcConfig = run.getBean(JdbcConfig.class);
            //com.stonebridge.boot.config.JdbcConfig@ddf20fd
            System.out.println(jdbcConfig);
    
            JdbcConfig jdbcConfig1 = run.getBean(JdbcConfig.class);
            //com.stonebridge.boot.config.JdbcConfig@ddf20fd
            System.out.println(jdbcConfig1);
        }
    }
    
3.2.1.6.@Conditional

条件装配:满足Conditional指定的条件,则进行组件注入。需要注意注入顺序。

其派生注解

示例:

  1. 容器注入组件

    @Configuration(proxyBeanMethods = true)
    public class MyConfig {
        @Bean("tom")
        public Cat cat() {
            return new Cat("tomcat");
        }
    
        @ConditionalOnBean(name = "tom")
        @Bean("user") //给容器添加组件,方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的实例
        public User user() {
            User user = new User("stonebridge", 19);
            user.setCat(cat());
            return user;
        }
    }
    

    @ConditionalOnBean(name = “tom”)判断当容器中存在名字为tom的组件时才注入user组件,注意注入顺序。

  2. 从容器中获取对应组件,判断是否存在于容器。

    @SpringBootApplication
    @Import(value = {JdbcConfig.class})
    public class MainApplication {
        public static void main(String[] args) {
            //1.返回IOC容器
            ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
            boolean isExistTom = run.containsBean("tom");
            System.out.println("容器中是否存在Tom组件" + isExistTom);
            boolean isExistUser = run.containsBean("user");
            System.out.println("容器中是否存在User组件" + isExistUser);
        }
    }
    
  3. 结果

    • 当注入tom组件时(@Bean(“tom”))

      容器中是否存在Tom组件true
      容器中是否存在User组件true

    • 当不注入tom组件时

      容器中是否存在Tom组件false
      容器中是否存在User组件false

3.2.2.原生配置文件引入

3.2.2.1.@ImportResource

@ImportResource注解用于导入Spring的配置文件,让配置文件里面的内容生效;(就是以前写的springmvc.xml、applicationContext.xml)

Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别;想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上。

注意!这个注解是放在主入口函数的类上,而不是测试类上

  1. 定义配置文件beans.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <bean id="haha" class="com.stonebridge.boot.bean.User">
            <property name="username" value="zhangsan"></property>
            <property name="age" value="18"></property>
        </bean>
    </beans>
    
  2. 从容器中获取

    @SpringBootApplication
    @ImportResource("classpath:beans.xml")
    public class MainApplication {
        public static void main(String[] args) {
            //1.返回IOC容器
            ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
            boolean isExisthaha = run.containsBean("haha");  //容器中是否存在Tom组件true
            System.out.println("容器中是否存在Tom组件" + isExisthaha);
        }
    }
    

3.2.3.配置绑定

3.2.3.1.读取*.properties中的内容

如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;

示例:将jdbc的参数保存到jdbc.properties中,然后读取出来。

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("jdbc.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             //封装到JavaBean。
         }
     }
 }
3.2.3.2.@Component + @ConfigurationProperties

示例:在application.properties定义数据

mycar.brand=Aston Martin
mycar.price=10000
@Component //只有在在容器中的组件,才会拥有Spring Boot提供的属性绑定功能
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private String brand;
    private Integer price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

使用时使用 @Autowired自动注入即可

@Autowired
private Car car;
3.2.3.3.@EnableConfigurationProperties + @ConfigurationProperties

在配置类上使用

@SpringBootApplication
@EnableConfigurationProperties(Car.class)
//1.开启Car配置绑定功能
//2.把这个Car组件注册到容器
public class MainApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
    }
}
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private String brand;
    private Integer price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

在Car中使用@ConfigurationProperties即可。

3.3.自动配置原理入门

3.3.1.引导加载自动配置类@SpringBootApplication

@SpringBootApplication注解是@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan组合注解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{
    ……
}
3.3.1.1.@SpringBootConfiguration

该注解相当于@Configuration,被标注类是一个配置类;@Configuration核心是@Component,说明Spring的配置类也是Spring容器内的一个组件。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;
}
3.3.1.2.@ComponentScan

指定扫描哪些,Spring注解;

3.3.1.3.@EnableAutoConfiguration(重点)

该注解是开启自动配置的功能

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}
  1. @AutoConfigurationPackage

    将使用该注解的类所在包以及子包下的组件能够被自动扫描到spring容器中。

    自动配置包

    @Import({Registrar.class})//利用Registrar给容器批量注册组件
    public @interface AutoConfigurationPackage {
        String[] basePackages() default {};
        Class<?>[] basePackageClasses() default {};
    }
    

    给容器中导入Registrar组件,其作用利用Registrar给容器批量注册组件,即将指定的一个包及其子包下所有组件注册进容器。这个指定的包就是Spring Boot的主启动类所在的包。因此所有组件都要写在主启动类所在包或者其子包下才会被自动扫描到。

  2. @Import({AutoConfigurationImportSelector.class})

    1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
    2、在1中的方法调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
    3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
    4、从META-INF/spring.factories位置来加载一个文件。
    	默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
        spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
        
    

    文件里面写死了spring-boot一启动就要给容器中加载的所有配置类

    spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories
    # 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,\
    ……
    

    虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration 按照条件装配规则(@Conditional),最终会按需配置。

3.3.2.修改默认配置

例如:配置MultipartResolver组件时,为了防止用户自己配置组件name错误,当检测到组件的name不为DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME(multipartResolver)时,重新将其注入容器,并将其命名为函数名multipartResolver

@Bean
@ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
//public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
    //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
    //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
    // Detect if the user has created a MultipartResolver but named it incorrectly
    return resolver;
}

SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先,例如配置CharacterEncodingFilter时会先通过@ConditionalOnMissingBean判断是否存在该组件。

@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
    ……
}

总结:

  1. SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
  2. 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件(application.yaml、application.properties)进行了绑定
  3. 生效的配置类就会给容器中装配很多组件
  4. 只要容器中有这些组件,相当于这些功能就有了。
  5. 定制化配置
    • 用户直接自己@Bean替换底层的组件
    • 用户去看这个组件是获取的配置文件什么值就去修改。

xxxxxAutoConfiguration —> 组件 —>xxxxProperties里面拿值 ----> application.properties

因此Spring Boot项目中配置信息,用户只需要在application.properties配置文件进行修改即可。

示例:

3.3.3.最佳实践

  1. 引入场景依赖

    springboot官方为其配置的场景

    https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter

  2. 查看自动配置了哪些(选做)

    • 自己分析,引入场景对应的自动配置一般都生效了

    • 配置文件中debug=true开启自动配置报告。Negative(不生效)\Positive(生效)

  3. 是否需要修改

    • 参照文档修改配置项
      • https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
      • 自己分析。xxxxProperties绑定了配置文件的哪些。
    • 自定义加入或者替换组件
      • @Bean、@Component
    • 自定义器 XXXXXCustomizer

3.4.开发小技巧

3.4.1.Lombok

简化JavaBean开发

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
@NoArgsConstructor
//@AllArgsConstructor
@Data
@ToString
@EqualsAndHashCode
public class User {
    private String name;
    private Integer age;
    private Pet pet;
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}
简化日志开发
@Slf4j
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String handle01(@RequestParam("name") String name){
        log.info("请求进来了....");
        return "Hello, Spring Boot 2!"+"你好:"+name;
    }
}

3.4.2.dev-tools

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

项目或者页面修改以后:Ctrl+F9;完成热更新(自动重启),不需要重启项目

3.4.3.Spring Initailizr(项目初始化向导)

3.4.4.引入processor依赖

  1. 在使用yaml配置时,为了能够提供绑定的属性提示。可以增加processor依赖。创建项目勾选或者maven导入

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-configuration-processor</artifactId>
    	<optional>true</optional>
    </dependency>
    
  2. 打包时排除processor

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

3.4.5.开启dashBoard

View ----tool windows ---- service

4.配置文件

4.1.文件类型

4.1.1.properties

同以前的properties用法

4.1.2.yaml

4.1.2.1.简介

YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。

非常适合用来做以数据为中心的配置文件

4.1.2.2.基本语法
  • key: value;kv之间有空格
  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '#'表示注释
  • 字符串无需加引号,如果要加,’'与""表示字符串内容 会被 转义/不转义
4.1.2.3.数据类型
  1. 字面量:单个的、不可再分的值。date、boolean、string、number、null

    k: v
    
  2. 对象:键值对的集合。map、hash、set、object

    行内写法:  k: {k1:v1,k2:v2,k3:v3}
    #或
    k: 
      k1: v1
      k2: v2
      k3: v3
    
  3. 数组:一组按次序排列的值。array、list、queue

    行内写法:  k: [v1,v2,v3]
    #或者
    k:
     - v1
     - v2
     - v3
    

示例:

@ConfigurationProperties(prefix = "person")
@Component
@Data
@ToString
public class Person {
    private String userName;
    private Boolean boss;
    private Date birth;
    private Integer age;
    private Pet pet;
    private String[] interests;
    private List<String> animal;
    private Map<String, Object> score;
    private Set<Double> salarys;
    private Map<String, List<Pet>> allPets;
}
@Data
@ToString
public class Pet {
    private String name;
    private Double weight;
}
person:
  userName: stonebridge
  boss: true
  birth: 2019/09/12
  age: 18
  #interests:[篮球,足球]
  interests:
    - 篮球
    - 足球
  animal: [,]
  score: { english: 12,chinese: 34 }
  #  score:
  #    english:80
  #    math:70
  #    chinese:87
  salarys:
    - 99999.89
    - 78886.90
  pet:
    name: 阿狗
    weight: 90.09
  allPets:
    sick:
      - { name: tom }
      - { name: jerry,weight: 47 }
    health: [ { name: mario,weight: 47 } ]
@Controller
public class HelloController {
    @Autowired
    Person person;
    
    @RequestMapping("person")
    @ResponseBody
    public Person person() {
        return person;
    }
}

4.2.yaml配置提示

参考3.4.4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值