SpringBoot学习笔记

目录

第1章 SpringBoot 概述

1.1 Spring 缺点

1.2 SpringBoot 功能

1.3 小结

第2章 SpringBoot 快速入门

2.1 需求

2.2 Maven实现步骤

2.2.1 创建Maven项目

2.2.2 导入SpringBoot起步依赖

2.2.3 定义Controller

2.2.4 编写引导类

2.2.5 启动测试

2.3 使用IDEA搭建SpringBoot

2.4 小结

第3章 SpringBoot 起步依赖原理分析

第4章 SpringBoot 配置

4.1 配置文件分类

4.2 yaml

4.2.1 yaml定义

4.2.2 yaml基本语法

4.2.3 yaml数据格式

4.2.4 总结

4.3 读取配置文件内容

4.3.1 @Value方式

4.3.2 Environment

4.3.3 @ConfigurationProperties

4.4 profile

4.4.1 profile配置方式

4.4.2 profile激活方式

4.5 内部配置加载顺序

4.6 外部配置加载顺序

第5章 SpringBoot 整合其他框架

5.1 SpringBoot整合Junit

5.2 SpringBoot整合Redis

5.3 SpringBoot整合MyBatis

第6章 SpringBoot 自动配置

6.1 Condition

6.2 切换内置web服务器

6.3 @Enable*注解

6.4 @Import注解的用法

6.4.1 导入Bean

6.4.2 导入配置类

6.4.3 导入 ImportSelector 实现类

6.4.4 导入 ImportBeanDefinitionRegistrar 实现类

6.5 @EnableAutoConfiguration

6.6 案例(SPI)

第7章 SpringBoot 监听机制

7.1 Java监听机制

7.2 SpringBoot监听机制

第8章 SpringBoot 启动流程分析

第9章 Spring Boot 监控

9.1 SpringBoot 监控 - Spring Boot Admin

第10章 SpringBoot 项目部署

10.1 jar包方式

10.2 war包方式


第1章 SpringBoot 概述

SpringBoot提供了一种快速使用Spring的方式,基于约定优于配置的思想,可以让开发人员不必在配置与逻 辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度 上缩短了项目周期。2014 年 4 月,Spring Boot 1.0.0 发布。Spring的顶级项目之一(https://spring.io)。

1.1 Spring 缺点

1) 配置繁琐

虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多 XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。 Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。 所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所 以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但它要求的回报也不少。

2) 依赖繁琐

项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导 入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发 进度。

1.2 SpringBoot 功能

1) 自动配置

Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定 Spring配置应该用哪个,不该用哪个。该过程是SpringBoot自动完成的。

2) 起步依赖

起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖 ,这些东西加在一起即支持某项功能。 简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

3) 辅助功能

提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。

1.3 小结

Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式。

Spring的缺点:

  • 配置繁琐
  • 依赖繁琐

SpringBoot功能:

  • 自动配置
  • 起步依赖:依赖传递
  • 辅助功能

第2章 SpringBoot 快速入门

2.1 需求

搭建SpringBoot工程,定义HelloController.hello()方法,返回”Hello SpringBoot!"

2.2 Maven实现步骤

2.2.1 创建Maven项目

2.2.2 导入SpringBoot起步依赖

pom.xml文件导入如下依赖

<!--SpringBoot工程需要继承的父工程-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.4</version>
</parent>

<dependencies>
    <!--web开发的起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

2.2.3 定义Controller

package com.itheima.controller;

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello SpringBoot!";
    }
}

2.2.4 编写引导类

package com.itheima;

/**
 * 引导类。SpringBoot项目的入口
 */
@SpringBootApplication
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

2.2.5 启动测试

直接运行main方法即可

启动日志

浏览器访问

2.3 使用IDEA搭建SpringBoot

创建完毕,SpringBoot会自动配置好需要的依赖和创建相关目录

(4)创建controller测试即可

2.4 小结

  • SpringBoot在创建项目时,使用jar的打包方式。
  • SpringBoot的引导类,是项目入口,运行main方法就可以启动项目。
  • 使用SpringBoot和Spring构建的项目,业务代码编写方式完全一样。

第3章 SpringBoot 起步依赖原理分析

  • 在spring-boot-starter-parent中定义了各种技术的版本信息,组合了一套最优搭配的技术版本。
  • 在各种starter中,定义了完成该功能需要的坐标合集,其中大部分版本信息来自于父工程。
  • 我们的工程继承parent,引入starter后,通过依赖传递,就可以简单方便获得需要的jar包,并且不会存在版本冲突等问题

第4章 SpringBoot 配置

4.1 配置文件分类

SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用 application.properties或者application.yml(application.yaml)进行配置。

properties:

server.port=8080

server.port=8080

yml:

server:
   port: 8080

总结:

  • SpringBoot提供了2种配置文件类型:properteisyml/yaml
  • 默认配置文件名称:application
  • 在同一级目录下优先级为:properties > yml > yaml

4.2 yaml

4.2.1 yaml定义

        YAML全称是 YAML Ain't Markup Language 。YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅 读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP 等。YML文件是以数据为核心的,比传统的xml方式更加简洁。
YAML文件的扩展名可以使用.yml或者.yaml。

properties:

server.port=8080 
server.address=127.0.0.1

xml:

<server>
    <port>8080</port> 
    <address>127.0.0.1</address>
</server>

yml:

server:
    port: 8080
    address: 127.0.0.1

对比其他两种方式,yaml更简洁,以数据为核心

4.2.2 yaml基本语法

  • 大小写敏感
  • 数据值前边必须有空格,作为分隔符
  • 使用缩进表示层级关系
  • 缩进时不允许使用Tab键,只允许使用空格(各个系统 Tab对应的 空格数目可能不同,导致层次混乱)。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • # 表示注释,从这个字符一直到行尾,都会被解析器忽略。

如下:

server:
    port: 8080
    address: 127.0.0.1

4.2.3 yaml数据格式

  • 对象(map):键值对的集合。
person:
name: zhangsan
# 行内写法
person: {name: zhangsan}
  • 数组:一组按次序排列的值
address:
- beijing
- shanghai
# 行内写法
address: [beijing,shanghai]
  • 纯量:单个的、不可再分的值
msg1: 'hello \n world' # 单引忽略转义字符 
msg2: "hello \n world" # 双引识别转义字符(回车)
  • YAML:参数引用
name: lisi
person:
    name: ${name} # 引用上边定义的name值

4.2.4 总结

1) 配置文件类型

  • properties:和以前一样
  • yml/yaml:注意空格

2) yaml:简洁,以数据为核心

基本语法

  • 大小写敏感
    • 数据值前边必须有空格,作为分隔符
    • 使用空格缩进表示层级关系,相同缩进表示同一级
  • 数据格式
    • 对象
    • 数组: 使用 “- ”表示数组每个元素
    • 纯量
  • 参数引用
    •  ${key}

4.3 读取配置文件内容

4.3.1 @Value方式

配置文件

server:
  port: 8084

name: abc

person:
  name: ${name}
  age: 20
  address:
    - benjing
    - shanghai

address:
  - benjing
  - shanghai

msg1: 'hello \n springboot'
msg2: "hello \n springboot"
@RestController
public class HelloController {
    @Value("${name}")
    private String name;
    @Value("${person.name}")
    private String name1;
    @Value(("${address[0]}"))
    private String address1;
    @Value("${msg1}")
    private String msg1;
    @Value("${msg2}")
    private String msg2;


    @RequestMapping("/hello")
    public String hello(){
        System.out.println(name);
        System.out.println(name1);
        System.out.println(address1);
        System.out.println(msg1);
        System.out.println(msg2);
        return "Hello SpringBoot !";
    }
}
-- 输出
abc
abc
benjing
hello \n springboot
hello 
 springboot

4.3.2 Environment

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Autowired
    private Environment env;

    @RequestMapping("/hello2")
    public String hello2(){
        System.out.println(env.getProperty("name"));
        System.out.println(env.getProperty("person.age"));
        System.out.println(env.getProperty("address[0]"));

        return "Hello SpringBoot !";
    }
}
-- 输出
abc
20
benjing

4.3.3 @ConfigurationProperties

准备Person类

@Component//表示这个类被spring识别了
@ConfigurationProperties(prefix = "person")//绑定前缀
public class Person {
    private String name;
    private Integer age;
    private String[] address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String[] getAddress() {
        return address;
    }

    public void setAddress(String[] address) {
        this.address = address;
    }
}

测试

@Autowired
    private Person person;

@RequestMapping("/hello3")
    public String hello3(){
        System.out.println(person);
        return "Hello SpringBoot !";
]
-- 输出
Person{name='abc', age=20, address=[benjing, shanghai]}

针对告警,可以添加

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

4.4 profile

        我们在开发Spring Boot应用时,通常同一套程序会被安装到不同环境,比如:开发、测试、生产等。其中数据库地址、服务器端口等等配置都不同,如果每次打包时,都要修改配置文件,那么非常麻烦。profile功能就是来进行动态配置切换的。

4.4.1 profile配置方式

多profile文件方式

多profile文件方式:提供多个配置文件,每个代表一种环境。

  • application-dev.properties/yml 开发环境

  • application-test.properties/yml 测试环境

  • application-pro.properties/yml 生产环境

需要提供多种配置文件,每个配置文件代表一个环境

格式必须为application-xxx.properties/yml

在主配置文件application.properties选择用哪种环境

spring.profiles.active=dev

yml多文档方式

• 在yml中使用 --- 分隔不同配置

  1. yml多文档方式:

配置文件如下

---

server:
  port: 8081
spring:
  config:
    activate:
      on-profile: dev
---
server:
  port: 8082
spring:
  config:
    activate:
      on-profile: test
---
server:
  port: 8083
spring:
  config:
    activate:
      on-profile: pro
---
spring:
  profiles:
    active: test

4.4.2 profile激活方式

通过配置文件

spring.profiles.active=dev

通过虚拟机参数

在VM options 指定:-Dspring.profiles.active=dev

通过命令行参数

java–jarxxx.jar --spring.profiles.active=prod

生产环境如何设置启动参数

找到jar包位置

输入java –jar xxx.jar启动SpringBoot项目

输入java –jar xxx.jar --spring.profiles.active=xxx即可更改环境

注意:生产环境中就是通过写linux启动脚本的方式,控制profile。

4.5 内部配置加载顺序

Springboot程序启动时,会从以下位置加载配置文件:

  1. file:./config/:当前项目下的/config目录下
  2.  file:./ :当前项目的根目录
  3.  classpath:/config/:classpath的/config目录
  4.  classpath:/ :classpath的根目录(appliaction.properties文件的最终打包路径见下图)

加载顺序为上文的排列顺序,高优先级配置的属性会生效

注意:当用jar包方式运行时,1 2 配置并不会生效,因为在打的jar包是没有这两个文件的。如何生效看下面。

4.6 外部配置加载顺序

外部配置加载顺序参考https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

如何让上面章节的配置生效???可以通过命令行方式

指定外部配置文件:

java -jar /Users/lizhijian/IdeaProjects/study/heima/springboot/spring-config/target/spring-config-0.0.1-SNAPSHOT.jar --spring.config.location=/Users/lizhijian/IdeaProjects/study/application.properties

除此之外还可以,通过命令行方式指定端口号等操作:

 java -jar /Users/lizhijian/IdeaProjects/study/heima/springboot/spring-config/target/spring-config-0.0.1-SNAPSHOT.jar  --server.port=9091

第5章 SpringBoot 整合其他框架

5.1 SpringBoot整合Junit

1. 搭建SpringBoot工程

2. 引入依赖

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

3. 编写测试类

@Service
public class UserService {

    void add(){
        System.out.println("add");
    }
}


@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringbootTestApplication.class)// 关联引导类,如果测试类和主类在同包名或者子包下,则无需关联
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testAdd(){
        userService.add();
    }
}
-- 输出
add

这里面笔者按照同包名下不关联启动类的方式报错,待更新  ToDo

5.2 SpringBoot整合Redis

1搭建SpringBoot工程
2 引入redis起步依赖

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

3 配置redis相关属性

spring:
  redis:
    host: 127.0.0.1 #redis的主机ip
    port: 6379

4 注入RedisTemplate模板

@Autowired
private RedisTemplate redisTemplate;

5 编写测试方法,测试

@Autowired
private RedisTemplate redisTemplate;

@SpringBootTest
class SpringbootRedisApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void testSet(){
        //存数剧
        redisTemplate.boundValueOps("name").set("zhangsan");
    }
    @Test void testGet(){
        //获取数据
        Object name = redisTemplate.boundValueOps("name").get();
        System.out.println(name);
    }
}

5.3 SpringBoot整合MyBatis

参考SpringBoot学习笔记(二) 整合redis+mybatis+Dubbo-CSDN博客

第6章 SpringBoot 自动配置

6.1 Condition

Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。

案例1

问题:

SpringBoot是如何知道要创建哪个Bean的?比如SpringBoot是如 何知道要创建RedisTemplate的?

可以通过Condition进行控制

在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求:
1. 导入Jedis坐标后,加载该Bean,没导入,则不加载。
2. 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。

当我们没导入redis-start时,会报错

@SpringBootApplication
public class SpringbootDemo01Application {

    public static void main(String[] args) {
        //启动SpringBoot应用,返回Spring的IOC容器
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootDemo01Application.class, args);
        //获取Bean,redisTemplate
        Object redisTemplate = run.getBean("redisTemplate");
        System.out.println(redisTemplate);

    }
}

报错信息

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'redisTemplate' available

当导入redis起步依赖后

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
--输出
org.springframework.data.redis.core.RedisTemplate@b7ff25

问题:SpringBoot是怎么知道我们导入redis坐标的呢?

案例2

在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求:
1. 导入Jedis坐标后,加载该Bean,没导入,则不加载。
2. 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。

<dependency>
     <groupId>redis.clients</groupId>
     <artifactId>jedis</artifactId>
     <version>2.9.0</version>
</dependency>
@Configuration
public class UserConfig {

    @Bean
    @Conditional(ClassCondition.class)
    public User user(){
        return new User();
    }
}

public class ClassCondition implements Condition {

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        // 导入Jedis坐标后创建Bean
        // 判断redis.clients.jedis.Jedis是否存在
        try {
            Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
            return true;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return false;
        }
    }
}

@SpringBootApplication
public class SpringbootConditionApplication {

    public static void main(String[] args)  {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
        Object user = context.getBean("user");
        System.out.println(user);

    }

}

-- 输出
com.it.test.springbootcondition.domain.User@4bbf6d0e

当去掉redis坐标时,发现确实没有生成User的Bean

将ClassCondition类中基于类的判断升级为动态判断,这里可以通过自定义注解的方式实现。

新增ConditionOnClass注解(注解传递性??? todo)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {

    String[] value();
}

User类修改为

@Configuration
public class UserConfig {

    @Bean
    //@Conditional(ClassCondition.class)
    @ConditionOnClass("redis.clients.jedis.Jedis")
    public User user(){
        return new User();
    }
}

ClassCondition修改为

public class ClassCondition implements Condition {

    /**
     * @param context  上下文对象,用于获取环境,ClassLoader对象
     * @param metadata 注解的元对象,可以用于注解定义的属性值
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();

        //1.需求:导入指定坐标后创建Bean
        //思路:判断指定坐标文件是否存在

        //获取注解属性值 value
        Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        String[] value = (String[]) map.get("value");
        boolean flag = true;
        try {
            for (String className : value) {
                Class<?> cls = Class.forName(className);
            }
        } catch (ClassNotFoundException e) {
            flag = false;
        }

        return flag;
    }
}

总结

自定义条件:

① 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回 boolean值 。 matches 方法两个参数:

  • context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
  • metadata:元数据对象,用于获取注解属性。

② 判断条件:在初始化Bean时,使用 @Conditional(条件类.class)注解

SpringBoot 提供的常用条件注解:

  • ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
@Bean                        // 配置文件中有键为ticast,且值为itheima才加载
@ConditionalOnProperty(name = "itcast",havingValue = "itheima")
public User user1(){
    return new User();
}

application.properties文件内容:

itcast=itheima
  • ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
  • ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean

6.2 切换内置web服务器

SpringBoot的web环境中默认使用tomcat作为内置服务器,其实SpringBoot提供了4中内置服务器供我们选择,我们可 以很方便的进行切换。

当项目未映入web做标时,会发现程序启动后立即终止,因为只有web工程才会一直运行,提供用户访问服务。

当添加web坐标后

可以发现,web项目默认用的tomcat服务器,

当切换坐标时

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!--排除tomcat依赖-->
    <exclusions>
        <exclusion>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <groupId>org.springframework.boot</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!--引入相应服务器的依赖-->
<dependency>
    <artifactId>spring-boot-starter-jetty</artifactId>
    <groupId>org.springframework.boot</groupId>
</dependency>

6.3 @Enable*注解

SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import注 解导入一些配置类,实现Bean的动态加载。

问题:SpringBoot 工程是否可以直接获取jar包中定义的Bean? 不可以

模拟上述场景

B项目打jar包

@Configuration
public class UserConfig {

    @Bean
    public User user() {
        return new User();
    }

    @Bean
    public Role role() {
        return new Role();
    }
}

A引入B的坐标

<dependency>
      <groupId>com.itheima</groupId>
      <artifactId>springboot-enable-other</artifactId>
      <version>0.0.1-SNAPSHOT</version>
</dependency>

A引入用B

@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        //获取Bean
        Object user = context.getBean("user");
        System.out.println(user);

    }

可以看到,B项目中的jar包未被实例化Bean。原因:@SpringBootApplication的ComponentScan注解扫描范围:当前引导类所在包及其子包。

处理方式:

 1.使用@ComponentScan扫描com.itheima.springbooyembal包
 2.可以使用@Import注解,加载类,这些类都会被Spring创建,并放入IOC容器。
 3.可以对@Import注解进行封装

 1、通过手动指定的方式加载B项目的User类。

@ComponentScan("com.itheima.config") //B项目包名
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        //获取Bean
        Object user = context.getBean("user");
        System.out.println(user);

    }

缺点:需要知道第三方类名

2、使用import加载类

@Import(UserConfig.class)    // B项目类
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        //获取Bean
        Object user = context.getBean("user");
        System.out.println(user);

    }

缺点:依然需要A项目知道B项目的具体类型

3、对@Import注解进行封装

B项目新建注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)// 引入配置类
public @interface EnableUser {
}

A项目引用:

@EnableUser
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        //获取Bean
        Object user = context.getBean("user");
        System.out.println(user);

    }

6.4 @Import注解的用法

@Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用 法:

  1. 导入Bean
  2. 导入配置类
  3. 导入 ImportSelector 实现类。一般用于加载配置文件中的类
  4. 导入 ImportBeanDefinitionRegistrar 实现类。

6.4.1 导入Bean

@Import(User.class)
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);
       
        Map<String, User> map = context.getBeansOfType(User.class);
        System.out.println(map);

}

6.4.2 导入配置类

@Import(UserConfig.class)  // 第二种
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);

        Role role = context.getBean(Role.class);
        System.out.println(role);

}

@Configuration              // 用import导入配置类时,该注解可以不添加
public class UserConfig {

    @Bean
    public User user() {
        return new User();
    }

    @Bean
    public Role role() {
        return new Role();
    }
}


6.4.3 导入 ImportSelector 实现类

一般用于加载配置文件中的类

@Import(MyImportSelector.class)  // 第三种
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);

        Role role = context.getBean(Role.class);
        System.out.println(role);

}


public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.itheima.domain.User", "com.itheima.domain.Role"};
    }
}

6.4.4 导入 ImportBeanDefinitionRegistrar 实现类

@Import({MyImportBeanDefinitionRegistrar.class})
@SpringBootApplication
public class SpringbootEnableApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);

        //获取Bean
        User user = context.getBean(User.class);
        System.out.println(user);
        // 因为没有导入role,所以会导致失败
        Role role = context.getBean(Role.class);
        System.out.println(role);

}

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        registry.registerBeanDefinition("user", beanDefinition);
    }
}

6.5 @EnableAutoConfiguration

  • @EnableAutoConfiguration 注解内部使用
  • @Import(AutoConfigurationImportSelector.class)来加载配置类。
  • 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载 这些配置类,初始化Bean
  • 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean

实战案例:自定义自己的starter  要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean

参考mybatis自动注入,当引入坐标后,

会间接引入坐标

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>

相应坐标下的META-INF/spring.factories文件中存放MybatisAutoConfiguration自动配置类,Spring根据自动配置类条件确定是否加载相应Bean。

6.6 案例(SPI)

自定义redis-starter。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean

实现步骤

1. 创建 redis-spring-boot-autoconfigure 模块(module)并引入坐标

<!--引入jedis依赖-->
<dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
</dependency>

2. 创建 redis-spring-boot-starter 模块,依赖 redis-spring-boot-autoconfigure的模块(starter只是做一层pom的透传,作为调用方和autoconfigure之前的纽带)

<!--引入自定义的autocongifure-->
<dependency>
    <groupId>com.ithiema</groupId>
    <artifactId>redis-spring-boot-autocongifure</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

3. 在 redis-spring-boot-autoconfigure 模块中初始化 Jedis 的Bean。并定义META-INF/spring.factories 文件,并打成jar包

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.ithiema.redis.config.RedisAutoConfiguration
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnClass(Jedis.class)
public class RedisAutoConfiguration {
    /**
     * 提供Jedis的bean
     */
    @Bean
    @ConditionalOnMissingBean(name = "jedis")
    public Jedis jedis(RedisProperties redisProperties) {
        System.out.println("RedisAutoConfiguration....");
        return new Jedis(redisProperties.getHost(), redisProperties.getPort());
    }
}

@ConfigurationProperties(prefix = "redis")
public class RedisProperties {

    private String host = "localhost";
    private int port = 6379;


    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
}

注意:这里需要手动启用RedisProperties,加@Component不一定好使,因为不一定会被扫描到,解决办法是在RedisAutoConfiguration上加@EnableConfigurationProperties(RedisProperties.class))
4 在测试模块中引入自定义的 redis-starter 依赖,测试获取Jedis 的Bean,操作 redis。

<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>redis-sping-boot-start</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>
@SpringBootApplication
public class SpringbootEnableApplication {
    Jedis jedis = context.getBean(Jedis.class);
}

采用该种方式(SPI),仅需要调用方引入坐标即可,后续的RedisAutoConfiguration类中会自行决定是否生成对应的Bean。

验证点

当调用方application.properties中指定

redis.port=6666

时,启动时会报

说明jar包中的Bean读到了本地的属性配置。

优化点

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnClass(Jedis.class)// 优化点1:只有引入了Jedis类才去加载Bean
public class RedisAutoConfiguration {


    /**
     * 提供Jedis的bean
     */
    @Bean
    @ConditionalOnMissingBean(name = "jedis")// 优化点2:没有jedis Bean时才去加载当前Bean
    public Jedis jedis(RedisProperties redisProperties) {
        System.out.println("RedisAutoConfiguration....");
        return new Jedis(redisProperties.getHost(), redisProperties.getPort());
    }
}

扩展: 常用配置注解

  • @ConfigurationProperties

使用@ConfigurationProperties的时候,把配置类的属性与yml配置文件绑定起来的时候,还需要加上@Component注解才能绑定并注入IOC容器中,若不加上@Component,则会无效。

  • @EnableConfigurationProperties

@EnableConfigurationProperties的作用:则是将让使用了 @ConfigurationProperties 注解的配置类生效,将该类注入到 IOC 容器中,交由 IOC 容器进行管理,此时则不用再配置类上加上@Component。
 

第7章 SpringBoot 监听机制

7.1 Java监听机制

SpringBoot 的监听机制,其实是对Java提供的事件监听机制的封装。

Java中的事件监听机制定义了以下几个角色:

① 事件:Event,继承 java.util.EventObject 类的对象

② 事件源:Source ,任意对象Object

③ 监听器:Listener,实现 java.util.EventListener 接口的对象

7.2 SpringBoot监听机制

SpringBoot 在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成 一些操作。 ApplicationContextInitializer、SpringApplicationRunListener、CommandLineRunner、ApplicationRunner

案例

ApplicationContextInitializer

@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializer...initialize");
    }
}

SpringApplicationRunListener

public class MySpringApplicationRunListener implements SpringApplicationRunListener {
    public MySpringApplicationRunListener(SpringApplication application,String[] args) {
    }

    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        SpringApplicationRunListener.super.starting(bootstrapContext);
    }

    @Override
    public void starting() {
        System.out.println("starting...项目启动中");
    }

    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        SpringApplicationRunListener.super.environmentPrepared(bootstrapContext, environment);
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("environmentPrepared...环境对象开始准备");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("contextPrepared...上下文对象开始准备");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("contextLoaded...上下文对象开始加载");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("started...上下文对象加载完成");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("running...项目启动完成,开始运行");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("failed...项目启动失败");

    }
}

CommandLineRunner

@Component
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner...run");
        System.out.println(Arrays.asList(args));
    }
}

ApplicationRunner

/**
 * 当项目启动后执行run方法:可以用于提前加载缓存redis
 */
@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner...run");
        System.out.println(Arrays.asList(args.getSourceArgs()));
    }
}

其中CommandLineRunnerApplicationRunner的实现不需要配置文件里配置东西:

要想ApplicationContextInitializerSpringApplicationRunListener可以实现,必须在resources目录下的META-INF文件夹下的配置spring.factories(名字必须一致,这样spring才能扫描到)

org.springframework.context.ApplicationContextInitializer=com.itheima.springbootlistener.listener.MyApplicationContextInitializer
#所继承类的全类名=实现类的全类名
org.springframework.boot.SpringApplicationRunListener=com.itheima.springbootlistener.listener.MySpringApplicationRunListener

第8章 SpringBoot 启动流程分析

详见  SpringBoot学习笔记(三) 自动装配原理_assourceclass-CSDN博客

第9章 Spring Boot 监控

SpringBoot自带监控功能Actuator,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、配置属性 、日志信息等。

案例:SpringBoot 监控使用

导入依赖坐标

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

访问

http://localhost:8080/acruator

路径

描述

/beans

描述应用程序上下文里全部的Bean,以及它们的关系

/env

获取全部环境属性

/env/{name}

根据名称获取特定的环境属性值

/health

报告应用程序的健康指标,这些值由HealthIndicator的实现类提供

/info

获取应用程序的定制信息,这些信息由info打头的属性提供

/mappings

描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系

/metrics

报告各种应用程序度量信息,比如内存用量和HTTP请求计数

/metrics/{name}

报告指定名称的应用程序度量值

/trace

提供基本的HTTP请求跟踪信息(时间戳、HTTP头等)

9.1 SpringBoot 监控 - Spring Boot Admin

  • Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。
  • Spring Boot Admin 有两个角色,客户端(Client)和服务端(Server)。
  • 应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册
  • Spring Boot Admin Server 的UI界面将Spring Boot Admin Client的Actuator Endpoint上的一些监控信息

admin-server

① 创建 admin-server 模块

② 导入依赖坐标 admin-starter-server

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

③在引导类上启用监控功能@EnableAdminServer

@SpringBootApplication
@EnableAdminServer
public class SpringbootAdminServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootAdminServerApplication.class, args);
    }
}

admin-client

① 创建 admin-client 模块

② 导入依赖坐标 admin-starter-client

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

③ 配置相关信息:server地址等

#指定admin.server地址
spring.boot.admin.client.url=http://localhost:9000
#展示健康检查详细详细展示出来
management.endpoint.health.show-details=always
#开启所有配置
management.endpoints.web.exposure.include=*

④ 启动server和client服务,访问server  http://localhost:9000/#/applications

第10章 SpringBoot 项目部署

SpringBoot 项目开发完毕后,支持两种方式部署到服务器:

  1. jar包(官方推荐)
  2. war包

10.1 jar包方式

jar包地址

打开命令行,启动jar报  java -jar   xxxx.jar

10.2 war包方式

1.pom.xml里更改打包方式

2.在引导类继承SpringBootServletInitializer类,并重写configure方法

@SpringBootApplication
public class SpringBootDeployApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDeployApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(SpringBootDeployApplication.class);
    }
}

将war包放到tomcat webapps 目录下

启动tomcat

访问时需要加上解压后文件路径。

参考资料:黑马程序员SpringBoot教程,6小时快速入门Java微服务架构Spring Boot_哔哩哔哩_bilibili

1、SpringBoot3-快速入门 · 语雀

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值