Spring Boot 解決快啟動與運行Spring項目的問題,Spring配置依賴比較繁瑣。它不是對Spring功能上的增強,而是提供了一種快速使用Spring的方式。
- 自動配置:項目啟動時,配置自動選擇
- 起步依賴:使用Maven項目對象模型,將一些搭配使用的包打包在一起
- 其他:提供一些大型項目中常用的非功能特性,如嵌入式服務器
起步依賴原理
1. spring-boot-starter-parent
定義了各類技術的版本信息,組合了一套最優搭配的功能版本。在各種starts中定義了該功能所需的做標記he,大部分版本信息來自父工程,因此並不會導致版本沖突
2. spring-boot-starter-web
Sprint Boot 配置文件
定義
properties:鍵值對形式,參數=值。是默認的配置文件
yml / yaml:參數.小參數: 空格 (冒號后面是有空格的)
- yml 以數據為核心,比傳統的xml方式更簡潔,不用寫標簽;比簡介的properties更完整,可以看到上層參數
- 大小寫敏感;數據值前有空格;使用縮進代表層級關系,且縮進不允許用 tab,只能用空格(IDEA無需關注);#代表空格
- 數據格式
#對象
person:
name: zhangsan
#或者使用行內寫法,少用
person: {name: zhangsan}
#數組
address:
- bejing #帶空格
- shanghai
#行內寫法
address:{bejing,shanghai}
#純量(常量)
msg: 'hello \n word' #使用單引號,忽略轉義字符,直接輸出\n
msg: "hello \n word" #使用雙引號,識別轉義字符,\n識別為換行
- 參數引用:${ arg }
三種配置文件優先級:properties>yml>yaml。若配置文件中配置了相同的參數,則讀取優先級更高的
讀取
- @Value:使用注解在代碼的變量中引用,如:@Value("${name}") private String name;。單個引用,數量較多時比較繁雜
- Environment對象:environment對象+Autowired注解。只需注入一次就可以全局使用。如:@Autowired private Environment environment; 引用參數時直接使用其getProperty即可
- @CpnfigurationProperties:將一個實體類作為配置文件類。實體類的變量對應配置文件中的參數。但要注意,使用這種方法獲取參數時,名字需要完整,如果想獲取二級參數,需要在添加前綴,前綴就是一級參數名,例如獲取person下的所有參數,應寫成這樣:@ConfigurationProperties(prefix = "person")。這樣在使用get方法時,就會准確定位到想要的參數
補充知識:
@Component:確保被Spring Boot所識別,是一個Been
添加@CpnfigurationProperties時配置文件會報紅,添加以下依賴即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>true</scope>
</dependency>
profile配置
properties方式
當存在多個配置文件時,需要激活后方可使用,否則就使用默認的,可以看到,未激活時項目啟動顯示:
2021-12-26 14:23:02.165 INFO 1420 --- [ main] c.e.s.SpringbootProfileApplication : No active profile set, falling back to default profiles: default
而當需要另一個配置文件生效時(激活),在application.properties文件中配置:spring.profiles.active=dev 即可。這里的dev就是配置文件的標記。當配置多個配置文件時,文件命名應按照如下格式,application后應該使用-來區分環境:
application.properties application-dev.properties application-prd.properties application-test.properties
標記某配置文件后,讀取配置時就會選擇所標記的文件,而啟動項目時,也會顯示日志:
2021-12-26 14:26:47.815 INFO 7808 --- [ main] c.e.s.SpringbootProfileApplication : The following profiles are active: dev
yml方式
yml內部使用 --- 三條短杠為分隔符將一個yml文件分為多個部分,環境名使用spring.profiles:xxx 來區分,激活時用spring.profile.active:xxx。這樣使得在一個yml配置文件中就存在不同的配置環境
補充:
激活方式②:虛擬機命令激活:在右上角配置一欄選擇設置配置文件,並在VM options中填寫:-Dspring.profiles.active=xxx 這樣也可以激活配置文件,並且優先級比配置文件要高
激活方式③:命令行激活:在運行jar包時帶入外部參數:--spring.profiles.active=prd 即可切換不同的環境
綜上所述,boot項目的配置文件既有內部的,也有外部的。因此他們的加載順序自然是不同的。這里列舉一些內部位置:
- file:./config/:當前項目下的/config目錄——即在當前項目的根目錄下創建一個config目錄,里面的配置文件是優先級最高的
- file:./:當前項目的根目錄 ——即當前項目根目錄,在IDEA頁面使用Project File頁面新建一個配置文件,這個配置文件的優先級是2,僅次於1
- classpath:/config/:classpath下的/config/目錄:即resource/config/目錄下的配置文件,/config文件本是沒有的,但創建后,里面的配置文件優先級就比外部根目錄的要高了
- classpath/:classpath的根目錄——即resource下的application.properties文件,默認的配置文件(yml也是)
- 注意:由於file:./config/和file:/下的文件並不符合maven的結構,所以不會打進jar包
外部位置。官方定義了十多個外部可引用的位置,但實際用到的卻不多:
- 命令行(會比上面的4、5優先級高)
- 在運行jar包時指定啟動參數 E:\Work\Workspace\springboot-config\target>java -jar springboot-config-0.0.1-SNAPSHOT.jar --server.port=8079
- 也可以在運行jar包時指定外部配置文件 E:\Work\Workspace\springboot-config\target>java -jar springboot-config-0.0.1-SNAPSHOT.jar --spring.config.location=e://application.properties
- 將配置文件直接放在於jar包同級的目錄下,則會自動讀取
- 建立與jar包同級的/config/application.properties目錄文件,也可以讀取到
E:\Work\Workspace\springboot-config\target>java -jar springboot-config-0.0.1-SNAPSHOT.jar --spring.config.location=e://application.properties . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.6.2) 2021-12-26 15:41:46.188 INFO 4932 --- [ main] c.e.s.SpringbootConfigApplication : Starting SpringbootConfigApplication v0.0.1-SNAPSHOT using Java 1.8.0_181 on DESKTOP-FPG6BLH with PID 4932 (E:\Work\Workspace\springboot-config\target\springboot-config-0.0.1-SNAPSHOT.jar started by Administrator in E:\Work\Workspace\springboot-config\target) 2021-12-26 15:41:46.201 INFO 4932 --- [ main] c.e.s.SpringbootConfigApplication : No active profile set, falling back to default profiles: default 2021-12-26 15:41:47.706 INFO 4932 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8078 (http)
……
springboot設置這么多配置的原因就是在項目啟動后如果配置錯誤,可以使用外部配置來替代內部配置。而無需修改內部配置
(說起來,如果配置有誤,難道不要重啟項目才會讀取嗎?既然都重啟了,為啥不直接改呢(可能是熱部署模式下可以不用重啟項目就可以讀取新配置吧!))
整合Junit
整合框架需要注意的點
- 該打的注解不能少,Service層需要加@Service,測試主類需要加@SpringBootTest,並指定對應的主類:@SpringBootTest(classes = SpringbootJunitApplication.class)
- 如果測試所在的包與主類所在的包是同一級或者在它的子包,就無需使用SpringBootTest去指定主類
- 由快速構建出來的Spring Boot項目,它的測試包和主類包是一致的(flag:個人還未驗證)
整合MyBatis
(補充)
SprintBoot切換內置服務器
配置類:EmbeddedWebServerFactoryCustomizerAutoConfiguration
注:可在pom.xml右鍵選擇diagrams查看編輯里面的內容
<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> <!-- 加入jetty依賴 --> <dependency> <artifactId>spring-boot-starter-jetty</artifactId> <groupId>org.springframework.boot</groupId> </dependency>
選擇性的創建Bean:Condition 注解
在Sprintboot中獲取Bean
//啟動SpringBoot,獲取Ioc容器 ConfigurableApplicationContext context = SpringApplication.run(SprintbootMyUnApplication.class, args); //當字節碼文件存在時,創建bean后在這里可以獲取到userbaen Object user = context.getBean("user"); System.out.println(user);
介紹:條件接口——Condition
@FunctionalInterface
//實現該接口並重寫matches方法,自定義判斷條件
public interface Condition {
//返回值是一個boolean值,為true則創建bean,為false不創建
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
實現條件接口,並重寫方法:
/** * 自定義的條件類,根據類是否存在決定是否創建bean * 實現Condition接口,重寫matches方法,並在里面進行邏輯判斷 * 返回boolean值,true則創建,false不創建 */ public class ClassCondition implements Condition { /** * @param context 上下文對象,用於獲取環境,IOC容器,Classloader對象 * @param metadata 注解元對象,用於獲取注解定義的屬性值 * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { boolean flag = true; //1.根據是否導入指定依賴坐標來判斷是否創建User Bean // try { // //判斷對應文件是否存在 // Class<?> aClass = Class.forName("redis.clients.jedis.Jedis"); // } catch (ClassNotFoundException e) { // flag = false; // } //2.導入通過注解屬性值val指定坐標創建bean Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionOnClass.class.getName()); String[] val = (String[])annotationAttributes.get("val"); try { for (String className : val) { //判斷對應文件是否存在 Class<?> aClass = Class.forName(className); } } catch (ClassNotFoundException e) { flag = false; } return flag; } }
sprintboot自動配置包:org.sprintbootframework.boot.autoconfigure,其中的condition包內有多個ConditionOnxxxx的類,就是按照各種條件來決定是否創建Bean的方法
按照properties內是否存在某屬性的鍵值對來檢測是否創建該bean
@Bean @ConditionalOnProperty(name = "xiaozhou", havingValue = "zzz") //當name和value的值存在時,才會創建這個bean
//獲取baen的時候以這個方法名為准
public User user(){
return new User(); }
總結:
- 定義條件類:自定義實現Condition接口,重寫matches方法,在matches方法中進行邏輯判斷,返回boolean值
- 判斷條件:在初始化Bean時,使用@Conditional(條件類.class)注解
自動配置的關鍵:Enable注解
Springboot中提供了很多Enable開頭的注解,這些注解都是用於動態啟動某些功能的,而底層原理是使用@Import注解導入一些類,實現Bean的動態加載
@EnableAutoConfiguration
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) //使用import注解導入配置類,實現Bean的動態加載 public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
手動掃描包,讓SpringBoot添加其為Bean掃描范圍
/** * ComponentScan掃描范圍是當前引導類所在包以及其子包 * 如果不是,可使用以下方法獲取bean * 1.在當前啟動類上添加@ComponentScan("包名“) * 2.使用@Import注解加載類,這些類都會被Sprint創建,並放入Ioc容器 * 3.對Import注解進行封裝,讓sprintboot識別到並加載進來 */ @SpringBootApplication //@ComponentScan("com.yz.config") //@Import(UserConfig.class) @EnableUser //在EnableUser中封裝Import public class SprintbootMyUnApplication {
………………
}
Enbale注解
import com.yz.config.UserConfig; import org.springframework.context.annotation.Import; import java.lang.annotation.*; //這三個是Import的元注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(UserConfig.class) //在這里添配置類 public @interface EnableUser { }