1. Spring Boot简介
Spring Boot的设计目的是用来简化新Spring应用的初始搭建以及开发过程。SpringBoot所具备的特征有:
- 可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs
- 内嵌Tomcat或Jetty等Servlet容器
- 提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置
- 尽可能自动配置Spring容器
- 提供准备好的特性,如指标、健康检查和外部化配置
- 绝对没有代码生成,不需要XML配置
Spring Boot官网:https://spring.io/projects/spring-boot/
Spring Boot启动结构图(图片太大了,可单独查看图片):
Spring Boot启动过程:
一路追踪源码:
SpringApplication.run()
// 先new对象,然后run
new SpringApplication(primarySources).run(args)
// new对象的构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 配置Source
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判断是否为web环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
// 创建初始化构造器,加载spring.factories文件中ApplicationContextInitializer对应的所有类
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 创建应用监听器,加载spring.factories文件中ApplicationListener对应的所有类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 配置应用的主方法所在类
this.mainApplicationClass = deduceMainApplicationClass();
}
// run方法:运行Spring应用程序,创建并刷新新的应用程序上下文
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
// 应用启动计时器开始计时
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 获取spring.factories文件中SpringApplicationRunListener对应的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 广播事件,应用启动监听器开始监听
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 应用启动时的提示图标,可以在resources下创建一个banner.txt来修改启动时的打印信息
Banner printedBanner = printBanner(environment);
// 创建新的应用程序上下文
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 准备新的应用程序上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新新的应用程序上下文
refreshContext(context);
afterRefresh(context, applicationArguments);
// 应用启动计时器结束计时
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 上下文已经刷新,应用程序已经启动
listeners.started(context);
// 调用CommandLineRunner和ApplicationRunner
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
2. 创建Spring Boot工程
需求:在页面展示hello, world
2.1 手工创建
创建Maven工程
-
在pom.xml中配置parent和web的起步依赖
<?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>com.52lhp</groupId> <artifactId>hello</artifactId> <version>1.0-SNAPSHOT</version> <!--1. 继承spring-boot-starter-parent Spring Boot版本控制的原理: spring-boot-starter-parent继承了spring-boot-dependencies, 而spring-boot-dependencies中定义了各个依赖的版本 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <!--2. 添加web的起步依赖--> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
-
创建启动类
package com.lhp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class HelloWorldApplication { public static void main(String[] args) { SpringApplication.run(HelloWorldApplication.class, args); } }
-
编写业务代码:
package com.lhp.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloWorldController { @RequestMapping("/hello") public String hello() { return "hello, world"; } }
运行启动类的main方法
2.2 通过Spring Initializr创建
在IDEA中创建工程时使用Spring Initializr
- 点击Next转到Spring Initializr Project Settings页面-->配置Group、Artifact、Packaging、Java Version、Package等
- 点击Next转到Dependencies页面-->选择Spring Boot的版本和需要的依赖-->Next-->Finish
- 右键模块-->Add Framework Support...-->选中Maven
2.3 部署Spring Boot项目
jar包部署:
在pom.xml中配置spring-boot-maven-plugin插件,将项目打成jar包
-
控制台执行命令
java -jar xxx.jar
war包部署:
- 让启动类继承SpringBootServletInitializer
- 在pom.xml中配置spring-boot-maven-plugin插件,将项目打成war包
- 在Web服务器(如:Tomcat)中部署war包
3. Spring Boot配置文件
Spring Boot的很多配置都有默认值,可以使用application.properties或application.yml(application.yaml)来修改默认配置。SpringBoot默认从Resource目录加载自定义配置文件。
如果同时存在properties和yaml/yml,则properties优先级高于yaml/yml。
yaml/yml基本语法,参考:https://www.runoob.com/w3cnote/yaml-intro.html
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
-
#
表示注释 - 值前面要加一个空格
- 对象键值对使用冒号结构表示key: value
- 以-开头的行表示构成一个数组
- 纯量包括:字符串、布尔值、整数、浮点数、Null、时间、日期
- &用来建立锚点,*用来引用锚点
3.1 获取配置文件中的值
-
application.yml配置文件内容:
person: name: lhp age: 21 # 纯量数据类型 sex: 男 # 数组数据类型 hobbies: - study - reading # 对象数据类型 pet: name: 狗 food: 骨头 # 参数引用 age: ${person.age}
-
POJO:
@Data public class Pet implements Serializable { private String name; private String food; private Integer age; } // 1. 通过ConfigurationProperties把POJO的属性和yml中key的值自动建立映射关系 // 2. 将POJO交给Spring容器管理(POJO需要有Setter方法) // 3. 使用时直接从Spring容器中获取该POJO // 对象中的属性若有自身,则可能映射失败;如Person中含有Person @Data @Component @ConfigurationProperties(prefix = "person") public class Person implements Serializable { private String name; private Integer age; private String sex; private String[] hobbies; private Pet pet; }
-
获取配置文件中的值:
import com.lhp.demo.pojo.Person; 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.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class TestController { // @Value("${属性名}")只能获取纯量 @Value("${person.hobbies[0]}") private String personHobbies0; // 对象和数组的获取方式相同 @Autowired private Person person; // 通过Environment可以获取纯量 @Autowired private Environment environment; @GetMapping("/scalars") private String scalars() { System.out.println(environment.getProperty("person.hobbies[0]")); return "person.hobbies[0]=" + personHobbies0; } @GetMapping("/object") private String object() { return "person=" + person; } }
3.2 使用指定的环境配置
-
方式1:编写多个配置文件application-xxx
-
application.yml:
# 通过active指定要使用的配置文件 spring: profiles: # active的值为application-后面的部分 active: pro
-
application-dev.yml:
# 开发环境 server: port: 8081
-
application-pro.yml:
# 生产环境 server: port: 8082
-
application-test.yml:
# 测试环境 server: port: 8083
-
-
方式2:在application.yml中使用---来分割
spring: profiles: active: dev --- # 开发环境 server: port: 8081 spring: profiles: dev --- # 生产环境 server: port: 8082 spring: profiles: pro --- # 测试环境 server: port: 8083 spring: profiles: test
-
激活profiles的方式:
方式1:如上,在配置文件中配置spring.profiles.active=环境
-
方式2:运行时指定参数
java -jar xxx.jar --spring.profiles.active=test
-
方式3:配置jvm参数
-Dspring.profiles.active=dev
4. Spring Boot整合其他框架
4.1 整合MyBatis
-
添加依赖:
<!--mybatis的起步依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
-
在application.yml中配置:
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db01?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8 username: root password: 123456 mybatis: # 配置mybatis映射文件的位置 mapper-locations: classpath:com/lhp/integration/dao/*Dao.xml
-
在启动类上添加MapperScan注解,扫描Dao接口所在的包:
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication // MapperScan会扫描指定包下的所有的接口,然后将接口的代理对象交给Spring容器 @MapperScan(basePackages = "com.lhp.integration.dao") public class IntegrationApplication { public static void main(String[] args) { SpringApplication.run(IntegrationApplication.class, args); } }
4.2 整合JUnit
-
添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
测试类所在的包最好在启动类所在的包下
-
低版本Spring Boot使用:
package com.lhp.integration; import com.lhp.integration.dao.UserDao; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class IntegrationApplicationTests { @Autowired private UserDao userDao; @Test public void contextLoads() { System.out.println(userDao.findAll()); } }
-
高版本Spring Boot使用:
package com.lhp.integration; import com.lhp.integration.dao.UserDao; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class IntegrationApplicationTests { @Autowired private UserDao userDao; @Test public void contextLoads() { System.out.println(userDao.findAll()); } }
4.3 整合Redis
-
添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
在application.yml中配置:
spring: redis: host: localhost port: 6379
-
在启动类中配置Redis序列化机制:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @SpringBootApplication public class IntegrationApplication { public static void main(String[] args) { SpringApplication.run(IntegrationApplication.class, args); } // 配置Redis序列化机制 @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); // 设置Key的序列化机制为StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); // 设置value的序列化机制为JdkSerializationRedisSerializer template.setValueSerializer(new JdkSerializationRedisSerializer()); return template; } }
-
使用:
package com.lhp.integration; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; @SpringBootTest public class IntegrationApplicationTests { @Autowired private RedisTemplate redisTemplate; @Test public void redisTest() { redisTemplate.boundValueOps("name").set("lhp"); String name = String.valueOf(redisTemplate.boundValueOps("name").get()); System.out.println(name); } }
RedisTemplate支持的Redis序列化机制(RedisSerializer接口的实现类):
- GenericJackson2JsonRedisSerializer
- GenericToStringSerializer
- Jackson2JsonRedisSerializer
- JdkSerializationRedisSerializer(默认):要求key和value都实现Serializable接口
- OxmSerializer
- StringRedisSerializer
- 自己定义的RedisSerializer接口实现类
5. Spring Boot自动配置的原理
5.1 Condition接口
Condition接口和@Conditional注解是Spring4之后提供的,增加条件判断功能,可以选择性的注入Bean对象到Spring容器中。
自定义Condition实现类,需求:根据是否导入坐标,来选择是否加载Bean
-
是否导入的坐标:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency>
-
POJO:
public class User { }
-
自定义Condition实现类:
package com.lhp.study.condition; import com.lhp.study.annotation.ConditionalOnClass; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map; public class OnClassCondition implements Condition { /** * @param context 上下文信息对象:可以获取环境信息、容器工程、类加载器对象 * @param metadata 注解的元数据:可以获取注解的属性信息 * @return 是否匹配条件,true匹配,false不匹配 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { /*try { Class.forName("redis.clients.jedis.Jedis"); // 如果加载成功,则依赖已经导入,返回true return true; } catch (ClassNotFoundException e) { e.printStackTrace(); // 如果加载失败,则有依赖没有导入,返回false return false; }*/ // 获取注解的属性信息 Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName()); // 获取注解中name的值 String[] names = (String[]) annotationAttributes.get("name"); for (String name : names) { try { Class.forName(name); } catch (ClassNotFoundException e) { e.printStackTrace(); // 如果加载失败,则有依赖没有导入,返回false return false; } } // 如果指定的类都加载成功,则对应的依赖已经导入,返回true return true; } }
-
自定义一个注解:
package com.lhp.study.annotation; import com.lhp.study.condition.OnClassCondition; import org.springframework.context.annotation.Conditional; import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional({OnClassCondition.class}) public @interface ConditionalOnClass { // 类全限定名的字符串数组 String[] name() default {}; }
-
配置类:
package com.lhp.study.config; import com.lhp.study.annotation.ConditionalOnClass; import com.lhp.study.condition.OnClassCondition; import com.lhp.study.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfig { @Bean // @Conditional注解表示一个组件只有在value指定的所有条件都匹配时才有资格注册 // @Conditional(value = OnClassCondition.class) @ConditionalOnClass(name = "redis.clients.jedis.Jedis") public User user() { return new User(); } }
-
在启动类中测试:
package com.lhp.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class StudyApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(StudyApplication.class, args); // 若导入坐标,则打印类信息;若没有导入坐标,则抛出NoSuchBeanDefinitionException System.out.println(context.getBean("user")); } }
在org.springframework.boot.autoconfigure.condition包下有很多Spring已经定义好的Condition实现类和注解,如:
- ConditionalOnBean:当Spring容器中有某一个Bean时使用
- ConditionalOnClass:当目前类路径下有某一个类时使用
- ConditionalOnMissingBean:当Spring容器中没有某一个Bean时使用
- ConditionalOnMissingClass:当目前类路径下没有某一个类时使用
- ConditionalOnProperty:当配置文件中有某一个键值对时使用
5.2 切换内置的Web容器
org.springframework.boot.autoconfigure.web.embedded包下有4种Web容器定制器:
- JettyWebServerFactoryCustomizer
- NettyWebServerFactoryCustomizer
- TomcatWebServerFactoryCustomizer
- UndertowWebServerFactoryCustomizer
在spring-boot-starter-web中排除spring-boot-starter-tomcat依赖,然后添加其他的Web容器依赖即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--在spring-boot-starter-web中排除spring-boot-starter-tomcat依赖-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加其他的Web容器依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
5.3 加载第三方的Bean
@SpringBootApplication上有@SpringBootConfiguration,而@SpringBootConfiguration上有@Configuration,因此启动类本身也是一个配置类,该配置类相当于Spring中的applicationContext.xml文件,用于加载配置使用。
@SpringBootApplication上有@EnableAutoConfiguration,这种@EnableXxx开头的注解是Spring Boot中定义的一些动态启用某些功能的注解,其底层原理实际上是用@import注解导入一些配置,来自动进行配置、加载Bean。
一路追踪源码:
@SpringBootApplication
@EnableAutoConfiguration
@Import({AutoConfigurationImportSelector.class})
String[] selectImports(AnnotationMetadata annotationMetadata)
AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)
List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
META-INF/spring.factories
在spring-boot-autoconfigure-x.x.x.jar中的META-INF/spring.factories文件中就有各种自动配置类
@SpringBootApplication上有@ComponentScan,而@ComponentScan类似于xml中的包扫描context:component-scan;如果不指定扫描路径,则扫描该注解修饰的启动类所在的包及子包。
@Import注解用于导入其他的配置,让Spring容器进行加载和初始化;其使用方式有:
- 直接导入Bean
- 导入配置类
- 导入ImportSelector的实现类
- 导入ImportBeanDefinitionRegistrar实现类
实现加载第三方的Bean示例:创建两个工程enable1和enable2,enable2中有Bean,enable1依赖enable2,期望在enable1中直接使用enable2中的Bean
-
在enable2中创建POJO和配置类:
// POJO package com.lhp.enable2.pojo; public class User { } // 配置类 package com.lhp.enable2.config; import com.lhp.enable2.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfig { @Bean public User user() { return new User(); } }
在enable1中添加第三方依赖enable2的坐标
在enable1的启动类中直接加载使用第三方依赖enable2中的Bean
-
解决NoSuchBeanDefinitionException:
方式1:在enable1的启动类上使用@ComponentScan("com")注解将包扫描路径放大
方式2:在enable1的启动类上使用@Import({UserConfig.class})注解导入配置类
-
方式3:自定义一个注解@EnableUser,然后在enable1的启动类上使用该注解
// 自定义注解 package com.lhp.enable2.config; import org.springframework.context.annotation.Import; import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({UserConfig.class}) public @interface EnableUser { }
-
方式4:自定义一个ImportSelector实现类,然后在enable1的启动类上使用@Import注解导入该实现类;此时不需要配置类
// ImportSelector实现类 package com.lhp.enable2.config; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MyImportSelector implements ImportSelector { /** * @return 需要加载的Bean的全限定名数组 */ @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 全限定名数组可以从配置文件中读取 // 此时Bean的id为全限定名,可以通过getBean("com.lhp.enable2.pojo.User")或getBean(User.class)获取 return new String[]{"com.lhp.enable2.pojo.User"}; } }
-
方式5:自定义一个ImportBeanDefinitionRegistrar实现类,然后在enable1的启动类上使用@Import注解导入该实现类;此时不需要配置类
// ImportBeanDefinitionRegistrar实现类 package com.lhp.enable2.config; import com.lhp.enable2.pojo.User; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { String beanName = "user"; // 创建beanDefinition AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); // 注册Bean registry.registerBeanDefinition(beanName, beanDefinition); } }
5.4 自定义starter起步依赖
需求:当加入jedis的坐标时,自动配置jedis的Bean,加载到Spring容器中
创建起步依赖工程starter,添加spring-boot-starter和jedis的坐标
-
创建配置的POJO:
package com.lhp.starter.pojo; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; @Data @ConfigurationProperties(prefix = "redis") public class RedisProperties { // 给定默认值 private String host = "localhost"; private Integer port = 6379; }
-
创建自动配置类
// 自动配置类 package com.lhp.starter.autoconfigure; import com.lhp.starter.pojo.RedisProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.Jedis; @Configuration // 启用POJO,交给Spring容器管理,与配置文件建立映射关系 @EnableConfigurationProperties(RedisProperties.class) // 当类路径下有Jedis这个类时再使用这个配置类 @ConditionalOnClass(Jedis.class) public class RedisAutoConfiguration { @Bean // 当没有jedis这个Bean时再配置,避免和用户自己的jedis冲突 @ConditionalOnMissingBean(name = "jedis") public Jedis jedis(RedisProperties redisProperties) { System.out.println(redisProperties); return new Jedis(redisProperties.getHost(), redisProperties.getPort()); } }
-
在resources下创建META-INF/spring.factories文件:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.lhp.starter.autoconfigure.RedisAutoConfiguration
在其他工程中加入自定义的starter起步依赖的坐标,便可直接使用自定义starter起步依赖中的Bean
6. Spring Boot监控
6.1 Spring Boot Actuator
Spring Boot Actuator是Spring Boot自带的组件,可以监控Spring Boot应用,如健康检查、审计、统计、HTTP追踪等
使用Spring Boot Actuator:
-
添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
在application.yml中配置:
management: # 配置健康端点开启所有详情信息 endpoint: health: show-details: always # 设置开放所有web相关的端点信息 endpoints: web: exposure: include: "*" # 设置info前缀的信息 info: name: lhp age: 21
部分监控路径列表:
- /beans:描述应用程序上下文里全部的Bean及它们的关系
- /env:获取全部环境属性
- /env/{name}:根据名称获取特定的环境属性值
- /health:报告应用程序的健康指标,这些值由HealthIndicator的实现类提供
- /info:获取应用程序的定制信息,这些信息由info打头的属性提供
- /mappings:描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系
- /metrics:报告各种应用程序度量信息,比如内存用量和HTTP请求计数
- /metrics/{name}:报告指定名称的应用程序度量值
- /trace:提供基本的HTTP请求跟踪信息(时间戳、HTTP头等)
6.2 Spring Boot Admin
Spring Boot Admin是一个开源社区项目;它有两个角色,Client和Server;应用程序作为Client向Server注册;Server可以通过图形化界面展示Client的监控信息
- 每一个Spring Boot工程都是一个Client
- Server可以收集统计所有相关Client注册过来的信息,并进行汇总展示
使用Spring Boot Admin Server:
-
创建server工程,导入坐标:
<properties> <java.version>1.8</java.version> <spring-boot-admin.version>2.3.1</spring-boot-admin.version> </properties> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-dependencies</artifactId> <version>${spring-boot-admin.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
在启动类上添加@EnableAdminServer注解来启用Server功能
-
在application.yml中配置:
server: port: 8088
使用Spring Boot Admin Client:
-
创建server工程,导入坐标:
<properties> <java.version>1.8</java.version> <spring-boot-admin.version>2.3.1</spring-boot-admin.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-dependencies</artifactId> <version>${spring-boot-admin.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
-
在application.yml中配置:
spring: # 配置server的地址 boot: admin: client: url: http://localhost:8088 # 配置系统名称 application: name: client management: endpoint: health: # 启用健康检查(默认就是true) enabled: true # 配置显示所有的监控详情 show-details: always endpoints: web: # 开放所有端点 exposure: include: "*"
浏览器访问服务端地址:http://localhost:8088/