1. Spring Boot簡(jiǎn)介
Spring Boot的設(shè)計(jì)目的是用來簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開發(fā)過程。SpringBoot所具備的特征有:
- 可以創(chuàng)建獨(dú)立的Spring應(yīng)用程序凉馆,并且基于其Maven或Gradle插件已日,可以創(chuàng)建可執(zhí)行的JARs和WARs
- 內(nèi)嵌Tomcat或Jetty等Servlet容器
- 提供自動(dòng)配置的“starter”項(xiàng)目對(duì)象模型(POMS)以簡(jiǎn)化Maven配置
- 盡可能自動(dòng)配置Spring容器
- 提供準(zhǔn)備好的特性蒋腮,如指標(biāo)、健康檢查和外部化配置
- 絕對(duì)沒有代碼生成腻豌,不需要XML配置
Spring Boot官網(wǎng):https://spring.io/projects/spring-boot/
Spring Boot啟動(dòng)結(jié)構(gòu)圖(圖片太大了引谜,可單獨(dú)查看圖片):
Spring Boot啟動(dòng)過程:
一路追蹤源碼:
SpringApplication.run()
// 先new對(duì)象厦画,然后run
new SpringApplication(primarySources).run(args)
// new對(duì)象的構(gòu)造方法
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環(huán)境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
// 創(chuàng)建初始化構(gòu)造器疮茄,加載spring.factories文件中ApplicationContextInitializer對(duì)應(yīng)的所有類
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 創(chuàng)建應(yīng)用監(jiān)聽器,加載spring.factories文件中ApplicationListener對(duì)應(yīng)的所有類
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 配置應(yīng)用的主方法所在類
this.mainApplicationClass = deduceMainApplicationClass();
}
// run方法:運(yùn)行Spring應(yīng)用程序根暑,創(chuàng)建并刷新新的應(yīng)用程序上下文
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
// 應(yīng)用啟動(dòng)計(jì)時(shí)器開始計(jì)時(shí)
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 獲取spring.factories文件中SpringApplicationRunListener對(duì)應(yīng)的監(jiān)聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 廣播事件力试,應(yīng)用啟動(dòng)監(jiān)聽器開始監(jiān)聽
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 準(zhǔn)備環(huán)境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 應(yīng)用啟動(dòng)時(shí)的提示圖標(biāo),可以在resources下創(chuàng)建一個(gè)banner.txt來修改啟動(dòng)時(shí)的打印信息
Banner printedBanner = printBanner(environment);
// 創(chuàng)建新的應(yīng)用程序上下文
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 準(zhǔn)備新的應(yīng)用程序上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新新的應(yīng)用程序上下文
refreshContext(context);
afterRefresh(context, applicationArguments);
// 應(yīng)用啟動(dòng)計(jì)時(shí)器結(jié)束計(jì)時(shí)
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 上下文已經(jīng)刷新排嫌,應(yīng)用程序已經(jīng)啟動(dòng)
listeners.started(context);
// 調(diào)用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. 創(chuàng)建Spring Boot工程
需求:在頁面展示hello, world
2.1 手工創(chuàng)建
創(chuàng)建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中定義了各個(gè)依賴的版本 --> <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>
-
創(chuàng)建啟動(dòng)類
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); } }
-
編寫業(yè)務(wù)代碼:
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"; } }
運(yùn)行啟動(dòng)類的main方法
2.2 通過Spring Initializr創(chuàng)建
在IDEA中創(chuàng)建工程時(shí)使用Spring Initializr
- 點(diǎn)擊Next轉(zhuǎn)到Spring Initializr Project Settings頁面-->配置Group、Artifact淳地、Packaging怖糊、Java Version、Package等
- 點(diǎn)擊Next轉(zhuǎn)到Dependencies頁面-->選擇Spring Boot的版本和需要的依賴-->Next-->Finish
- 右鍵模塊-->Add Framework Support...-->選中Maven
2.3 部署Spring Boot項(xiàng)目
jar包部署:
在pom.xml中配置spring-boot-maven-plugin插件颇象,將項(xiàng)目打成jar包
-
控制臺(tái)執(zhí)行命令
java -jar xxx.jar
war包部署:
- 讓啟動(dòng)類繼承SpringBootServletInitializer
- 在pom.xml中配置spring-boot-maven-plugin插件伍伤,將項(xiàng)目打成war包
- 在Web服務(wù)器(如:Tomcat)中部署war包
3. Spring Boot配置文件
Spring Boot的很多配置都有默認(rèn)值,可以使用application.properties或application.yml(application.yaml)來修改默認(rèn)配置遣钳。SpringBoot默認(rèn)從Resource目錄加載自定義配置文件扰魂。
如果同時(shí)存在properties和yaml/yml,則properties優(yōu)先級(jí)高于yaml/yml蕴茴。
yaml/yml基本語法劝评,參考:https://www.runoob.com/w3cnote/yaml-intro.html
- 大小寫敏感
- 使用縮進(jìn)表示層級(jí)關(guān)系
- 縮進(jìn)不允許使用tab,只允許空格
- 縮進(jìn)的空格數(shù)不重要荐开,只要相同層級(jí)的元素左對(duì)齊即可
-
#
表示注釋 - 值前面要加一個(gè)空格
- 對(duì)象鍵值對(duì)使用冒號(hào)結(jié)構(gòu)表示key: value
- 以-開頭的行表示構(gòu)成一個(gè)數(shù)組
- 純量包括:字符串付翁、布爾值简肴、整數(shù)晃听、浮點(diǎn)數(shù)、Null砰识、時(shí)間能扒、日期
- &用來建立錨點(diǎn),*用來引用錨點(diǎn)
3.1 獲取配置文件中的值
-
application.yml配置文件內(nèi)容:
person: name: lhp age: 21 # 純量數(shù)據(jù)類型 sex: 男 # 數(shù)組數(shù)據(jù)類型 hobbies: - study - reading # 對(duì)象數(shù)據(jù)類型 pet: name: 狗 food: 骨頭 # 參數(shù)引用 age: ${person.age}
-
POJO:
@Data public class Pet implements Serializable { private String name; private String food; private Integer age; } // 1. 通過ConfigurationProperties把POJO的屬性和yml中key的值自動(dòng)建立映射關(guān)系 // 2. 將POJO交給Spring容器管理(POJO需要有Setter方法) // 3. 使用時(shí)直接從Spring容器中獲取該P(yáng)OJO // 對(duì)象中的屬性若有自身辫狼,則可能映射失敵醢摺;如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; // 對(duì)象和數(shù)組的獲取方式相同 @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 使用指定的環(huán)境配置
-
方式1:編寫多個(gè)配置文件application-xxx
-
application.yml:
# 通過active指定要使用的配置文件 spring: profiles: # active的值為application-后面的部分 active: pro
-
application-dev.yml:
# 開發(fā)環(huán)境 server: port: 8081
-
application-pro.yml:
# 生產(chǎn)環(huán)境 server: port: 8082
-
application-test.yml:
# 測(cè)試環(huán)境 server: port: 8083
-
-
方式2:在application.yml中使用---來分割
spring: profiles: active: dev --- # 開發(fā)環(huán)境 server: port: 8081 spring: profiles: dev --- # 生產(chǎn)環(huán)境 server: port: 8082 spring: profiles: pro --- # 測(cè)試環(huán)境 server: port: 8083 spring: profiles: test
-
激活profiles的方式:
方式1:如上膨处,在配置文件中配置spring.profiles.active=環(huán)境
-
方式2:運(yùn)行時(shí)指定參數(shù)
java -jar xxx.jar --spring.profiles.active=test
-
方式3:配置jvm參數(shù)
-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驅(qū)動(dòng)--> <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
-
在啟動(dòng)類上添加MapperScan注解见秤,掃描Dao接口所在的包:
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication // MapperScan會(huì)掃描指定包下的所有的接口砂竖,然后將接口的代理對(duì)象交給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>
測(cè)試類所在的包最好在啟動(dòng)類所在的包下
-
低版本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
-
在啟動(dòng)類中配置Redis序列化機(jī)制:
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序列化機(jī)制 @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); // 設(shè)置Key的序列化機(jī)制為StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); // 設(shè)置value的序列化機(jī)制為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序列化機(jī)制(RedisSerializer接口的實(shí)現(xiàn)類):
- GenericJackson2JsonRedisSerializer
- GenericToStringSerializer
- Jackson2JsonRedisSerializer
- JdkSerializationRedisSerializer(默認(rèn)):要求key和value都實(shí)現(xiàn)Serializable接口
- OxmSerializer
- StringRedisSerializer
- 自己定義的RedisSerializer接口實(shí)現(xiàn)類
5. Spring Boot自動(dòng)配置的原理
5.1 Condition接口
Condition接口和@Conditional注解是Spring4之后提供的,增加條件判斷功能鹃答,可以選擇性的注入Bean對(duì)象到Spring容器中乎澄。
自定義Condition實(shí)現(xiàn)類,需求:根據(jù)是否導(dǎo)入坐標(biāo)测摔,來選擇是否加載Bean
-
是否導(dǎo)入的坐標(biāo):
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency>
-
POJO:
public class User { }
-
自定義Condition實(shí)現(xiàn)類:
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 上下文信息對(duì)象:可以獲取環(huán)境信息置济、容器工程、類加載器對(duì)象 * @param metadata 注解的元數(shù)據(jù):可以獲取注解的屬性信息 * @return 是否匹配條件锋八,true匹配浙于,false不匹配 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { /*try { Class.forName("redis.clients.jedis.Jedis"); // 如果加載成功,則依賴已經(jīng)導(dǎo)入挟纱,返回true return true; } catch (ClassNotFoundException e) { e.printStackTrace(); // 如果加載失敗羞酗,則有依賴沒有導(dǎo)入,返回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(); // 如果加載失敗樊销,則有依賴沒有導(dǎo)入整慎,返回false return false; } } // 如果指定的類都加載成功,則對(duì)應(yīng)的依賴已經(jīng)導(dǎo)入围苫,返回true return true; } }
-
自定義一個(gè)注解:
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 { // 類全限定名的字符串?dāng)?shù)組 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注解表示一個(gè)組件只有在value指定的所有條件都匹配時(shí)才有資格注冊(cè) // @Conditional(value = OnClassCondition.class) @ConditionalOnClass(name = "redis.clients.jedis.Jedis") public User user() { return new User(); } }
-
在啟動(dòng)類中測(cè)試:
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); // 若導(dǎo)入坐標(biāo)裤园,則打印類信息;若沒有導(dǎo)入坐標(biāo)剂府,則拋出NoSuchBeanDefinitionException System.out.println(context.getBean("user")); } }
在org.springframework.boot.autoconfigure.condition包下有很多Spring已經(jīng)定義好的Condition實(shí)現(xiàn)類和注解拧揽,如:
- ConditionalOnBean:當(dāng)Spring容器中有某一個(gè)Bean時(shí)使用
- ConditionalOnClass:當(dāng)目前類路徑下有某一個(gè)類時(shí)使用
- ConditionalOnMissingBean:當(dāng)Spring容器中沒有某一個(gè)Bean時(shí)使用
- ConditionalOnMissingClass:當(dāng)目前類路徑下沒有某一個(gè)類時(shí)使用
- ConditionalOnProperty:當(dāng)配置文件中有某一個(gè)鍵值對(duì)時(shí)使用
5.2 切換內(nèi)置的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淤袜,因此啟動(dòng)類本身也是一個(gè)配置類,該配置類相當(dāng)于Spring中的applicationContext.xml文件衰伯,用于加載配置使用铡羡。
@SpringBootApplication上有@EnableAutoConfiguration,這種@EnableXxx開頭的注解是Spring Boot中定義的一些動(dòng)態(tài)啟用某些功能的注解意鲸,其底層原理實(shí)際上是用@import注解導(dǎo)入一些配置烦周,來自動(dòng)進(jìn)行配置、加載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文件中就有各種自動(dòng)配置類
@SpringBootApplication上有@ComponentScan读慎,而@ComponentScan類似于xml中的包掃描context:component-scan;如果不指定掃描路徑槐雾,則掃描該注解修飾的啟動(dòng)類所在的包及子包夭委。
@Import注解用于導(dǎo)入其他的配置,讓Spring容器進(jìn)行加載和初始化募强;其使用方式有:
- 直接導(dǎo)入Bean
- 導(dǎo)入配置類
- 導(dǎo)入ImportSelector的實(shí)現(xiàn)類
- 導(dǎo)入ImportBeanDefinitionRegistrar實(shí)現(xiàn)類
實(shí)現(xiàn)加載第三方的Bean示例:創(chuàng)建兩個(gè)工程enable1和enable2株灸,enable2中有Bean崇摄,enable1依賴enable2,期望在enable1中直接使用enable2中的Bean
-
在enable2中創(chuàng)建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的坐標(biāo)
在enable1的啟動(dòng)類中直接加載使用第三方依賴enable2中的Bean
-
解決NoSuchBeanDefinitionException:
方式1:在enable1的啟動(dòng)類上使用@ComponentScan("com")注解將包掃描路徑放大
方式2:在enable1的啟動(dòng)類上使用@Import({UserConfig.class})注解導(dǎo)入配置類
-
方式3:自定義一個(gè)注解@EnableUser慌烧,然后在enable1的啟動(dòng)類上使用該注解
// 自定義注解 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:自定義一個(gè)ImportSelector實(shí)現(xiàn)類配猫,然后在enable1的啟動(dòng)類上使用@Import注解導(dǎo)入該實(shí)現(xiàn)類;此時(shí)不需要配置類
// ImportSelector實(shí)現(xiàn)類 package com.lhp.enable2.config; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MyImportSelector implements ImportSelector { /** * @return 需要加載的Bean的全限定名數(shù)組 */ @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 全限定名數(shù)組可以從配置文件中讀取 // 此時(shí)Bean的id為全限定名杏死,可以通過getBean("com.lhp.enable2.pojo.User")或getBean(User.class)獲取 return new String[]{"com.lhp.enable2.pojo.User"}; } }
-
方式5:自定義一個(gè)ImportBeanDefinitionRegistrar實(shí)現(xiàn)類泵肄,然后在enable1的啟動(dòng)類上使用@Import注解導(dǎo)入該實(shí)現(xiàn)類;此時(shí)不需要配置類
// ImportBeanDefinitionRegistrar實(shí)現(xiàn)類 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"; // 創(chuàng)建beanDefinition AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); // 注冊(cè)Bean registry.registerBeanDefinition(beanName, beanDefinition); } }
5.4 自定義starter起步依賴
需求:當(dāng)加入jedis的坐標(biāo)時(shí)淑翼,自動(dòng)配置jedis的Bean腐巢,加載到Spring容器中
創(chuàng)建起步依賴工程starter,添加spring-boot-starter和jedis的坐標(biāo)
-
創(chuàng)建配置的POJO:
package com.lhp.starter.pojo; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; @Data @ConfigurationProperties(prefix = "redis") public class RedisProperties { // 給定默認(rèn)值 private String host = "localhost"; private Integer port = 6379; }
-
創(chuàng)建自動(dòng)配置類
// 自動(dòng)配置類 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容器管理冯丙,與配置文件建立映射關(guān)系 @EnableConfigurationProperties(RedisProperties.class) // 當(dāng)類路徑下有Jedis這個(gè)類時(shí)再使用這個(gè)配置類 @ConditionalOnClass(Jedis.class) public class RedisAutoConfiguration { @Bean // 當(dāng)沒有jedis這個(gè)Bean時(shí)再配置,避免和用戶自己的jedis沖突 @ConditionalOnMissingBean(name = "jedis") public Jedis jedis(RedisProperties redisProperties) { System.out.println(redisProperties); return new Jedis(redisProperties.getHost(), redisProperties.getPort()); } }
-
在resources下創(chuàng)建META-INF/spring.factories文件:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.lhp.starter.autoconfigure.RedisAutoConfiguration
在其他工程中加入自定義的starter起步依賴的坐標(biāo)遭京,便可直接使用自定義starter起步依賴中的Bean
6. Spring Boot監(jiān)控
6.1 Spring Boot Actuator
Spring Boot Actuator是Spring Boot自帶的組件胃惜,可以監(jiān)控Spring Boot應(yīng)用,如健康檢查哪雕、審計(jì)船殉、統(tǒng)計(jì)、HTTP追蹤等
使用Spring Boot Actuator:
-
添加依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
在application.yml中配置:
management: # 配置健康端點(diǎn)開啟所有詳情信息 endpoint: health: show-details: always # 設(shè)置開放所有web相關(guān)的端點(diǎn)信息 endpoints: web: exposure: include: "*" # 設(shè)置info前綴的信息 info: name: lhp age: 21
部分監(jiān)控路徑列表:
- /beans:描述應(yīng)用程序上下文里全部的Bean及它們的關(guān)系
- /env:獲取全部環(huán)境屬性
- /env/{name}:根據(jù)名稱獲取特定的環(huán)境屬性值
- /health:報(bào)告應(yīng)用程序的健康指標(biāo)斯嚎,這些值由HealthIndicator的實(shí)現(xiàn)類提供
- /info:獲取應(yīng)用程序的定制信息利虫,這些信息由info打頭的屬性提供
- /mappings:描述全部的URI路徑,以及它們和控制器(包含Actuator端點(diǎn))的映射關(guān)系
- /metrics:報(bào)告各種應(yīng)用程序度量信息堡僻,比如內(nèi)存用量和HTTP請(qǐng)求計(jì)數(shù)
- /metrics/{name}:報(bào)告指定名稱的應(yīng)用程序度量值
- /trace:提供基本的HTTP請(qǐng)求跟蹤信息(時(shí)間戳糠惫、HTTP頭等)
6.2 Spring Boot Admin
Spring Boot Admin是一個(gè)開源社區(qū)項(xiàng)目;它有兩個(gè)角色钉疫,Client和Server硼讽;應(yīng)用程序作為Client向Server注冊(cè);Server可以通過圖形化界面展示Client的監(jiān)控信息
- 每一個(gè)Spring Boot工程都是一個(gè)Client
- Server可以收集統(tǒng)計(jì)所有相關(guān)Client注冊(cè)過來的信息牲阁,并進(jìn)行匯總展示
使用Spring Boot Admin Server:
-
創(chuàng)建server工程固阁,導(dǎo)入坐標(biāo):
<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>
在啟動(dòng)類上添加@EnableAdminServer注解來啟用Server功能
-
在application.yml中配置:
server: port: 8088
使用Spring Boot Admin Client:
-
創(chuàng)建server工程,導(dǎo)入坐標(biāo):
<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 # 配置系統(tǒng)名稱 application: name: client management: endpoint: health: # 啟用健康檢查(默認(rèn)就是true) enabled: true # 配置顯示所有的監(jiān)控詳情 show-details: always endpoints: web: # 開放所有端點(diǎn) exposure: include: "*"
瀏覽器訪問服務(wù)端地址:http://localhost:8088/