写点什么

Spring Boot「05」Annotations 02

作者:Samson
  • 2022-10-13
    上海
  • 本文字数:2597 字

    阅读完需:约 9 分钟

Spring Boot「05」Annotations 02

昨天我们学习了 Spring Boot 中的条件化配置注解,今天我们来学习如何自定义一个自动化配置。

01-基本步骤

基本步骤如下:

  1. 定义一个配置类,例如我们可以定义一个 MySQL 数据源的自动化配置:


@Configuration@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)public class MysqlAutoConfiguration {    // ...}
复制代码


  1. META-INF/spring.factories中增加如下配置,以便 Spring Boot 的自动化配置特性可以识别到我们自定义的配置


org.springframework.boot.autoconfigure.EnableAutoConfiguration=\self.samson.example.autoconfigure.MysqlAutoConfiguration
复制代码


  1. 通过@AutoConfigureOrder注解,我们可以控制自定义的自动化配置的优先级,最低(Ordered.LOWEST_PRECEDENCE)到最高(Ordered.HIGHEST_PRECEDENCE)之间的一个值。

  2. 通过@Conditional定义条件化的配置。注:自动化配置中定义的 Bean 只有在我们没有定义类似的 Bean 时生效,如果我们定义了 Bean,则会覆盖自动化配置中的定义。

02-自定义自动化配置(MySQL 数据源)

我们要定义的 MySQL 数据源配置,只有在我们的应用中有数据库相关的依赖时,才需要去配置。所以我们的自定义配置类可以写成:


@Configuration@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@ConditionalOnClass(DataSource.class)public class MysqlAutoConfiguration {    // ...}
复制代码


@ConditionalOnClass : @Conditional that only matches when the specified classes are on the classpath.


这意味着,只有应用的 classpath 下包含DataSource.class时,该配置类定义的 Bean 才生效。


在配置中定义 Bean 时,Spring Boot @Conditional 特性同样提供了几种方式来配置 Bean 尽在某些条件下才能生效:


  1. 根据某个 property 的值来控制条件化 Bean 生效。例如,我们想通过 useMysql=local 表示使用本地的 MySQL 数据库,而使用 useMysql=custom 表示用户自定义的数据库,数据库配置信息仍然通过属性给出(@PropertySource("classpath:mysql.properties"))。那么我们就可以把我们的配置文件写作:


@Bean(name = "dataSource")@ConditionalOnProperty(        name = "useMysql",        havingValue = "local")@ConditionalOnMissingBean(DataSource.class)public DataSource localMysqlDataSource() {    // use local mysql}
@Bean(name = "dataSource")@ConditionalOnProperty( name = "useMysql", havingValue = "custom")@ConditionalOnMissingBean(DataSource.class)public DataSource customMysqlDataSource() { // use custom mysql}
复制代码


  1. 根据某个类型或某个名称的 Bean 来控制条件化 Bean 生效。例如,我们创建 DataSource 的前提就是,当前容器中还未包含任何 DataSource 实例。我们就可以通过@ConditionalOnMissingBean(DataSource.class)来控制生效条件。

  2. 根据某个资源来控制条件化 Bean 生效。


@ConditionalOnResource(        resources = "classpath:mysql.properties")Properties additionalProperties() {    // additional properties}
复制代码


  1. 定义复杂的条件逻辑,通过继承 SpringBootCondition.class 并实现 getMatchOutcome 方法,并搭配 @Conditional 使用。


@Conditional(HibernateCondition.class)Properties additionalProperties() {    // additional properties}
public class HibernateCondition extends SpringBootCondition { private static String[] CLASS_NAMES = { "org.hibernate.ejb.HibernateEntityManager", "org.hibernate.jpa.HibernateEntityManager" }; @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage.forCondition("Hibernate");
return Arrays.stream(CLASS_NAMES) .filter(className -> ClassUtils.isPresent(className, context.getClassLoader())) .map(className -> ConditionOutcome.match(message.found("class").items(ConditionMessage.Style.NORMAL, className))) .findAny() .orElseGet(() -> ConditionOutcome.noMatch(message.didNotFind("class", "classes").items(ConditionMessage.Style.NORMAL, Arrays.asList(CLASS_NAMES)))); }}
复制代码


需要注意的是:条件化配置中定义的条件,测试条件是否匹配时,根据的仅仅是测试条件这一时刻应用上下文中的内容。例如,考虑如下代码:


@Bean(name = "dataSource")@ConditionalOnProperty(        name = "useMysql",        havingValue = "custom")@ConditionalOnMissingBeanpublic DataSource customMysqlDataSource() {    DriverManagerDataSource dataSource = new DriverManagerDataSource();    return dataSource;}
@Bean@ConditionalOnBean(name = "dataSource")@ConditionalOnMissingBeanpublic LocalContainerEntityManagerFactoryBean entityManagerFactory(@Autowired @Qualifier(value = "dataSource") DataSource dataSource) { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); return em; }
复制代码


当测试entityManagerFactory是否生效时,需要依赖名称为dataSource的 Bean。如果将 customMysqlDataSource() 方法放置在 entityManagerFactory() 方法之后,则entityManagerFactory的定义不生效。


完整的程序代码以及测试用力可以到 gitee 下载。


有没有什么方法可以关闭某个自动化配置呢?使用@EnableAutoConfiguration中的 exclude 属性,如下:


@EnableAutoConfiguration(  exclude={MySQLAutoconfiguration.class})public class AutoConfigureApplication {     }
复制代码


或者通过设置 property 来实现:spring.autoconfigure.exclude=self.samson.example.autoconfigure.MysqlAutoConfiguration

03-在 docker 中安装 mysql

docker search mysql     docker pull mysql 
docker run -d -p 3306:3306 --privileged=true -v "D:\docker\mysql\log":"/var/log/mysql" -v "D:\docker\mysql\data":"/var/lib/mysql" -v "D:\docker\mysql\conf":"/etc/mysql/conf.d" -e MYSQL_ROOT_PASSWORD="123456" mysql:latest
复制代码


发布于: 12 小时前阅读数: 4
用户头像

Samson

关注

还未添加个人签名 2019-07-22 加入

InfoQ签约作者 | 阿里云社区签约作者

评论

发布
暂无评论
Spring Boot「05」Annotations 02_Java_Samson_InfoQ写作社区