讀書筆記中涉及到的書可以從這個repo下載
1 背景
我們可以通過3種方式裝配bean,分別是:
- 在XML中進行顯式配置
- 在Java中進行顯示配置
- 隱式的bean發現機制和自動裝配
這篇博文講第3種方式
2 隱式的bean發現機制和自動裝配
Spring從兩個角度來實現自動化裝配
- 組件掃描(component scanning):Spring會自動發現應用上下文中所創建的bean。
- 自動裝配(autowiring): Spring自動滿足bean之間的依賴
我以一個項目爲例,來描述自動化裝配的過程。
項目的github地址如下:https://github.com/AChaoZJU/Vue-Spring-Boot-Get-Started
2.1 組件掃描
首先看項目的啓動文件BusinessApplication.java:
package com.wepay.business;
import com.wepay.business.resource.storage.StorageProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class BusinessApplication {
public static void main(String[] args) {
SpringApplication.run(BusinessApplication.class, args);
}
}
重點在於@SpringBootApplication 這個annotation
注意:這個annotation是Spring Boot的annnotation
在IDEA中點開
@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這個annotation
@ComponentScan能夠在Spring中啓動組件掃描(compoent sacn, 默認是不開啓的)。如果沒有其他配置的話,@ComponentScan默認會掃描與配置類相同的包(掃描這個包以及這個包下的所有子包,查找帶有@Component註解的類,並且自動爲其創建一個bean
@Component會發現FileSystemStorageService 這個類
package com.wepay.business.resource.storage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import java.util.stream.Stream;
@Service
public class FileSystemStorageService implements StorageService {
...
}
然後爲其創建一個bean。
等等!!這個類只有@Service註解,沒有@Component註解。這樣也可以被創建成一個bean。
答案是:可以。
因爲@Service註解被@Component註解修飾
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
}
OK, 我們已經FileSystemStorageService 這個類創建了一個bean
2.2 自動裝配
接下來: 我們看這個bean如何被自動裝配,代碼見GoodResource類
package com.wepay.business.resource;
import com.wepay.business.model.Good;
import com.wepay.business.repo.GoodRepository;
import com.wepay.business.resource.storage.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.util.List;
import java.util.Optional;
@CrossOrigin
@RestController
@RequestMapping("/api")
public class GoodResource {
@Autowired
private GoodRepository repository;
private final StorageService storageService;
@Autowired
public GoodResource(StorageService storageService) {
this.storageService = storageService;
}
...
}
- 自動裝配就是讓Spring自動滿足bean依賴的一種方法。上面GoodResource類依賴StorageService的bean。在我們的代碼中,只有FileSystemStorageService 這個類實現了StorageService 這個接口。所以你也可以認爲,我們依賴FileSystemStorageServicel 類的bean。
- 在滿足依賴的過程中,自動裝配會在Spring應用上下文尋找匹配某個bean(指GoodResource類的bean,因爲@RestController被@Component註解)需求的其他bean(指FileSystemStorageServicel 類的bean)
- 而@Autowired幫助我們實現了自動裝配。
這表明,當Spring創建GoodResource bean的時候,會通過這個構造器( public GoodResource(StorageService storageService) )實例化並且傳入一個可設置(is assignable)給StorageService的bean
在GoodResource構造函數中,我們沒有進行任何實例化操作。這就是bean的作用。
以上,我們就實現了組件掃描Component Scan(發現@Component註解的類併爲其創建一個bean)和自動裝配(尋找bean,並在滿足其他bean對這個bean的依賴需求)兩個過程。
2.3 更新
在class GoodResource中,構造器的註解@Autowired註解可以不加
因爲:
Starting with Spring 4.3, if a class, which is configured as a Spring bean, has only one constructor, the @Autowired annotation can be omitted and Spring will use that constructor and inject all necessary dependencies.
詳見:spring-injects-dependencies-in-constructor-without-autowired-annotation
package com.wepay.business.resource;
import com.wepay.business.model.Good;
import com.wepay.business.repo.GoodRepository;
import com.wepay.business.resource.storage.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.util.List;
import java.util.Optional;
@CrossOrigin
@RestController
@RequestMapping("/api")
public class GoodResource {
@Autowired
private GoodRepository repository;
private final StorageService storageService;
// @Autowired
public GoodResource(StorageService storageService) {
this.storageService = storageService;
}
...
}