目錄
一,spring boot簡述
springboot是什麼?簡單地理解它是一個工具箱。它不是重複的造輪子,底層用的還是spring的那一套,不同的是它讓spring的使用更爲簡單,讓開發者真正的做到了開箱即用,降低了使用spring的成本;此刻我想到了當初被spring支配的恐懼,你需要在web.xml中配置dispatherServlet,還有各種listener,如果使用了springmvc, 還需要建立一個spring-mvc.xml裏面再配置相應的視圖解析器還有包掃描等等,對於一個小白,或者只是想用來建個項目進行學習的人來說真的是太不友好了,於是Spring Boot出現了,我能想到的它的優點如下:
1,解決了各種依賴的問題,通過繼承springboot parent讓自身的項目具有各種版本控制,
你只需要確定要使用哪個版本的springboot就行了,剩下的絕大多數依賴都是現成的,
再也不會出現以前因爲版本不兼容出現各種bug的問題。
2,基於約定優於配置的原則,你的類路徑引入了哪些jar包就會幫開發者自動裝配相應的環境,
只需要對自身特定的需求進行小部分修改工作,真正的開箱即用。
3,很多的starter,涵蓋了開發中的方方面面的需要,比如web的,jdbc的等,
使用這些starter的好處便是開發者不用再關心底層需要哪些jar包,要哪些版本,
可以專心於業務的開發而不是環境的搭建。
4,內置tomcat容器,可以像運行一個普通的java項目一樣來開發web項目。
還有好多......
二,spring boot項目的搭建
基於maven構建web項目,jdk選擇1.8, maven3.5,ide選擇idea, spring boot版本選擇2.1.4.RELEASE
2.1 pom文件
在pom.xml中引入下面的依賴,當然,創建springboot項目的方式有很多,比如直接去官網選擇相應的版本下載;通過idea直接構建springboot項目等等,本人比較喜歡手動引入,因爲這樣對項目中用到了什麼,以及項目的結構會比較清楚。pom文件如下:
<?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.mytest</groupId>
<artifactId>springboot418</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
上面的pom中引入spring-boot-starter-web這樣很容易就能使用springmvc的功能,而下面的lombok則是爲了省略一些getter, setter, toString方法的實現;最下面的spring-boot-maven-plugin是spring boot提供的maven插件,它可以方便的將spring boot項目打包爲jar包或者war包。(生成的jar包可以使用java -jar 命令直接運行)
2.2 主配置類
創建一個application.java文件,內容如下:
@SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
這樣就完事了,通過運行這個main方法,你的spring boot項目就直接啓動了,但是爲了更加明顯,我們可以添加一個controller來實現web訪問,修改上面的主配置類:
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
@GetMapping("/hello")
public String sayHello(){
return "hello world!";
}
}
啓動之後訪問http://localhost:8080/hello,就能獲得服務端"hello world"的迴應,需要注意的是在2.1.4這個版本,控制檯並不會打印出controller映射的路徑信息,此時可以通過在配置文件application.yml/properties中設置,如下:
logging:
level:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping:
trace
2.3 @SpringBootApplication
通常在學習springboot的時候,我們都知道這個註解需要標註在springboot的啓動類上,它又用什麼作用呢?實際上它是一個組合註解,
從springboot的官方文檔我們可以發現:
它主要包含@EnableAutoConfiguration, 開啓自動配置;@ComponentScan, 開啓包掃描, @Configuration表示這是一個配置類;
意思就是說,你可以通過使用上面3個註解對@SpringBootApplication進行替換,說幹就幹,修改主配置class:
@Configuration
@EnableAutoConfiguration
@ComponentScan
@RestController
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
@GetMapping("/hello")
public String sayHello(){
return "hello world!";
}
}
之後重新執行,訪問,會發現一切都正常,但是需要注意的是,雖然官方文檔上說明"with their default attributes",但是當你查看@SpringBootApplication註解你會發現有些不一樣,如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {//省略內容}
@ComponentScan註解並沒有使用默認屬性,而是使用了過濾器,它的作用就是遇到滿足情況的bean不進行掃描。FileType分爲以下幾種類型:
public enum FilterType {
//基於註解
ANNOTATION,
//按照指定類型
ASSIGNABLE_TYPE,
//基於aspectJ
ASPECTJ,
//基於正則表達式
REGEX,
//自定義
CUSTOM
}
當我們選擇了FilterType.CUSTOM,意思就是需要自定義過濾規則,此時我們可以通過實現TypeFilter接口定義自己的過濾規則,如下,建立一個MyFilter:
public class MyFilter implements TypeFilter {
/**
* @param metadataReader the metadata reader for the target class
* @param metadataReaderFactory a factory for obtaining metadata readers
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//獲取類上註解信息
//metadataReader.getAnnotationMetadata();
//獲取類信息
boolean result = metadataReader.getClassMetadata().getClassName().contains("Student");
System.out.println("#################result is " + result + "##############");
return result;
}
}
其中metadataReader包含有掃描到目標類的信息,它主要的方法有獲取描述類的元信息,和類上的註解信息,需要注意的是它不代表掃描到的類,所以要獲得描述的類的信息,需要使用相應的getMetadataXXX方法,而不是直接metadataReader.getClass()。
此處的邏輯很簡單,當掃描到的類名中包含有“Student”則匹配上,即略過它,不會向ioc容器裏注入。
建立一個component以Student**爲名,如下:
@Component
public class StudentComponent {
}
修改啓動類:
@Configuration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyFilter.class)})
@RestController
public class Application {
@Autowired
private StudentComponent student;
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
@GetMapping("/hello")
public String sayHello(){
return "hello world!" + student;
}
}
如上所示,我們把自己的過濾器配到了@ComponentScan的excludeFilters中, 爲了判斷自己定義的過濾是否生效,直接注入StudentComponent對象,如果不存在,那麼啓動時將會報錯。
運行,發現控制檯果然報錯:
Field student in com.mytest.Application required a bean of type 'com.mytest.Component.StudentComponent' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.mytest.Component.StudentComponent' in your configuration.
這裏就證明配置生效了,在包掃描路徑下即使定義了相應的component,也沒有生效。通過此處的例子,可以更好的理解官方那兩個過濾器的功能。
TypeExcludeFilter功能:
//關鍵代碼如下:
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (this.beanFactory instanceof ListableBeanFactory
&& getClass() == TypeExcludeFilter.class) {
Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory) this.beanFactory)
.getBeansOfType(TypeExcludeFilter.class).values();
for (TypeExcludeFilter delegate : delegates) {
if (delegate.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
}
return false;
}
從上述代碼可以發現,該過濾器的功能是獲取beanFactory(此爲該類中的一個屬性)中所有的TypeExcludeFilter, 然後循環進行匹配,滿足的就過濾掉。
AutoConfigurationExcludeFilter功能:
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
}
該過濾器的功能就是過濾掉其他同時標註有@Configuration和@EnableAutoConfiguration的類。
上面在判斷是不是自動配置類時調用了isAutoConfiguration()方法,內部使用了:
this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(
EnableAutoConfiguration.class, this.beanClassLoader);
其實再仔細的查看源代碼會發現判斷是不是自動配置類是通過獲取類路徑META-INF/spring.factories中讀取的,而讀取的內容就是以key=
org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有屬性值,這些屬性值爲自動裝配類,內容如下:
spring boot中的複合註解
“複合註解”是指一個註解底層是由別的註解組成,比如我們比較熟悉的@RestController就是@Controller和@ResponseBody兩個註解組成的;還有的例如@SpringBootConfiguration其實內部就是@Configuration註解,但是前者是spring boot使用的註解,而後者是spring的註解,spring能夠識別spring boot的註解恰恰就是利用了這種派生性,再推廣開來說,@Configuration註解就是@Component的派生,所以此外包括@Service, @Controller, @Repository才能夠被@ComponentScan統一識別。另外,java註解不支持繼承的特性,所以才使用了派生。
@SpringBootApplication註解內部還使用了許多屬性別名,例如:
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
上面的註解的意思就是說@SpringBootApplication的excludeName和scanBasePackages屬性分別代表@EnableAutoConfiguration中的excludeName和@ComponentScan中的basePackages屬性,需要注意的是@AliasFor是spring 4.2纔開始支持的,而spring boot相應的在1.3開始使用註解屬性別名。
spring boot默認掃描的是@SpringBootApplication所在類的包及其子包下的bean,通過scanBasePackages屬性可以手動指定需要掃描的包,使用如下:
修改主配置類,如下所示,指定包掃描路徑爲com.mytest.controller,而當前類位於com.mytest.application包下,此處如果不指定包掃描路徑,則controller包下的所有bean都不會被掃描到的。
//package com.mytest.application;
@SpringBootApplication(scanBasePackages = "com.mytest.controller")
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
在com.mytest.controller下新建HelloController.java, 內容如下:
//package com.mytest.controller;
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello(){
return "hello world!";
}
}
啓動主配置類,發現控制檯正常打印處了映射路徑。
三,總結
本文主要簡單講述了SpringBoot項目的搭建,以及關於@SpringBootApplication註解相關的內容,該系列博文用於記錄自己學習spring boot的過程,部分內容來自《spring boot 編程思想》,如有問題,歡迎指出。