Springboot核心註解
執行文中的程式碼需要在專案構建中引入springboot 相關依賴.
① @configuration
configuration,用來將bean加入到ioc容器。代替傳統xml中的bean配置。程式碼示例:
定義一個普通類:
public class Person { }
定義一個配置類,用來將此類註冊到ioc容器中:
@Configuration public class PersonConfig { @Bean public Person person(){ return new Person(); } }
測試類:
publicclass App { public static void main(String[] args) { AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(PersonConfig.class); System.out.println(ac.getBean("person")); } }
執行結果:
②@ComponentScan
用來掃包,相當於xml配置的 :<context:component-scan base-package="" />
程式碼示例:
首先我們看一下專案的目錄結構:
目前所有的類都是在CompentScanTest下面
一個普通的service bean:
@Service public class UserService { }
一個配置類,這裡會使用 @ComponentScan 註解:
@ComponentScan public class ScanConfig { }
一個測試類:
public class CompentScanTest { public static void main(String[] args) { AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(ScanConfig.class); Stream.of(ac.getBeanDefinitionNames()).forEach(System.out::println); } }
執行結果:
本包下的加了註解的類都被掃描了,也就是說@ComponentScan 預設掃描本包下面加了對應註解的類。
可以新增一個熟悉 然後測試 :
@ComponentScan(basePackages = "com.llicat.stage3.springboot") public class ScanConfig { }
此時把configuration包下的類也會掃描出來:
還有其他的像排除之類的,這裡不在複述。
③@EnableAutoConfiguration
主要有兩個註解:
@import 的說明:
xml 中有如下配置:
<import resource="classpath:spring-dao.xml"/>引入其他配置檔案的配置。
程式碼結構:
一個簡單物件類:
public class SimpleBean { }
配置類,用來把物件加入ioc容器:
@Configuration public class SimpleBeanConfig { @Bean public SimpleBean simpleBean(){ return new SimpleBean(); } }
測試類:
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(SimpleBeanConfig.class); Stream.of(ac.getBeanDefinitionNames()).forEach(System.out::println); System.out.println(ac.getBean("simpleBean")); } }
執行結果:
掃描了當前包下面存在註解的類。
在other包下面存在一個bean 和對應的配置類,我們修改一下程式碼:
@Configuration @Import(OtherBeanConfig.class) public class SimpleBeanConfig { @Bean public SimpleBean simpleBean(){ return new SimpleBean(); } }
然後執行發現,其他類也被掃描了。區別與compentScan @import 引入動態的配置類,可以使得掃描更加靈活和動態化
注意到這一個配置:
AutoConfigurationImportSelector實現了ImportSelector,importSelector是spring提供的一個用來支援動態注入的配置。這裡可以模仿實現:
定義一個介面與兩個實現類:
public interface Logger { void doLogger(); } public class DevLogger implements Logger { @Override public void doLogger() { System.out.println("[DEBUG]..."); } } public class TestLogger implements Logger { @Override public void doLogger() { System.out.println("[INFO]"); } }
然後定義一個importSelector:
public class LoggerSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { Map<String, Object> include = importingClassMetadata.getAnnotationAttributes(EnableAutoSwitchLogger.class.getName()); Class [] clzs=null==include.get("include")?null:((Class[]) include.get("include")); ArrayList<String> classList=new ArrayList<>(); Stream.of(clzs).forEach(clz->{ if(clz.isInterface()){ return; } classList.add(clz.getName()); }); return classList.toArray(new String[classList.size()]); } }
這個類主要是為了獲取元資料資訊,然後返回需要被掃描注入到ioc的類。
定義@EnableAutoSwitchLogger 並且使用LoggerSelector:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Inherited 5 @Import(LoggerSelector.class) 6 public @interface EnableAutoSwitchLogger { 7 8 /** 9 * 這裡可以指定要返回的日誌實現類 10 * @return 11 */ 12 Class<?>[] include() default {}; 13 }
測試類:
1 @Configuration 2 @EnableAutoSwitchLogger(include ={TestLogger.class,DevLogger.class}) 3 public class App { 4 5 6 public static void main(String[] args) { 7 8 AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(App.class); 9 Stream.of(ac.getBeanDefinitionNames()).forEach(System.out::println); 10 } 11 12 }
測試結果:
這個實際上就是springboot實現動態注入的一個關鍵點。分析AutoConfigurationImportSelector 的核心實現:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry( autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
點進去loadMetadata,會載入配置檔案,其實就是條件註解,只有當前掃描的路徑下存在條件中配置的類 然後去載入spring.factories中對應的第三方類。為了保證效能,不能是所有依賴的第三方的jar的類都被
載入,而是你配置了,才會去載入。
protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties"; private AutoConfigurationMetadataLoader() { } public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); }
getAutoConfigurationEntry方法呼叫了getCandidateConfigurations 使用了 spring中的spi工具類SpringFactoriesLoader去載入配置檔案META-INF/spring.factories中key為
org.springframework.boot.autoconfigure.EnableAutoConfiguration(註解的全限定名),對應的value會作為字串被返回,然後作為需要被載入到ioc中的資訊。spi是用來spring用來把第三方的jar包裡面的類載入到
ioc容器的一種技術,需要滿足指定路徑下指定檔案,且key匹配。@configuration 掃包掃不到第三方jar包。
總結:
@SpringBootApplication=@ComponentScan+@Configuration+@EnableAutoConfiguration
ComponentScan用來指定當前包路徑,在這個路徑下的都會被掃描到,@Configuration 用來說明這是一個配置類,可以去載入bean到ioc中。@EnableAutoConfiguration ,核心依AutoConfigurationImportSelector的實現:首先在META-INF/spring.factories 中預先配置了許多預設需要被載入的bean,然後在spring-autoconfigure-metadata.properties配置了,這些預設bean被載入的條件,
並且通過條件可以過濾不需要被載入的bean,為了滿足更大的客戶化需求,在使用@SpringBootApplication 可以指定 exclude屬性,這些會作為元素資料,去移除掉不需要載入的類,同時也可以使用@ConditionalOnClass 去指定需要滿足某些條件,預設配置中的類才會被載入。