1. 程式人生 > 實用技巧 >Springboot核心註解

Springboot核心註解

執行文中的程式碼需要在專案構建中引入springboot 相關依賴.

① @configuration

configuration,用來將bean加入到ioc容器。代替傳統xml中的bean配置。程式碼示例:

定義一個普通類:

public class Person {


}  

定義一個配置類,用來將此類註冊到ioc容器中:

@Configuration
public class PersonConfig {


    @Bean
    public Person person(){
        return new Person();
    }

}

測試類:

public
class 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 去指定需要滿足某些條件,預設配置中的類才會被載入。