1. 程式人生 > >Spring 幾個配置註解的含義

Spring 幾個配置註解的含義

說說以下幾個註解的含義

[email protected]此註解的用義是讓一個類成為一個配置類,它與 @Bean 註解一起使用(一般用在類中的方法上面),可以用於生成一系列的 Bean .它們是用來簡單基於 xml 配置 Bean 的註解。可以這樣說,一個類上面 @Configuration 就相當於一個 定義 很多Bean 的 xml 檔案。

[email protected]此註解的用義是讓一個類成為一個注入了 propeties 變數的配置類,一般與 @Component 和 @PropertySource 一起使用,例如:

@Component
@PropertySource("classpath:application.properties")
@ConfigurationProperties(prefix="hrService")
public class HRConfig
意思是,HRConfig 是一個裝配了 propeties 的配置類(@ConfigurationProperties),裝配變數的檔案是 classpath:application.properties ,(@PropertySource(...)),讀取檔案中變數的規則是,所有以  hrService 為字首的變數, 可以對外提供讀取配置變數的服務(@Component)。

上面的配置其實可以在 Spingboot 中簡化為

@Component@ConfigurationProperties
(prefix="hrService")
因為 Springboot 中 classpath:application.properties 是預設載入的配置檔案。

@ConfigurationProperties 此註釋從字面上來理解,可以設定配置的屬性,使被裝配類有不同的行為.例如,可以傳入 prefix,ignoreUnknowFields 等等,以達到裝配行為多樣化。其實它並不真正具體一個對外提供配置的能力,@ConfigurationProperties 只是規定了一個類如何成為一個配置服務類,但並不使它真正成為一個配置服務類。使它具備這種能力也即使它成為一個 Bean,@Conponent 就能做到,@EnableConfigurationProperties 也能做取。

[email protected] 允許任意一個類具有對外提供配置的能力。它與 @Bean 的方式不同,@Bean 是配置在類本身上,一勞永逸,一旦配置,任何地方都可以注入。而 @EnableConfigurationProperties 是配置在希望使用此配置服務的那個類上面。這樣就可以實現可拔插。下面拷貝一份程式碼以說明:

@ConfigurationProperties(
        prefix = "fps",
        ignoreUnknownFields = true
)
@Data
public class FpsProperties {

    private String host="localhost";
    private int port=7008;
    private String username;
    private String password;
    private String orgid="99996";
    private int connectTimeout=10000;
    private int connectRequestTimeout=10000;
    private int socketTimeout=120000;//相當於讀超時

    private int retryCount=3;//重試次數
    private boolean fpsEnabled=true;

}

//-------------------------------------------------------------------------------
@Configuration
@EnableConfigurationProperties({FpsProperties.class})
@ConditionalOnClass({FpsClient.class})
@ConditionalOnProperty(
        prefix = "fps",
        name = {"enabled"},
        matchIfMissing = true
)
@Slf4j
public class FpsAutoConfiguration {
    @Autowired
    private FpsProperties fpsProperties;


    @Bean
    @ConditionalOnMissingBean
    @ConfigurationProperties(
            prefix = "fps"
    )
    public FpsService fpsService() {
        FpsServiceImpl fpsService = new FpsServiceImpl(fpsProperties.getUsername(), fpsProperties.getPassword(), fpsProperties.getHost(), fpsProperties.getPort(), fpsProperties.getOrgid(), fpsProperties.getConnectRequestTimeout(), fpsProperties.getConnectTimeout(), fpsProperties.getSocketTimeout());
        return fpsService;
    }

}

首先,@configuration 表示 FpsAutoConfiguration 類是一個可生成多個 Bean 的配置器。@EnableConfigurationProperties({FpsProperties.class}) 表示使 FpsProperties 這個類擁有對“我”提供配置的能力。類裡面的 @Autowired 就把這種配置能力注入進來了。@ConditionalOnClass({FpsClient.class}) 表示當整個 jvm 中存在 FpsClient 這個類時,本 @Configuration 才生效,否則不生效。當時看這個配置非常疑惑,如果 FpsClient 這個類不存在,此行程式碼應該會立即報錯,何談載入到 jvm 中去執行呢?解釋一下,@conditionalOnClass 一般是在庫中使用的,假設庫工程是 A,有上面的程式碼。另有一個關聯工程 B,A 中沒有 FpsClient 類,而 B 中有,讓 A 工程依賴於 B,將 A 打包,A.jar 作為一個庫對外發布。此時,A.jar 是沒有 FpsClient 類的定義的。此時,有一個客戶工程,付費了,想使用 A.jar ,而 C 沒有定義 FpsClient 類,所以 A.jar 中的上述配置就不會生效,因此,C 在執行時,就不會承擔 A 裡面產生的 Bean 的負擔。就這是熱拔插的意思。所以 @ConditionalOnClass 是為了完成這件事情的。

@ConditionalOnProperty 與 @ConditionalOnClass 目的一致,邏輯上是且的關係,只要 @ConditionalOnProperty 中規定的條件沒有滿足,整個 @Configuration 就不生效。上面的配置中,規定了配置變數 fps.enabled=true 時,@Configuration 才生效。

一般情況下,若變數是 fps.x.y.z.enabled,則上面要寫為:prefix="fps.x.y.z", name={"enabled"}。這是因為 name 一定是最終的名字,而不含有任何字首。

後面類裡面的,@Bean 就會很熟悉了,表示定義一個 Bean 生成的條件,依賴的變數和生成的步驟。@ConditionalOnMissingBean 表示此 Bean 生成的規則:只有當這個類的  Bean 沒有生成時,才會去生成 Bean. @ConfigurationProperties 其實是多餘的,因為後面的程式碼直接使用了 FpsProperties 去取值.

上面生成  FpsServiceImpl 的 Bean 其實可以更簡單:

FpsServiceImpl fpsService = new FpsServiceImpl();//前提是有無參構造方法

此時 @ConfigurationProperties 表示去FpsProperties 中找變數的值,還是預設配置檔案  application.properties 中按 prefix = fps 去讀呢。這個問題可能沒有意義,首先要明確一點,所有配置類的配置載入器是同一個,不可能存在不同的值,至於存在同名的多個配置檔案,載入的是哪一個就要具體看了,一般是後面載入的會替換前面載入的。因此,上面的問題就不用回答的。

另外,使用了此註解的類所在的包及子包下面的類也預設使用了此註解。若將此註解應用到根目錄包下面的類,則所有類也都應用了此註解。所以此註解的使用一般要放在最高階的根目錄類上面。

關於這個註解,最後再舉一個例:

@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class MyBatisConfig {


//    @Value("${spring.datasource.encode-password}")
//    private String encodePassword;
    @Autowired
    private DataSourceProperties properties;

    //這個配置會載入所有spring.datasource配置注入DataSource物件,未載入會有問題。
    @Bean
    @ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
    public DataSource dataSource() {
      //  Assert.isNull(this.properties.getPassword(), "必須為空否則,加密密碼會被覆蓋");

        DataSourceBuilder factory = DataSourceBuilder
                .create(this.properties.getClassLoader())
                .driverClassName(this.properties.getDriverClassName())
                .url(this.properties.getUrl()).username(this.properties.getUsername())
                .password(properties.getPassword());

        if (this.properties.getType() != null) {
            factory.type(this.properties.getType());
        }
        BasicDataSource basicDataSource= (BasicDataSource)factory.build();
        basicDataSource.setMaxTotal(150);
        basicDataSource.setMaxWaitMillis(5000);

        return basicDataSource;
    }


    @Bean(name = "sqlSessionFactory")
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage("com.******.entity");

        //分頁外掛
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("reasonable", "true");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("returnPageInfo", "check");
        properties.setProperty("params", "count=countSql");
        pageHelper.setProperties(properties);
        //新增外掛
        bean.setPlugins(new Interceptor[]{pageHelper});

        //新增XML目錄
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

4.@EnableAutoconfiguration 這個註解作用在於讓 Spring Boot 根據應用所宣告的依賴來對 Spring 框架進行自動配置,比較智慧的是,它還根據 classpath 中的 jar 包推斷需要引入哪些註解/ Bean ,另外,使用了此註解的類所在的包及子包下面的類也預設使用了此註解。若將此註解應用到根目錄包下面的類,則所有類也都應用了此註解,這也是最通常的做法,實際上, @SpingBootApplication 註解就使用了這個註解。詳細的說明可以參看 EnableAutoconfiguration  註解實現檔案的說明。

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}