当前位置: 首页 > news >正文

Spring boot再来一遍

spring-boot-starter-parent

  • 作为spring boot快速构建spring工程所依赖包的模板
  • spring-boot-starter-parent的继承了spring-b
  • boot-dependencies(2.1.8 Release)
  • dependencies中定义了spring boot快速构建所需要的依赖包及版本信息
  • dependencies中有版本锁定dependencManagement版本锁定,定义了一个最优的版本搭配套装,如果一个工程继承了spring-booot-dependencies,那么该工程即可以不用配置依赖包版本

spring-boot-starter-web

  • spring-boot-starter-web是spring-boot-start-parent中已经定义好的坐标和版本信息
  • 业务工程中,只需要指定spring-boot-starter-web的坐标即可,不需要再指定版本

artifactid

  • springboot自己提供的依赖,一般功能名称在最后,例如spring-boot-starter-jdbc
  • 而第三方提供的依赖,一般功能名字在最前,例如mybatis-spring-boot-starter

spring配置文件

  • 优先级顺序:application.properties>application.yml>application.yaml
  • 如果在写yml过程中没有提示联想信息,需要引入dependencies-processor依赖
<artifactId>spring-boot-configuration-processor</artifactId>
  • yaml中定义数组:
address:
	- beijing
	- tianjin
address: [beijing,tianjin]

user:
 name: zh
 age: 18
 phones:
  - 15522222222
  - 15551111111
  • 程序引用配置的方式:
// 1、@value注解
@Value("${address[0]}")
private String address;// beijing

// 2.Environment(org.springframework.core.env)
@Autowired
private Environmen env;
public void print(){
	sout(env.getProperty("address[0]"));// beijing
}

// 3、@ConfigurationProperties,实体类映射配置,一定注意层级关系,如果有必要,需要加prefix 
@Component
@ConfigurationProperties(prefix = "user")
public class user(){
	private String name;
	private Int age;
	private String[] phones;

	// 省略get set方法
}

@Condition

  • 按照条件来判断,满足条件的类会被容器注册为bean
  • 基本用法:
// 1.define a class implements Conditon, override 'matches' method and return true or false after your check statement
public class ConditionCheck implements Condition(){
	@Override
	public boolean matches(CondtionContext context, AnnotatedTypeMetadta metadata){
		// check statement
		return false;
	}
}

// 2.mark the annotation on the class you want to control
@Configuration
public class CustomerizedConfig{
	@Bean
	@Condition(ConditionCheck.class)
	pubic Config config(){
		return new Config();
	}
}
  • 也可以基于元注解,自定义一个注解,进行条件解析
// 1.define customerize annotation 
@Target({ElementType.TYPE, ElementType.METHOD})// 元注解:指定本注解范围
@Retention(RetentionPolicy.RUNTIME)//元注解:指定生效时间
@Documented//元注解:是否生成javadoc
@Condition(ConditionCheck.class)// 引用Condition注解
public @interface MyCondtion{
	String value();// 这里的值,会传给ConditionCheck.class中,可以通过metadata中获取value
}

// 2.mark the annotation on the class you want to control
@Configuration
public class CustomerizedConfig{
	@Bean
	@MyCondtion("abc")// abc就会传给MyCondtion的value属性中,然后再在ConditionCheck.class中加工判断,返回boolean决定本方法中的Config类是否被加载到bean容器中
	pubic Config config(){
		return new Config();
	}
}
  • Condition相关的还有很多注解:
    • @ConditionalOnClass // 判断环境中是否又对应的文件
    • @ConditionalOnProperty // 判断配置文件中是否有对应属性值
    • @ConditionalOnMissingBean // 判断环境中没有对应的bean
  • matches方法两个参数:
    • CondtionContext context:上下文对象,可获取属性值,获取类加载器,获取BeanFactory等
    • AnnotatedTypeMetadta metadata:元数据对象,用于获取注解属性。

@Enable开头的注解

  • spring boot中的enbale开头的注解,用于动态启动某些功能,其内部都是实用@import导入配置类,进行bean的动态加载。
  • @ComponentScan是@SpringBootApplication中的注解,其作用是扫描该类同目录及其子目录下所有的bean
  • Enable开头注解的由来,举例说明下演变过程:
// 案例:如果项目A如果想获取B项目的test类,怎么获取?
// 而且B中的@ComponentScan注解还扫描不到test类
// 方法一,在项目A中使用@ComponentScan添加该类的扫描:
@SpringBootApplication
@Component("com.test")
public class AprojectAapplication{
	xxx
	context.getBean("test");// 可以获取到
}

// 但是方法一比较笨重,因为第三方test的路径,使用方法二,在A项目使用Import注解:
@SpringBootApplication
@Import(test.class)
public class AprojectAapplication{
	xxx
	context.getBean("test");// 可以获取到
}

// 方法二也不是最好的方法,使用方法三,在B项目进行封装个Enable开头的注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(test.class)
public @interface EnableTest{
}

// 然后再B项目直接饮用EnableTest注解,此处也是验证到了Enable开头注解的作用和由来
@SpringBootApplication
@EnableTest
public class AprojectAapplication{
	xxx
	context.getBean("test");// 可以获取到
}

@Import

  • 被@Import导入的类,会被加载到IOC容器中
  • @Import导入的四种方式:
    • 直接导入Bean
    • 导入配置类
@Configuration
public class Config{
	@Bean
	public Test test(){
		return new Test();
	}
	@Bean
	public Test2 test2(){
		return new Test2();
	}
}
* 导入ImportSelector实现类(常用于加载配置文件中的类)
// 先在项目B声明一个ImportSelector实现类
public TestImportSelector implements ImportSelector{
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata ){
		return String[]{"com.Test", "com.Test2"};
	}
}

// 再在项目A中import
@SpringBootApplication
@Import(TestImportSelector.class)
public class AprojectAapplication{
	xxx
	context.getBean("test");// 可以获取到
}

* 导入ImportBeanDefinitionRegistrar实现类

public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar{
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Test.class).getBeanDefinition();
        registry.registerBeanDefinition("test", beanDefinition);

    }
}

@EnableAutoConfiguration

  • @EnableAutoConfiguration注解内部使用@Import加载配置类
  • 工程会根据依赖第三方包中的META/spring.factories配置,扫描配置类,当SpringBoot应用时,会自动初始化这些bean(关于spring.factories的意义,点击这篇文章可以了解更多)
  • 第三方的bean也是依赖conditon条件进行初始化的

模拟redis自定义一个starter,构建一个第三方包

  1. 定义一个redis-speeder-autoconfig,用来配置和模拟链接redis
  2. 定义一个redis-speeder-starter,引用redis-spring-boot-autoconfigure,用来整合和包装
  3. 使用META-INF/spring.factories,控制容器是否加载jedis的bean,关于
  4. 测试starter,测试获取jedis的bean
// 1、新建redis-speeder-autoconfig模块,用来配置和模拟链接redis
// 1.1、定义Redis.config类,用来配置redis和读取用户自定义redis配置
package com.entity;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 这个配置类是为了让引用本包的用户自定义配置host和port
 */
@Component
// ConfigurationProperties是绑定用户定义的配置文件,redis开头的配置即可,可以是.properties/.yaml等
@ConfigurationProperties(prefix = "redis")
// 避免和META-INF/spring.factories
@ConditionalOnMissingBean(RedisConfig.class)
public class RedisConfig{
    private String host = "localhost";// 默认localhost,如果用户配置了redis.host,则会覆盖本配置
    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;
    }
}

// 1.2、定义RedisMock,用来模拟redis服务
package com.entity;

/**
 * 模拟redis的启动
 */
public class RedisMock {
    public RedisMock(RedisConfig rc){
        System.out.println("redis initint...");
        System.out.println("redis mocked host:"+rc.getHost());
        System.out.println("redis mocked port:"+rc.getPort());
        System.out.println("redis inited! ");
    }
    public void print(){
        System.out.println("redis server is running");
    }
}

// 1.3、加载Redis服务类bean至ioc容器中
package com.speeder;

import com.entity.RedisConfig;
import com.entity.RedisMock;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
// 加载配置类RedisConfig,用来读取redis配置或者用户定义的redis配置
@EnableConfigurationProperties(RedisConfig.class)
public class RedisAutoConfiguration {

    @Bean
    public RedisMock redisMock(RedisConfig RedisConfig){

        return new RedisMock(RedisConfig);
    }
}


// 1.4、定义META-INF/spring.factories,用来加载RedisConfig类至ioc容器中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.entity.RedisConfig


// 2、定义redis-speeder-starter模块,仅用来整合和包装redis-speeder-autoconfig模块
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>redis-speeder-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

// 3、测试,
// 3.1定义redis-speeder-test模块,用来测试starter的配置
package com.speeder;

import com.entity.RedisMock;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class TestApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(TestApplication.class, args);
        // context.getBean("RedisConfig");
        RedisMock redisMock = (RedisMock)context.getBean("redisMock");
        redisMock.print();
        System.out.println("bean = " + redisMock);
    }
}
// 3.2、定义配置类resource/application.properties
redis.host=233.3.3.3
redis.port=1111

// 测试结果:
// 在启动过程中,可以得到手工打印的内容:
2022-09-24 17:06:03.256  INFO 1588 --- [           main] com.speeder.TestApplication              : Starting TestApplication using Java 1.8.0_231 on DESKTOP-E7BNK32 with PID 1588 (E:\mycode\spring-study\spring-boot\redis-speeder-test\target\classes started by zhaoheng in E:\mycode\spring-study\spring-boot)
2022-09-24 17:06:03.256  INFO 1588 --- [           main] com.speeder.TestApplication              : No active profile set, falling back to 1 default profile: "default"
2022-09-24 17:06:04.257  INFO 1588 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-09-24 17:06:04.272  INFO 1588 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-09-24 17:06:04.272  INFO 1588 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.65]
2022-09-24 17:06:04.397  INFO 1588 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-09-24 17:06:04.397  INFO 1588 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1094 ms
redis initint...
redis mocked host:233.3.3.3
redis mocked port:1111
redis inited! 
2022-09-24 17:06:04.678  INFO 1588 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-09-24 17:06:04.694  INFO 1588 --- [           main] com.speeder.TestApplication              : Started TestApplication in 1.928 seconds (JVM running for 3.405)
redis server is running
bean = com.entity.RedisMock@70325d20

SpringBoot Actuator监控

  • 引入依赖启动,即可创建监控台
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-actuator</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
  • 访问监控内的相关资源:
    1. 访问监控台:localhost:8080/actuator,会返回控制台所有地址导航信息
    2. 访问配置信息:localhost:8080/actuator/info等
    3. 健康检查:localhost:8080/actuator/health,可以查看项目是否在正常运行,也可以检查其他第三方依赖的健康状况,例如redis、数据库等
    4. 开启actuator所有监控:application.properties中加入management.endpoints.web.exposure.include=*
    5. 查看ioc容器中所有的bean:localhost:8080/actuator/bean
    6. 查看项目的环境配置信息:localhost:8080/actuator/env
    7. 查看所有的接口请求路径:localhost:8080/actuator/mappings
{
    "_links": {
        "self": {
            "href": "http://localhost:8080/actuator",
            "templated": false
        },
        "health-component": {
            "href": "http://localhost:8080/actuator/health/{component}",
            "templated": true
        },
        "health-component-instance": {
            "href": "http://localhost:8080/actuator/health/{component}/{instance}",
            "templated": true
        },
        "health": {
            "href": "http://localhost:8080/actuator/health",
            "templated": false
        },
        "info": {
            "href": "http://localhost:8080/actuator/info",
            "templated": false
        }
    }
}

遗留问题

  • SpringBoot Bean懒加载
  • SpringBoot自动配置类和Spring配置类区别

相关文章:

  • Mathorcup数学建模竞赛第三届-【妈妈杯】B题:关于三维健康评分模型的研究(附带赛题解析获奖论文)(一)
  • 最新版本vscode 真正解决用vscode + unity搭配开发没有代码智能提示 OmniSharp服务启动 vscode调试unity准备
  • T1064 奥运奖牌计数(信息学一本通C++)
  • python爬虫--cookie、防盗链、代理
  • Vue3+TSX开发模式下内置组件的替代方案
  • 燃烧化学平衡判据
  • 05--Django视图层-JsonResponse对象、request对象中的其他方法以及FBV与CBV的书写
  • Spring中Bean的生命周期详解
  • Linux文件之/etc/passwd和/etc/shadow
  • OCR - 微软windows 11系统自带的Windows OCR功能初体验
  • 公众号网课查题系统
  • 关于SELECT...FOR UPDATE到底锁表还是锁行
  • 解决问题的思路很重要,运维领域,结果对,过程就对!
  • C++ 顺序表和单链表的二路归并思想(详解+示例代码)
  • T1071 菲波那契数(信息学一本通C++)
  • 时间复杂度分析经典问题——最大子序列和
  • [LeetCode] Wiggle Sort
  • [Vue CLI 3] 配置解析之 css.extract
  • 4个实用的微服务测试策略
  • Android框架之Volley
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • JavaScript设计模式与开发实践系列之策略模式
  • JAVA并发编程--1.基础概念
  • Js基础知识(四) - js运行原理与机制
  • Laravel 菜鸟晋级之路
  • leetcode46 Permutation 排列组合
  • Mocha测试初探
  • MySQL主从复制读写分离及奇怪的问题
  • ng6--错误信息小结(持续更新)
  • oschina
  • spring-boot List转Page
  • Vue小说阅读器(仿追书神器)
  • 关于springcloud Gateway中的限流
  • 利用DataURL技术在网页上显示图片
  • 那些被忽略的 JavaScript 数组方法细节
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 前端性能优化——回流与重绘
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 网络应用优化——时延与带宽
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • ​业务双活的数据切换思路设计(下)
  • #NOIP 2014# day.2 T2 寻找道路
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (python)数据结构---字典
  • (三)uboot源码分析
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (转)3D模板阴影原理
  • (转)Oracle存储过程编写经验和优化措施
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .bat批处理出现中文乱码的情况
  • .gitignore文件设置了忽略但不生效
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes